You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PluginDeployer.java 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Sonar, open source software quality management tool.
  3. * Copyright (C) 2009 SonarSource SA
  4. * mailto:contact AT sonarsource DOT com
  5. *
  6. * Sonar is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * Sonar is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with Sonar; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
  19. */
  20. package org.sonar.server.plugins;
  21. import org.apache.commons.io.FileUtils;
  22. import org.apache.commons.lang.StringUtils;
  23. import org.slf4j.Logger;
  24. import org.slf4j.LoggerFactory;
  25. import org.sonar.api.Plugin;
  26. import org.sonar.api.ServerComponent;
  27. import org.sonar.api.platform.Server;
  28. import org.sonar.api.utils.Logs;
  29. import org.sonar.api.utils.TimeProfiler;
  30. import org.sonar.api.utils.ZipUtils;
  31. import org.sonar.core.plugin.JpaPlugin;
  32. import org.sonar.core.plugin.JpaPluginDao;
  33. import org.sonar.server.platform.DefaultServerFileSystem;
  34. import org.sonar.server.platform.ServerStartException;
  35. import com.google.common.collect.Maps;
  36. import java.io.File;
  37. import java.io.IOException;
  38. import java.net.URL;
  39. import java.net.URLClassLoader;
  40. import java.util.ArrayList;
  41. import java.util.Collection;
  42. import java.util.List;
  43. import java.util.Map;
  44. public final class PluginDeployer implements ServerComponent {
  45. private static final Logger LOG = LoggerFactory.getLogger(PluginDeployer.class);
  46. private Server server;
  47. private DefaultServerFileSystem fileSystem;
  48. private JpaPluginDao dao;
  49. private PluginClassLoaders classloaders;
  50. private Map<String, PluginMetadata> pluginByKeys = Maps.newHashMap();
  51. private Map<String, PluginMetadata> deprecatedPlugins = Maps.newHashMap();
  52. public PluginDeployer(Server server, DefaultServerFileSystem fileSystem, JpaPluginDao dao, PluginClassLoaders classloaders) {
  53. this.server = server;
  54. this.fileSystem = fileSystem;
  55. this.dao = dao;
  56. this.classloaders = classloaders;
  57. }
  58. public void start() throws IOException {
  59. TimeProfiler profiler = new TimeProfiler().start("Install plugins");
  60. loadUserPlugins();
  61. moveAndLoadDownloadedPlugins();
  62. loadCorePlugins();
  63. deployPlugins();
  64. deployDeprecatedPlugins();
  65. persistPlugins();
  66. profiler.stop();
  67. }
  68. private void persistPlugins() {
  69. List<JpaPlugin> previousPlugins = dao.getPlugins();
  70. List<JpaPlugin> installedPlugins = new ArrayList<JpaPlugin>();
  71. for (PluginMetadata plugin : pluginByKeys.values()) {
  72. JpaPlugin installed = searchPlugin(plugin, previousPlugins);
  73. if (installed == null) {
  74. installed = JpaPlugin.create(plugin.getKey());
  75. installed.setInstallationDate(server.getStartedAt());
  76. }
  77. plugin.copyTo(installed);
  78. installedPlugins.add(installed);
  79. Logs.INFO.info("Plugin: " + plugin.getName() + " " + StringUtils.defaultString(plugin.getVersion(), "-"));
  80. }
  81. dao.register(installedPlugins);
  82. }
  83. private JpaPlugin searchPlugin(PluginMetadata plugin, List<JpaPlugin> preinstalledList) {
  84. if (preinstalledList != null) {
  85. for (JpaPlugin p : preinstalledList) {
  86. if (StringUtils.equals(p.getKey(), plugin.getKey())) {
  87. return p;
  88. }
  89. }
  90. }
  91. return null;
  92. }
  93. private void deployPlugins() {
  94. for (PluginMetadata plugin : pluginByKeys.values()) {
  95. deploy(plugin);
  96. }
  97. }
  98. private void deployDeprecatedPlugins() throws IOException {
  99. for (PluginMetadata deprecatedPlugin : deprecatedPlugins.values()) {
  100. PluginMetadata metadata = pluginByKeys.get(deprecatedPlugin.getKey());
  101. if (metadata != null) {
  102. FileUtils.deleteQuietly(deprecatedPlugin.getSourceFile());
  103. Logs.INFO.info("Old plugin " + deprecatedPlugin.getFilename() + " replaced by new " + metadata.getFilename());
  104. } else {
  105. pluginByKeys.put(deprecatedPlugin.getKey(), deprecatedPlugin);
  106. deploy(deprecatedPlugin);
  107. }
  108. }
  109. }
  110. private void deploy(PluginMetadata plugin) {
  111. try {
  112. LOG.debug("Deploy plugin " + plugin);
  113. File deployDir = new File(fileSystem.getDeployedPluginsDir(), plugin.getKey());
  114. FileUtils.forceMkdir(deployDir);
  115. FileUtils.cleanDirectory(deployDir);
  116. File target = new File(deployDir, plugin.getFilename());
  117. FileUtils.copyFile(plugin.getSourceFile(), target);
  118. plugin.addDeployedFile(target);
  119. for (File extension : fileSystem.getExtensions(plugin.getKey())) {
  120. target = new File(deployDir, extension.getName());
  121. FileUtils.copyFile(extension, target);
  122. plugin.addDeployedFile(target);
  123. }
  124. if (plugin.getDependencyPaths().length > 0) {
  125. // needs to unzip the jar
  126. File tempDir = ZipUtils.unzipToTempDir(plugin.getSourceFile());
  127. for (String depPath : plugin.getDependencyPaths()) {
  128. File file = new File(tempDir, depPath);
  129. target = new File(deployDir, file.getName());
  130. FileUtils.copyFile(file, target);
  131. plugin.addDeployedFile(target);
  132. }
  133. }
  134. classloaders.create(plugin);
  135. } catch (IOException e) {
  136. throw new RuntimeException("Fail to deploy the plugin " + plugin, e);
  137. }
  138. }
  139. private void loadCorePlugins() throws IOException {
  140. for (File file : fileSystem.getCorePlugins()) {
  141. registerPluginMetadata(file, true, false);
  142. }
  143. }
  144. private void loadUserPlugins() throws IOException {
  145. for (File file : fileSystem.getUserPlugins()) {
  146. registerPluginMetadata(file, false, false);
  147. }
  148. }
  149. private void moveAndLoadDownloadedPlugins() throws IOException {
  150. if (fileSystem.getDownloadedPluginsDir().exists()) {
  151. Collection<File> jars = FileUtils.listFiles(fileSystem.getDownloadedPluginsDir(), new String[] { "jar" }, false);
  152. for (File jar : jars) {
  153. File movedJar = moveDownloadedFile(jar);
  154. registerPluginMetadata(movedJar, false, true);
  155. }
  156. }
  157. }
  158. private File moveDownloadedFile(File jar) {
  159. try {
  160. FileUtils.moveFileToDirectory(jar, fileSystem.getUserPluginsDir(), true);
  161. return new File(fileSystem.getUserPluginsDir(), jar.getName());
  162. } catch (IOException e) {
  163. LOG.error("Fail to move the downloaded file: " + jar.getAbsolutePath(), e);
  164. return null;
  165. }
  166. }
  167. private void registerPluginMetadata(File file, boolean corePlugin, boolean canDeleteOld) throws IOException {
  168. PluginMetadata metadata = PluginMetadata.createFromJar(file, corePlugin);
  169. String pluginKey = metadata.getKey();
  170. if (pluginKey != null) {
  171. registerPluginMetadata(pluginByKeys, file, metadata, canDeleteOld);
  172. } else if (metadata.isOldManifest()) {
  173. loadDeprecatedPlugin(metadata);
  174. registerPluginMetadata(deprecatedPlugins, file, metadata, canDeleteOld);
  175. }
  176. }
  177. private void registerPluginMetadata(Map<String, PluginMetadata> map, File file, PluginMetadata metadata, boolean canDeleteOld) {
  178. String pluginKey = metadata.getKey();
  179. PluginMetadata existing = map.get(pluginKey);
  180. if (existing != null) {
  181. if (canDeleteOld) {
  182. FileUtils.deleteQuietly(existing.getSourceFile());
  183. map.remove(pluginKey);
  184. Logs.INFO.info("Old plugin " + existing.getFilename() + " replaced by new " + metadata.getFilename());
  185. } else {
  186. throw new ServerStartException("Found two plugins with the same key '" + pluginKey + "': "
  187. + metadata.getFilename() + " and "
  188. + existing.getFilename());
  189. }
  190. }
  191. map.put(metadata.getKey(), metadata);
  192. }
  193. private void loadDeprecatedPlugin(PluginMetadata plugin) throws IOException {
  194. // URLClassLoader locks files on Windows
  195. // => copy the file before in a temp directory
  196. File tempFile = new File(fileSystem.getDeprecatedPluginsDir(), plugin.getFilename());
  197. FileUtils.copyFile(plugin.getSourceFile(), tempFile);
  198. String mainClass = plugin.getMainClass();
  199. try {
  200. URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[] { tempFile.toURI().toURL() }, getClass().getClassLoader());
  201. Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance();
  202. plugin.setKey(pluginInstance.getKey());
  203. plugin.setDescription(pluginInstance.getDescription());
  204. plugin.setName(pluginInstance.getName());
  205. } catch (Exception e) {
  206. throw new RuntimeException("The plugin main class can not be created: plugin=" + plugin.getFilename() + ", class=" + mainClass, e);
  207. }
  208. if (StringUtils.isBlank(plugin.getKey())) {
  209. throw new ServerStartException("Found plugin with empty key: " + plugin.getFilename());
  210. }
  211. }
  212. }