From: Evgeny Mandrikov Date: Mon, 7 Feb 2011 21:51:56 +0000 (+0300) Subject: SONAR-2075: Add the ability for a plugin to extend an other plugin X-Git-Tag: 2.6~27 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1639e4fba1bd2203f39104076d85d69bd0228c8b;p=sonarqube.git SONAR-2075: Add the ability for a plugin to extend an other plugin --- diff --git a/pom.xml b/pom.xml index 2d7283c23df..07d4f902951 100644 --- a/pom.xml +++ b/pom.xml @@ -421,7 +421,7 @@ org.codehaus.sonar sonar-update-center-common - 1.0 + 1.1-SNAPSHOT org.codehaus.sonar 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 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 download(JpaPlugin pluginMetadata) { + List 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 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 jpaPlugins = dao.getPlugins(); + + for (JpaPlugin pluginMetadata : jpaPlugins) { + if (StringUtils.isEmpty(pluginMetadata.getBasePlugin())) { + String key = pluginMetadata.getKey(); + List 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 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 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 files = new ArrayList(); 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 @@ + description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2" /> \ 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 @@ + description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2"/> + description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" base_plugin="[null]" version="[null]" /> 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 @@ + description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" 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" /> 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 @@ + description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" base_plugin="[null]" version="2.2" /> 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 metadata = Lists.newArrayList(); + + public void addForCreation(PluginMetadata plugin) { + metadata.add(plugin); } ClassLoader create(String pluginKey, Collection classloaderFiles, boolean useChildFirstClassLoader) { @@ -51,6 +54,18 @@ public class PluginClassLoaders implements ServerComponent { } } + private void extend(String basePluginKey, String pluginKey, Collection classloaderFiles) { + try { + List 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 completeCreation() { + List 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 getUninstalls() { List names = Lists.newArrayList(); if (fileSystem.getRemovedPluginsDir().exists()) { - List files = (List) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[]{"jar"}, false); + List files = (List) 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 files = (List) FileUtils.listFiles(fileSystem.getRemovedPluginsDir(), new String[]{"jar"}, false); + List files = (List) 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 jars = FileUtils.listFiles(fileSystem.getDownloadedPluginsDir(), new String[]{"jar"}, false); + Collection 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 deployedFiles = new ArrayList(); @@ -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 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));