001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.core.plugins; 021 022 import org.apache.commons.io.FileUtils; 023 import org.sonar.api.Plugin; 024 import org.sonar.api.utils.SonarException; 025 import org.sonar.api.utils.ZipUtils; 026 import org.sonar.updatecenter.common.PluginKeyUtils; 027 import org.sonar.updatecenter.common.PluginManifest; 028 029 import java.io.File; 030 import java.io.IOException; 031 import java.net.URL; 032 import java.net.URLClassLoader; 033 import java.util.List; 034 import java.util.zip.ZipEntry; 035 036 public class PluginInstaller { 037 038 public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore, List<File> deprecatedExtensions) { 039 return install(pluginFile, isCore, deprecatedExtensions, null); 040 } 041 042 public DefaultPluginMetadata install(File pluginFile, boolean isCore, List<File> deprecatedExtensions, File toDir) { 043 DefaultPluginMetadata metadata = extractMetadata(pluginFile, isCore); 044 metadata.setDeprecatedExtensions(deprecatedExtensions); 045 return install(metadata, toDir); 046 } 047 048 public DefaultPluginMetadata install(DefaultPluginMetadata metadata, File toDir) { 049 try { 050 File pluginFile = metadata.getFile(); 051 File pluginBasedir = copyPlugin(metadata, toDir, pluginFile); 052 copyDependencies(metadata, pluginFile, pluginBasedir); 053 copyDeprecatedExtensions(metadata, pluginBasedir); 054 055 return metadata; 056 057 } catch (IOException e) { 058 throw new SonarException("Fail to install plugin: " + metadata, e); 059 } 060 } 061 062 private File copyPlugin(DefaultPluginMetadata metadata, File toDir, File pluginFile) throws IOException { 063 File pluginBasedir; 064 if (toDir != null) { 065 pluginBasedir = toDir; 066 FileUtils.forceMkdir(pluginBasedir); 067 File targetFile = new File(pluginBasedir, pluginFile.getName()); 068 FileUtils.copyFile(pluginFile, targetFile); 069 metadata.addDeployedFile(targetFile); 070 } else { 071 pluginBasedir = pluginFile.getParentFile(); 072 metadata.addDeployedFile(pluginFile); 073 } 074 return pluginBasedir; 075 } 076 077 private void copyDependencies(DefaultPluginMetadata metadata, File pluginFile, File pluginBasedir) throws IOException { 078 if (metadata.getPathsToInternalDeps().length > 0) { 079 // needs to unzip the jar 080 ZipUtils.unzip(pluginFile, pluginBasedir, new LibFilter()); 081 for (String depPath : metadata.getPathsToInternalDeps()) { 082 File dependency = new File(pluginBasedir, depPath); 083 if (!dependency.isFile() || !dependency.exists()) { 084 throw new IllegalArgumentException("Dependency " + depPath + " can not be found in " + pluginFile.getName()); 085 } 086 metadata.addDeployedFile(dependency); 087 } 088 } 089 } 090 091 private void copyDeprecatedExtensions(DefaultPluginMetadata metadata, File pluginBasedir) throws IOException { 092 for (File extension : metadata.getDeprecatedExtensions()) { 093 File toFile = new File(pluginBasedir, extension.getName()); 094 if (!toFile.equals(extension)) { 095 FileUtils.copyFile(extension, toFile); 096 } 097 metadata.addDeployedFile(toFile); 098 } 099 } 100 101 private static final class LibFilter implements ZipUtils.ZipEntryFilter { 102 public boolean accept(ZipEntry entry) { 103 return entry.getName().startsWith("META-INF/lib"); 104 } 105 } 106 107 public DefaultPluginMetadata extractMetadata(File file, boolean isCore) { 108 try { 109 PluginManifest manifest = new PluginManifest(file); 110 DefaultPluginMetadata metadata = DefaultPluginMetadata.create(file); 111 metadata.setKey(manifest.getKey()); 112 metadata.setName(manifest.getName()); 113 metadata.setDescription(manifest.getDescription()); 114 metadata.setLicense(manifest.getLicense()); 115 metadata.setOrganization(manifest.getOrganization()); 116 metadata.setOrganizationUrl(manifest.getOrganizationUrl()); 117 metadata.setMainClass(manifest.getMainClass()); 118 metadata.setVersion(manifest.getVersion()); 119 metadata.setHomepage(manifest.getHomepage()); 120 metadata.setPathsToInternalDeps(manifest.getDependencies()); 121 metadata.setUseChildFirstClassLoader(manifest.isUseChildFirstClassLoader()); 122 metadata.setBasePlugin(manifest.getBasePlugin()); 123 metadata.setCore(isCore); 124 if (metadata.isOldManifest()) { 125 completeDeprecatedMetadata(metadata); 126 } 127 return metadata; 128 129 } catch (IOException e) { 130 throw new IllegalStateException("Fail to extract plugin metadata from file: " + file, e); 131 } 132 } 133 134 private void completeDeprecatedMetadata(DefaultPluginMetadata metadata) { 135 String mainClass = metadata.getMainClass(); 136 File pluginFile = metadata.getFile(); 137 try { 138 // copy file in a temp directory because Windows+Oracle JVM Classloader lock the JAR file 139 File tempFile = File.createTempFile(pluginFile.getName(), null); 140 FileUtils.copyFile(pluginFile, tempFile); 141 142 URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[]{tempFile.toURI().toURL()}, getClass().getClassLoader()); 143 Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance(); 144 metadata.setKey(PluginKeyUtils.sanitize(pluginInstance.getKey())); 145 metadata.setDescription(pluginInstance.getDescription()); 146 metadata.setName(pluginInstance.getName()); 147 148 } catch (Exception e) { 149 throw new IllegalStateException("The metadata main class can not be created. Plugin file=" + pluginFile.getName() + ", class=" + mainClass, e); 150 } 151 } 152 }