aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2011-02-08 00:51:56 +0300
committerEvgeny Mandrikov <mandrikov@gmail.com>2011-02-08 12:05:17 +0300
commit1639e4fba1bd2203f39104076d85d69bd0228c8b (patch)
treeca8a6ef1af2e79aba281194dd37fec7c7151b729
parent54a6901006ec1ec7f498cbb968c47a05e4075937 (diff)
downloadsonarqube-1639e4fba1bd2203f39104076d85d69bd0228c8b.tar.gz
sonarqube-1639e4fba1bd2203f39104076d85d69bd0228c8b.zip
SONAR-2075: Add the ability for a plugin to extend an other plugin
-rw-r--r--pom.xml2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java58
-rw-r--r--sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java21
-rw-r--r--sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java19
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml4
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml4
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java56
-rw-r--r--sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java28
-rw-r--r--sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java21
-rw-r--r--sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java22
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/181_add_plugin_base.rb31
-rw-r--r--sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java11
-rw-r--r--sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java3
16 files changed, 206 insertions, 80 deletions
diff --git a/pom.xml b/pom.xml
index 2d7283c23df..07d4f902951 100644
--- a/pom.xml
+++ b/pom.xml
@@ -421,7 +421,7 @@
<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>
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
index 5ec8ac920ae..c883998d927 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
@@ -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();
diff --git a/sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java b/sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java
index 9df43699ec8..0914ecc37e0 100644
--- a/sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java
+++ b/sonar-core/src/main/java/org/sonar/core/classloaders/ClassLoadersCollection.java
@@ -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);
diff --git a/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
index a842173f3a2..2cc23e4afdf 100644
--- a/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
+++ b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
@@ -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);
diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
index f4c958a1452..7551bff7795 100644
--- a/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
+++ b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
@@ -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";
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
index 579a381a739..4ab85fcd109 100644
--- a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
@@ -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
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
index 38f8e3d72d1..484d192dbb8 100644
--- a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
@@ -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"/>
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
index 2ca3c309e6d..8bd8f817a45 100644
--- a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
@@ -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"/>
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
index 57dfd1358d7..f594347688f 100644
--- a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
@@ -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"/>
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java b/sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java
index eee0237bf3e..170ade37686 100644
--- a/sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java
+++ b/sonar-server/src/main/java/org/sonar/server/plugins/PluginClassLoaders.java
@@ -19,24 +19,27 @@
*/
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;
}
}
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java b/sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java
index 2c5406cdf5e..60d3ea35be0 100644
--- a/sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java
+++ b/sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java
@@ -19,6 +19,15 @@
*/
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());
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java b/sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java
index b4e0fbad137..8d1e2724721 100644
--- a/sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java
+++ b/sonar-server/src/main/java/org/sonar/server/plugins/PluginMetadata.java
@@ -19,17 +19,17 @@
*/
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());
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
index a5cc852215b..74a834c2563 100644
--- a/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
+++ b/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
@@ -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
index 00000000000..14a9eab96ab
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/181_add_plugin_base.rb
@@ -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
diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java
index f7acccd64c0..42124156673 100644
--- a/sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/plugins/PluginClassLoadersTest.java
@@ -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"));
diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java
index 868464c7879..37eafa9e03f 100644
--- a/sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java
@@ -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));