]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2075: Add the ability for a plugin to extend an other plugin
authorEvgeny Mandrikov <mandrikov@gmail.com>
Mon, 7 Feb 2011 21:51:56 +0000 (00:51 +0300)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Tue, 8 Feb 2011 09:05:17 +0000 (12:05 +0300)
16 files changed:
pom.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java
sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java
sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java
sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java
sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
sonar-server/src/main/webapp/WEB-INF/db/migrate/181_add_plugin_base.rb [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java
sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java

diff --git a/pom.xml b/pom.xml
index 2d7283c23df7b60266da21763811162c263c6f02..07d4f902951e86c20ae77d2088c7fef880e35971 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       <dependency>
         <groupId>org.codehaus.sonar</groupId>
         <artifactId>sonar-update-center-common</artifactId>
-        <version>1.0</version>
+        <version>1.1-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>org.codehaus.sonar</groupId>
index 5ec8ac920aef226da61174fc9809557f8a430eb3..c883998d9275d5b902fed921c59a1c139d606b76 100644 (file)
@@ -57,6 +57,7 @@ public class BatchPluginRepository extends AbstractPluginRepository {
   private ClassLoadersCollection classLoaders;
   private ExtensionDownloader extensionDownloader;
   private EnvironmentInformation environment;
+  private List<JpaPlugin> register;
 
   public BatchPluginRepository(JpaPluginDao dao, ExtensionDownloader extensionDownloader, EnvironmentInformation environment) {
     this.dao = dao;
@@ -69,36 +70,59 @@ public class BatchPluginRepository extends AbstractPluginRepository {
    * for unit tests only
    */
   BatchPluginRepository() {
+  }
+
+  private List<URL> download(JpaPlugin pluginMetadata) {
+    List<URL> urls = Lists.newArrayList();
+    for (JpaPluginFile pluginFile : pluginMetadata.getFiles()) {
+      File file = extensionDownloader.downloadExtension(pluginFile);
+      try {
+        urls.add(file.toURI().toURL());
 
+      } catch (MalformedURLException e) {
+        throw new SonarException("Can not get the URL of: " + file, e);
+      }
+    }
+    return urls;
   }
 
   public void start() {
+    register = Lists.newArrayList();
     classLoaders = new ClassLoadersCollection(Thread.currentThread().getContextClassLoader());
-    for (JpaPlugin pluginMetadata : dao.getPlugins()) {
-      String key = pluginMetadata.getKey();
-      List<URL> urls = Lists.newArrayList();
-      for (JpaPluginFile pluginFile : pluginMetadata.getFiles()) {
-        File file = extensionDownloader.downloadExtension(pluginFile);
-        try {
-          urls.add(file.toURI().toURL());
-
-        } catch (MalformedURLException e) {
-          throw new SonarException("Can not get the URL of: " + file, e);
-        }
+
+    List<JpaPlugin> jpaPlugins = dao.getPlugins();
+
+    for (JpaPlugin pluginMetadata : jpaPlugins) {
+      if (StringUtils.isEmpty(pluginMetadata.getBasePlugin())) {
+        String key = pluginMetadata.getKey();
+        List<URL> urls = download(pluginMetadata);
+        classLoaders.createClassLoader(key, urls, pluginMetadata.isUseChildFirstClassLoader() == Boolean.TRUE);
+        register.add(pluginMetadata);
       }
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("Classloader of plugin " + key + ":");
-        for (URL url : urls) {
-          LOG.debug("   -> " + url);
+    }
+
+    // Extend plugins by other plugins
+    for (JpaPlugin pluginMetadata : jpaPlugins) {
+      String pluginKey = pluginMetadata.getKey();
+      String basePluginKey = pluginMetadata.getBasePlugin();
+      if (StringUtils.isNotEmpty(basePluginKey)) {
+        if (classLoaders.get(basePluginKey) != null) {
+          LOG.debug("Plugin {} extends {}", pluginKey, basePluginKey);
+          List<URL> urls = download(pluginMetadata);
+          classLoaders.extend(basePluginKey, pluginKey, urls);
+          register.add(pluginMetadata);
+        } else {
+          // Ignored, because base plugin doesn't exists
+          LOG.warn("Plugin {} extends nonexistent plugin {}", pluginKey, basePluginKey);
         }
       }
-      classLoaders.createClassLoader(key, urls, pluginMetadata.isUseChildFirstClassLoader() == Boolean.TRUE);
     }
+
     classLoaders.done();
   }
 
   public void registerPlugins(MutablePicoContainer pico) {
-    for (JpaPlugin pluginMetadata : dao.getPlugins()) {
+    for (JpaPlugin pluginMetadata : register) {
       try {
         Class claz = classLoaders.get(pluginMetadata.getKey()).loadClass(pluginMetadata.getPluginClass());
         Plugin plugin = (Plugin) claz.newInstance();
index 9df43699ec8c2fbbbf217f351bd10d73f73c858b..0914ecc37e01566972d662d345cd9c3d4f15a53e 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
+import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.codehaus.plexus.classworlds.ClassWorld;
 import org.codehaus.plexus.classworlds.realm.ClassRealm;
@@ -33,8 +34,6 @@ import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
 import org.sonar.api.utils.Logs;
 import org.sonar.api.utils.SonarException;
 
-import com.google.common.collect.Lists;
-
 /**
  * Encapsulates manipulations with ClassLoaders, such as creation and establishing dependencies. Current implementation based on
  * {@link ClassWorld}.
@@ -108,13 +107,27 @@ public class ClassLoadersCollection {
     }
   }
 
+  public void extend(String baseKey, String key, Collection<URL> urls) {
+    try {
+      ClassRealm base = world.getRealm(baseKey);
+      base.createChildRealm(key); // we create new realm to be able to return it by key without conversion to baseKey
+      for (URL url : urls) {
+        base.addURL(url);
+      }
+    } catch (NoSuchRealmException e) {
+      throw new SonarException(e);
+    } catch (DuplicateRealmException e) {
+      throw new SonarException(e);
+    }
+  }
+
   /**
    * Establishes dependencies among ClassLoaders.
    */
   public void done() {
     for (Object o : world.getRealms()) {
       ClassRealm realm = (ClassRealm) o;
-      if ( !StringUtils.endsWith(realm.getId(), "-parent")) {
+      if (!StringUtils.endsWith(realm.getId(), "-parent")) {
         String[] packagesToExport = new String[PREFIXES_TO_EXPORT.length];
         for (int i = 0; i < PREFIXES_TO_EXPORT.length; i++) {
           // important to have dot at the end of package name only for classworlds 1.1
@@ -132,7 +145,7 @@ public class ClassLoadersCollection {
     Logs.INFO.debug("Exporting " + Arrays.toString(packages) + " from " + realm.getId());
     for (Object o : world.getRealms()) {
       ClassRealm dep = (ClassRealm) o;
-      if ( !StringUtils.equals(dep.getId(), realm.getId())) {
+      if (!StringUtils.equals(dep.getId(), realm.getId())) {
         try {
           for (String packageName : packages) {
             dep.importFrom(realm.getId(), packageName);
index a842173f3a2067cc66660b2167173032c44f8833..2cc23e4afdf13d49716337f826a12ba2b86a8ae1 100644 (file)
@@ -78,16 +78,19 @@ public class JpaPlugin extends BaseIdentifiable {
 
   @Column(name = "core", updatable = true, nullable = true)
   private Boolean core;
-  
+
   @Column(name = "child_first_classloader", updatable = true, nullable = true)
   private Boolean childFirstClassLoader = Boolean.FALSE;
 
-  @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
+  @Column(name = "base_plugin", updatable = true, nullable = true)
+  private String basePlugin;
+
+  @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE,
             org.hibernate.annotations.CascadeType.DELETE,
             org.hibernate.annotations.CascadeType.MERGE,
             org.hibernate.annotations.CascadeType.PERSIST,
-            org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
-  @OneToMany(mappedBy = "plugin", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
+            org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
+  @OneToMany(mappedBy = "plugin", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
   private List<JpaPluginFile> files = new ArrayList<JpaPluginFile>();
 
   public JpaPlugin() {
@@ -218,6 +221,14 @@ public class JpaPlugin extends BaseIdentifiable {
     return this;
   }
 
+  public String getBasePlugin() {
+    return basePlugin;
+  }
+
+  public void setBasePlugin(String basePlugin) {
+    this.basePlugin = basePlugin;
+  }
+
   public void createFile(String filename) {
     JpaPluginFile file = new JpaPluginFile(this, filename);
     this.files.add(file);
index f4c958a145219072f49b889d5424b55268689a90..7551bff7795837c8bf775c5173b1545b0c8f9584 100644 (file)
@@ -31,7 +31,7 @@ import javax.persistence.*;
 public class SchemaMigration {
 
   public final static int VERSION_UNKNOWN = -1;
-  public static final int LAST_VERSION = 180;
+  public static final int LAST_VERSION = 181;
 
   public final static String TABLE_NAME = "schema_migrations";
 
index 579a381a7393e9432cba485e66ce5a9d55cc59ea..4ab85fcd1094cd1e4640c5878a62ebe992ae73b8 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
   <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2" />
+           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2" />
 
   <plugin_files id="3" plugin_id="1" filename="newfile.jar"/>
 </dataset>
\ No newline at end of file
index 38f8e3d72d1eb927792b20a65f31ec872cce98a1..484d192dbb8ba94888317048fb77b1bf681eb267 100644 (file)
@@ -1,9 +1,9 @@
 <dataset>
   <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2"/>
+           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2"/>
 
   <plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" version="[null]" />
+           description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" base_plugin="[null]" version="[null]" />
 
   <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
   <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
index 2ca3c309e6df7fc1f7319cfb52efd34107e42b8c..8bd8f817a45742dd284b7521aa770709039626e6 100644 (file)
@@ -1,9 +1,9 @@
 <dataset>
   <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2"/>
+           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2"/>
 
   <plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" version="2.2" />
+           description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" base_plugin="[null]" version="2.2" />
 
   <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
   <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
index 57dfd1358d74233486fb5f8b1d252ec661713364..f594347688fbbcddaf4a90e6c4b1a92415278eb8 100644 (file)
@@ -1,6 +1,6 @@
 <dataset>
   <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
-           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2" />
+           description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2" />
 
   <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
   <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
index eee0237bf3ee1ac5755789ecb4d3d9eb1d570020..170ade37686b76197b614535dae8b091a77ad451 100644 (file)
  */
 package org.sonar.server.plugins;
 
-import com.google.common.collect.Lists;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerComponent;
-import org.sonar.core.classloaders.ClassLoadersCollection;
-
 import java.io.File;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Collection;
 import java.util.List;
 
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.Logs;
+import org.sonar.core.classloaders.ClassLoadersCollection;
+
 public class PluginClassLoaders implements ServerComponent {
 
   private ClassLoadersCollection classLoaders = new ClassLoadersCollection(getClass().getClassLoader());
 
-  public ClassLoader create(PluginMetadata plugin) {
-    return create(plugin.getKey(), plugin.getDeployedFiles(), plugin.isUseChildFirstClassLoader());
+  private List<PluginMetadata> metadata = Lists.newArrayList();
+
+  public void addForCreation(PluginMetadata plugin) {
+    metadata.add(plugin);
   }
 
   ClassLoader create(String pluginKey, Collection<File> classloaderFiles, boolean useChildFirstClassLoader) {
@@ -51,6 +54,18 @@ public class PluginClassLoaders implements ServerComponent {
     }
   }
 
+  private void extend(String basePluginKey, String pluginKey, Collection<File> classloaderFiles) {
+    try {
+      List<URL> urls = Lists.newArrayList();
+      for (File file : classloaderFiles) {
+        urls.add(toUrl(file));
+      }
+      classLoaders.extend(basePluginKey, pluginKey, urls);
+    } catch (MalformedURLException e) {
+      throw new RuntimeException("Fail to load the classloader of the plugin: " + pluginKey, e);
+    }
+  }
+
   URL toUrl(File file) throws MalformedURLException {
     // From Classworlds javadoc :
     // A constituent is a URL that points to either a JAR format file containing
@@ -86,7 +101,30 @@ public class PluginClassLoaders implements ServerComponent {
     return clazz;
   }
 
-  public void done() {
+  public List<PluginMetadata> completeCreation() {
+    List<PluginMetadata> created = Lists.newArrayList();
+    for (PluginMetadata pluginMetadata : metadata) {
+      if (StringUtils.isEmpty(pluginMetadata.getBasePlugin())) {
+        create(pluginMetadata.getKey(), pluginMetadata.getDeployedFiles(), pluginMetadata.isUseChildFirstClassLoader());
+        created.add(pluginMetadata);
+      }
+    }
+    // Extend plugins by other plugins
+    for (PluginMetadata pluginMetadata : metadata) {
+      String pluginKey = pluginMetadata.getKey();
+      String basePluginKey = pluginMetadata.getBasePlugin();
+      if (StringUtils.isNotEmpty(pluginMetadata.getBasePlugin())) {
+        if (classLoaders.get(basePluginKey) != null) {
+          Logs.INFO.debug("Plugin {} extends {}", pluginKey, basePluginKey);
+          extend(basePluginKey, pluginKey, pluginMetadata.getDeployedFiles());
+          created.add(pluginMetadata);
+        } else {
+          // Ignored, because base plugin doesn't exists
+          Logs.INFO.warn("Plugin {} extends nonexistent plugin {}", pluginKey, basePluginKey);
+        }
+      }
+    }
     classLoaders.done();
+    return created;
   }
 }
index 2c5406cdf5ea0fc9a7cebec044c8823503fd052c..60d3ea35be0f84d30d47630675e6b8b4455463f2 100644 (file)
  */
 package org.sonar.server.plugins;
 
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.apache.commons.io.FileUtils;
@@ -38,15 +47,6 @@ import org.sonar.server.platform.DefaultServerFileSystem;
 import org.sonar.server.platform.ServerStartException;
 import org.sonar.updatecenter.common.PluginKeyUtils;
 
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
 public final class PluginDeployer implements ServerComponent {
 
   private static final Logger LOG = LoggerFactory.getLogger(PluginDeployer.class);
@@ -104,7 +104,7 @@ public final class PluginDeployer implements ServerComponent {
   public List<String> getUninstalls() {
     List<String> names = Lists.newArrayList();
     if (fileSystem.getRemovedPluginsDir().exists()) {
-      List<File> files = (List<File>) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[]{"jar"}, false);
+      List<File> files = (List<File>) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[] { "jar" }, false);
       for (File file : files) {
         names.add(file.getName());
       }
@@ -114,7 +114,7 @@ public final class PluginDeployer implements ServerComponent {
 
   public void cancelUninstalls() {
     if (fileSystem.getRemovedPluginsDir().exists()) {
-      List<File> files = (List<File>) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[]{"jar"}, false);
+      List<File> files = (List<File>) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[] { "jar" }, false);
       for (File file : files) {
         try {
           FileUtils.moveFileToDirectory(file, fileSystem.getUserPluginsDir(), false);
@@ -200,7 +200,7 @@ public final class PluginDeployer implements ServerComponent {
         }
         FileUtils.deleteQuietly(tempDir);
       }
-      classloaders.create(plugin);
+      classloaders.addForCreation(plugin);
 
     } catch (IOException e) {
       throw new RuntimeException("Fail to deploy the plugin " + plugin, e);
@@ -221,7 +221,7 @@ public final class PluginDeployer implements ServerComponent {
 
   private void moveAndLoadDownloadedPlugins() throws IOException {
     if (fileSystem.getDownloadedPluginsDir().exists()) {
-      Collection<File> jars = FileUtils.listFiles(fileSystem.getDownloadedPluginsDir(), new String[]{"jar"}, false);
+      Collection<File> jars = FileUtils.listFiles(fileSystem.getDownloadedPluginsDir(), new String[] { "jar" }, false);
       for (File jar : jars) {
         File movedJar = moveDownloadedFile(jar);
         if (movedJar != null) {
@@ -284,7 +284,7 @@ public final class PluginDeployer implements ServerComponent {
 
     String mainClass = plugin.getMainClass();
     try {
-      URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[]{tempFile.toURI().toURL()}, getClass().getClassLoader());
+      URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[] { tempFile.toURI().toURL() }, getClass().getClassLoader());
       Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance();
       plugin.setKey(PluginKeyUtils.sanitize(pluginInstance.getKey()));
       plugin.setDescription(pluginInstance.getDescription());
index b4e0fbad1378e364cbac2e0d70cf72c98a2e0387..8d1e2724721a8fba7fa15d3b6d5972496874810c 100644 (file)
  */
 package org.sonar.server.plugins;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.core.plugin.JpaPlugin;
 import org.sonar.updatecenter.common.PluginManifest;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * @since 2.2
  */
@@ -47,6 +47,7 @@ public class PluginMetadata {
   private String homepage;
   private boolean core;
   private boolean useChildFirstClassLoader;
+  private String basePlugin;
   private String[] dependencyPaths = new String[0];
   public List<File> deployedFiles = new ArrayList<File>();
 
@@ -167,6 +168,14 @@ public class PluginMetadata {
     return useChildFirstClassLoader;
   }
 
+  public void setBasePlugin(String key) {
+    this.basePlugin = key;
+  }
+
+  public String getBasePlugin() {
+    return basePlugin;
+  }
+
   public void setDependencyPaths(String[] paths) {
     this.dependencyPaths = paths;
   }
@@ -229,6 +238,7 @@ public class PluginMetadata {
     metadata.setDependencyPaths(manifest.getDependencies());
     metadata.setCore(corePlugin);
     metadata.setUseChildFirstClassLoader(manifest.isUseChildFirstClassLoader());
+    metadata.setBasePlugin(manifest.getExtendPlugin());
     return metadata;
   }
 
@@ -243,6 +253,7 @@ public class PluginMetadata {
     jpaPlugin.setHomepage(getHomepage());
     jpaPlugin.setCore(isCore());
     jpaPlugin.setUseChildFirstClassLoader(isUseChildFirstClassLoader());
+    jpaPlugin.setBasePlugin(getBasePlugin());
     jpaPlugin.removeFiles();
     for (File file : getDeployedFiles()) {
       jpaPlugin.createFile(file.getName());
index a5cc852215b537fd2da0188e66add8e1d3a0af6e..74a834c2563a4233e22bf676e305c61a5fe4979a 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.server.plugins;
 
+import java.util.List;
+
 import org.picocontainer.Characteristics;
 import org.picocontainer.MutablePicoContainer;
 import org.picocontainer.PicoContainer;
@@ -26,19 +28,15 @@ import org.sonar.api.Plugin;
 import org.sonar.api.ServerExtension;
 import org.sonar.api.utils.SonarException;
 import org.sonar.core.plugin.AbstractPluginRepository;
-import org.sonar.core.plugin.JpaPlugin;
-import org.sonar.core.plugin.JpaPluginDao;
 
 /**
  * @since 2.2
  */
 public class ServerPluginRepository extends AbstractPluginRepository {
 
-  private JpaPluginDao dao;
   private PluginClassLoaders classloaders;
 
-  public ServerPluginRepository(JpaPluginDao dao, PluginClassLoaders classloaders) {
-    this.dao = dao;
+  public ServerPluginRepository(PluginClassLoaders classloaders) {
     this.classloaders = classloaders;
   }
 
@@ -50,20 +48,18 @@ public class ServerPluginRepository extends AbstractPluginRepository {
 
   public void registerPlugins(MutablePicoContainer pico) {
     // Create ClassLoaders
-    for (JpaPlugin jpaPlugin : dao.getPlugins()) {
-      classloaders.getClassLoader(jpaPlugin.getKey());
-    }
-    classloaders.done();
+    List<PluginMetadata> register = classloaders.completeCreation();
     // Register plugins
-    for (JpaPlugin jpaPlugin : dao.getPlugins()) {
+    for (PluginMetadata pluginMetadata : register) {
       try {
-        Class pluginClass = classloaders.getClassLoader(jpaPlugin.getKey()).loadClass(jpaPlugin.getPluginClass());
+        Class pluginClass = classloaders.getClassLoader(pluginMetadata.getKey()).loadClass(pluginMetadata.getMainClass());
         pico.as(Characteristics.CACHE).addComponent(pluginClass);
         Plugin plugin = (Plugin) pico.getComponent(pluginClass);
-        registerPlugin(pico, plugin, jpaPlugin.getKey());
+        registerPlugin(pico, plugin, pluginMetadata.getKey());
 
       } catch (ClassNotFoundException e) {
-        throw new SonarException("Please check the plugin manifest. The main plugin class does not exist: " + jpaPlugin.getPluginClass(), e);
+        throw new SonarException(
+            "Please check the plugin manifest. The main plugin class does not exist: " + pluginMetadata.getMainClass(), e);
       }
     }
     invokeExtensionProviders(pico);
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/181_add_plugin_base.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/181_add_plugin_base.rb
new file mode 100644 (file)
index 0000000..14a9eab
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+
+#
+# Sonar 2.6
+#
+class AddPluginBase < ActiveRecord::Migration
+
+  def self.up
+    add_column 'plugins', 'base_plugin', :string, :limit => 100, :null => true
+    Plugin.reset_column_information
+  end
+
+end
index f7acccd64c0a9a08470708a20ab7e9abc9ea418a..421241566731370efc2d165987acbad8ad271281 100644 (file)
@@ -22,14 +22,13 @@ package org.sonar.server.plugins;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
-import org.junit.Test;
-import org.sonar.test.TestUtils;
-
-import com.google.common.collect.Lists;
-
 import java.io.File;
 import java.io.IOException;
 
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.test.TestUtils;
+
 public class PluginClassLoadersTest {
 
   @Test
@@ -43,7 +42,7 @@ public class PluginClassLoadersTest {
     assertNull(getClass().getClassLoader().getResource("foo.txt"));
 
     PluginClassLoaders classloaders = new PluginClassLoaders();
-    ClassLoader classloader = classloaders.create(metadata);
+    ClassLoader classloader = classloaders.create(metadata.getKey(), metadata.getDeployedFiles(), metadata.isUseChildFirstClassLoader());
 
     assertNotNull(classloader);
     assertNotNull(classloader.getResource("foo.txt"));
index 868464c7879dc51abb12ee78f36826bfcbac6a51..37eafa9e03f106b7ae437c971af3c2329ed60d3a 100644 (file)
@@ -91,6 +91,7 @@ public class PluginDeployerTest extends AbstractDbUnitTestCase {
     assertThat(deployedJar.isFile(), is(true));
 
     // check that the plugin has its own classloader
+    classloaders.completeCreation();
     ClassLoader classloader = classloaders.getClassLoader("foo");
     assertNotNull(classloader);
   }
@@ -118,6 +119,7 @@ public class PluginDeployerTest extends AbstractDbUnitTestCase {
     assertThat(deployedJar.isFile(), is(true));
 
     // check that the plugin has its own classloader
+    classloaders.completeCreation();
     ClassLoader classloader = classloaders.getClassLoader("buildbreaker");
     assertNotNull(classloader);
     assertNotNull(classloader.loadClass("org.sonar.plugins.buildbreaker.BuildBreakerPlugin"));
@@ -143,6 +145,7 @@ public class PluginDeployerTest extends AbstractDbUnitTestCase {
     assertThat(deployedJar.isFile(), is(true));
 
     // check that the extension is in the classloader
+    classloaders.completeCreation();
     ClassLoader classloader = classloaders.getClassLoader("foo");
     File extensionFile = FileUtils.toFile(classloader.getResource("foo-extension.txt"));
     assertThat(extensionFile.exists(), is(true));