diff options
Diffstat (limited to 'server')
87 files changed, 1462 insertions, 1134 deletions
diff --git a/server/sonar-server/pom.xml b/server/sonar-server/pom.xml index 78e7f995a1e..fd3ee697d4f 100644 --- a/server/sonar-server/pom.xml +++ b/server/sonar-server/pom.xml @@ -248,7 +248,19 @@ <directory>src/main/resources</directory> <filtering>true</filtering> </resource> + </resources> + <testResources> + <testResource> + <directory>src/test/resources</directory> + <filtering>false</filtering> + </testResource> + <testResource> + <directory>src/test/projects</directory> + <filtering>false</filtering> + </testResource> + + </testResources> <plugins> <plugin> diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java index 95ad3ef370e..d95084053ae 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java @@ -19,7 +19,7 @@ */ package org.sonar.server.computation; -import org.sonar.api.platform.ComponentContainer; +import org.sonar.core.platform.ComponentContainer; import org.sonar.core.issue.db.UpdateConflictResolver; import org.sonar.server.computation.issue.*; import org.sonar.server.computation.measure.MetricCache; diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java index b506fc5244a..7158b0d5e6d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java @@ -29,9 +29,9 @@ import org.sonar.api.platform.ServerUpgradeStatus; import org.sonar.api.utils.log.Loggers; import org.sonar.core.persistence.DdlUtils; import org.sonar.server.db.DbClient; -import org.sonar.server.plugins.ServerPluginRepository; import com.google.common.annotations.VisibleForTesting; +import org.sonar.server.plugins.ServerPluginRepository; /** * Restore schema by executing DDL scripts. Only H2 database is supported. @@ -46,10 +46,10 @@ public class DatabaseMigrator implements ServerComponent, Startable { private final ServerUpgradeStatus serverUpgradeStatus; /** - * ServerPluginRepository is used to ensure H2 schema creation is done only after copy of bundle plugins have been done + * ServerPluginInstaller is used to ensure H2 schema creation is done only after copy of bundle plugins have been done */ public DatabaseMigrator(DbClient dbClient, MigrationStep[] migrations, ServerUpgradeStatus serverUpgradeStatus, - ServerPluginRepository serverPluginRepository) { + ServerPluginRepository unused) { this.dbClient = dbClient; this.migrations = migrations; this.serverUpgradeStatus = serverUpgradeStatus; diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java index b0e1cb6bd20..da45c47bf68 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java @@ -25,9 +25,9 @@ import com.google.common.collect.Maps; import org.apache.commons.io.Charsets; import org.picocontainer.Startable; import org.sonar.api.Plugin; -import org.sonar.api.ServerExtension; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; +import org.sonar.api.ServerComponent; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import java.io.InputStreamReader; import java.io.Reader; @@ -45,7 +45,7 @@ import static com.google.common.collect.Lists.newArrayList; * they must be named "<pluginKey>-model.xml". * </p> */ -public class DebtModelPluginRepository implements ServerExtension, Startable { +public class DebtModelPluginRepository implements ServerComponent, Startable { public static final String DEFAULT_MODEL = "technical-debt"; @@ -87,14 +87,12 @@ public class DebtModelPluginRepository implements ServerExtension, Startable { contributingPluginKeyToClassLoader = Maps.newTreeMap(); // Add default model contributingPluginKeyToClassLoader.put(DEFAULT_MODEL, getClass().getClassLoader()); - for (PluginMetadata metadata : pluginRepository.getMetadata()) { - String pluginKey = metadata.getKey(); - Plugin plugin = pluginRepository.getPlugin(pluginKey); - if (plugin != null) { - ClassLoader classLoader = plugin.getClass().getClassLoader(); - if (classLoader.getResource(getXMLFilePath(pluginKey)) != null) { - contributingPluginKeyToClassLoader.put(pluginKey, classLoader); - } + for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { + String pluginKey = pluginInfo.getKey(); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); + ClassLoader classLoader = plugin.getClass().getClassLoader(); + if (classLoader.getResource(getXMLFilePath(pluginKey)) != null) { + contributingPluginKeyToClassLoader.put(pluginKey, classLoader); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java b/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java index df520c3900a..5258e753057 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java @@ -127,27 +127,16 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable { return new File(getHomeDir(), "extensions/downloads"); } - public File getTrashPluginsDir() { - return new File(getHomeDir(), "extensions/trash"); - } - - public List<File> getCorePlugins() { - File corePluginsDir = new File(getHomeDir(), "lib/core-plugins"); - return getFiles(corePluginsDir, "jar"); - } - - public List<File> getBundledPlugins() { - File corePluginsDir = new File(getHomeDir(), "lib/bundled-plugins"); - return getFiles(corePluginsDir, "jar"); + public File getInstalledPluginsDir() { + return new File(getHomeDir(), "extensions/plugins"); } - public List<File> getUserPlugins() { - File pluginsDir = getUserPluginsDir(); - return getFiles(pluginsDir, "jar"); + public File getBundledPluginsDir() { + return new File(getHomeDir(), "lib/bundled-plugins"); } - public File getUserPluginsDir() { - return new File(getHomeDir(), "extensions/plugins"); + public File getCorePluginsDir() { + return new File(getHomeDir(), "lib/core-plugins"); } public File getDeprecatedPluginsDir() { @@ -172,7 +161,7 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable { } private List<File> getFiles(File dir, String... fileSuffixes) { - List<File> files = new ArrayList<File>(); + List<File> files = new ArrayList<>(); if (dir != null && dir.exists()) { if (fileSuffixes != null && fileSuffixes.length > 0) { files.addAll(FileUtils.listFiles(dir, fileSuffixes, false)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 5d857910be2..4f28227339e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -19,16 +19,17 @@ */ package org.sonar.server.platform; -import java.util.Collection; -import java.util.Properties; -import javax.annotation.CheckForNull; -import javax.servlet.ServletContext; -import org.sonar.api.platform.ComponentContainer; +import org.sonar.core.platform.ComponentContainer; import org.sonar.api.platform.Server; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.persistence.DatabaseVersion; +import javax.annotation.CheckForNull; +import javax.servlet.ServletContext; +import java.util.Collection; +import java.util.Properties; + /** * @since 2.2 */ diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java b/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java index c034a78ee38..8f7cb251897 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java @@ -25,11 +25,11 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; import org.sonar.api.Plugin; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.platform.ServerFileSystem; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import javax.annotation.Nullable; @@ -47,28 +47,26 @@ public class RailsAppsDeployer implements Startable { private static final Logger LOG = Loggers.get(RailsAppsDeployer.class); private static final String ROR_PATH = "org/sonar/ror/"; - private final ServerFileSystem fileSystem; + private final ServerFileSystem fs; private final PluginRepository pluginRepository; - public RailsAppsDeployer(ServerFileSystem fileSystem, PluginRepository pluginRepository) { - this.fileSystem = fileSystem; + public RailsAppsDeployer(ServerFileSystem fs, PluginRepository pluginRepository) { + this.fs = fs; this.pluginRepository = pluginRepository; } @Override public void start() { - LOG.info("Deploy Ruby on Rails applications"); + LOG.info("Deploying Ruby on Rails applications"); File appsDir = prepareRailsDirectory(); - for (PluginMetadata pluginMetadata : pluginRepository.getMetadata()) { - String pluginKey = pluginMetadata.getKey(); - Plugin plugin = pluginRepository.getPlugin(pluginKey); - if (plugin != null) { - try { - deployRailsApp(appsDir, pluginKey, plugin.getClass().getClassLoader()); - } catch (Exception e) { - throw new IllegalStateException("Fail to deploy Ruby on Rails application: " + pluginKey, e); - } + for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { + String pluginKey = pluginInfo.getKey(); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); + try { + deployRailsApp(appsDir, pluginKey, plugin.getClass().getClassLoader()); + } catch (Exception e) { + throw new IllegalStateException(String.format("Fail to deploy Ruby on Rails application of plugin [%s]", pluginKey), e); } } } @@ -80,7 +78,7 @@ public class RailsAppsDeployer implements Startable { @VisibleForTesting File prepareRailsDirectory() { - File appsDir = new File(fileSystem.getTempDir(), "ror"); + File appsDir = new File(fs.getTempDir(), "ror"); prepareDir(appsDir); return appsDir; } @@ -88,7 +86,7 @@ public class RailsAppsDeployer implements Startable { @VisibleForTesting static void deployRailsApp(File appsDir, final String pluginKey, ClassLoader appClassLoader) { if (hasRailsApp(pluginKey, appClassLoader)) { - LOG.info("Deploy app: " + pluginKey); + LOG.info("Deploying app: " + pluginKey); File appDir = new File(appsDir, pluginKey); ClassLoaderUtils.copyResources(appClassLoader, pathToRubyInitFile(pluginKey), appDir, new Function<String, String>() { @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index b6347f84e61..6844878d9b9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -22,7 +22,6 @@ package org.sonar.server.platform; import com.google.common.collect.Lists; import org.sonar.api.config.EmailSettings; import org.sonar.api.issue.action.Actions; -import org.sonar.api.platform.ComponentContainer; import org.sonar.api.profiles.AnnotationProfileParser; import org.sonar.api.profiles.XMLProfileParser; import org.sonar.api.profiles.XMLProfileSerializer; @@ -58,6 +57,8 @@ import org.sonar.core.persistence.DefaultDatabase; import org.sonar.core.persistence.MyBatis; import org.sonar.core.persistence.SemaphoreUpdater; import org.sonar.core.persistence.SemaphoresImpl; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.platform.PluginLoader; import org.sonar.core.purge.PurgeProfiler; import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao; import org.sonar.core.qualitygate.db.QualityGateConditionDao; @@ -216,9 +217,8 @@ import org.sonar.server.platform.ws.UpgradesSystemWsAction; import org.sonar.server.plugins.InstalledPluginReferentialFactory; import org.sonar.server.plugins.PluginDownloader; import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.ServerPluginJarInstaller; -import org.sonar.server.plugins.ServerPluginJarsInstaller; import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.ServerPluginUnzipper; import org.sonar.server.plugins.UpdateCenterClient; import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.plugins.ws.AvailablePluginsWsAction; @@ -519,10 +519,10 @@ class ServerComponents { PlatformRubyBridge.class, // plugins - ServerPluginJarsInstaller.class, - ServerPluginJarInstaller.class, - InstalledPluginReferentialFactory.class, ServerPluginRepository.class, + ServerPluginUnzipper.class, + PluginLoader.class, + InstalledPluginReferentialFactory.class, ServerExtensionInstaller.class, // depends on plugins diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java index 9ec58dc4044..591168f2831 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java @@ -22,12 +22,13 @@ package org.sonar.server.platform.monitoring; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; +import org.sonar.updatecenter.common.Version; + +import javax.annotation.Nonnull; import java.util.LinkedHashMap; -import java.util.List; /** * Installed plugins (excluding core plugins) @@ -47,21 +48,24 @@ public class PluginsMonitor implements Monitor { @Override public LinkedHashMap<String, Object> attributes() { LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); - for (PluginMetadata plugin : plugins()) { + for (PluginInfo plugin : plugins()) { LinkedHashMap<String, Object> pluginAttributes = new LinkedHashMap<>(); pluginAttributes.put("Name", plugin.getName()); - pluginAttributes.put("Version", plugin.getVersion()); + Version version = plugin.getVersion(); + if (version != null) { + pluginAttributes.put("Version", version.getName()); + } attributes.put(plugin.getKey(), pluginAttributes); } return attributes; } - private List<PluginMetadata> plugins() { - return Lists.newArrayList(Iterables.filter(repository.getMetadata(), new Predicate<PluginMetadata>() { + private Iterable<PluginInfo> plugins() { + return Iterables.filter(repository.getPluginInfos(), new Predicate<PluginInfo>() { @Override - public boolean apply(PluginMetadata input) { - return !input.isCore(); + public boolean apply(@Nonnull PluginInfo info) { + return !info.isCore(); } - })); + }); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java index 257cd22ca05..599899c475c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java @@ -20,7 +20,7 @@ package org.sonar.server.plugins; import org.picocontainer.Startable; -import org.sonar.api.platform.PluginRepository; +import org.sonar.core.platform.PluginRepository; import org.sonar.updatecenter.common.PluginReferential; public class InstalledPluginReferentialFactory implements Startable { @@ -51,7 +51,7 @@ public class InstalledPluginReferentialFactory implements Startable { } private void init() { - installedPluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(pluginRepository.getMetadata()); + installedPluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(pluginRepository.getPluginInfos()); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java index 1e2dd190f49..b17ac977342 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java @@ -25,7 +25,7 @@ import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.SonarException; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.server.platform.DefaultServerFileSystem; import org.sonar.updatecenter.common.Release; import org.sonar.updatecenter.common.Version; @@ -48,6 +48,10 @@ import static org.apache.commons.io.FileUtils.forceMkdir; import static org.apache.commons.io.FileUtils.toFile; import static org.apache.commons.lang.StringUtils.substringAfterLast; +/** + * Downloads plugins from update center. Files are copied in the directory extensions/downloads and then + * moved to extensions/plugins after server restart. + */ public class PluginDownloader implements Startable { private static final Logger LOG = Loggers.get(PluginDownloader.class); @@ -56,21 +60,17 @@ public class PluginDownloader implements Startable { private final UpdateCenterMatrixFactory updateCenterMatrixFactory; private final HttpDownloader downloader; - private final ServerPluginJarInstaller installer; private final File downloadDir; public PluginDownloader(UpdateCenterMatrixFactory updateCenterMatrixFactory, HttpDownloader downloader, - DefaultServerFileSystem fileSystem, ServerPluginJarInstaller installer) { + DefaultServerFileSystem fileSystem) { this.updateCenterMatrixFactory = updateCenterMatrixFactory; this.downloader = downloader; - this.installer = installer; this.downloadDir = fileSystem.getDownloadedPluginsDir(); } /** - * Delete the temporary files remaining from previous downloads - * - * @see #downloadRelease(org.sonar.updatecenter.common.Release) + * Deletes the temporary files remaining from previous downloads */ @Override public void start() { @@ -112,10 +112,10 @@ public class PluginDownloader implements Startable { } /** - * @return the list of download plugins as {@link DefaultPluginMetadata} instances + * @return the list of download plugins as {@link PluginInfo} instances */ - public Collection<DefaultPluginMetadata> getDownloadedPlugins() { - return newArrayList(transform(listPlugins(this.downloadDir), installer.fileToPlugin())); + public Collection<PluginInfo> getDownloadedPlugins() { + return newArrayList(transform(listPlugins(this.downloadDir), PluginInfo.JarToPluginInfo.INSTANCE)); } public void download(String pluginKey, Version version) { @@ -154,10 +154,10 @@ public class PluginDownloader implements Startable { } private static Collection<File> listTempFile(File dir) { - return FileUtils.listFiles(dir, new String[]{TMP_SUFFIX}, false); + return FileUtils.listFiles(dir, new String[] {TMP_SUFFIX}, false); } private static Collection<File> listPlugins(File dir) { - return FileUtils.listFiles(dir, new String[]{PLUGIN_EXTENSION}, false); + return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java index 30646c02a95..70afcb42068 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java @@ -19,10 +19,11 @@ */ package org.sonar.server.plugins; -import org.sonar.api.platform.PluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.updatecenter.common.PluginManifest; import org.sonar.updatecenter.common.PluginReferential; import org.sonar.updatecenter.common.PluginReferentialManifestConverter; +import org.sonar.updatecenter.common.Version; import java.util.Collection; import java.util.List; @@ -35,14 +36,14 @@ public class PluginReferentialMetadataConverter { // Only static call } - public static PluginReferential getInstalledPluginReferential(Collection<PluginMetadata> metadata) { + public static PluginReferential getInstalledPluginReferential(Collection<PluginInfo> metadata) { List<PluginManifest> pluginManifestList = getPluginManifestList(metadata); return PluginReferentialManifestConverter.fromPluginManifests(pluginManifestList); } - private static List<PluginManifest> getPluginManifestList(Collection<PluginMetadata> metadata) { + private static List<PluginManifest> getPluginManifestList(Collection<PluginInfo> metadata) { List<PluginManifest> pluginManifestList = newArrayList(); - for (PluginMetadata plugin : metadata) { + for (PluginInfo plugin : metadata) { if (!plugin.isCore()) { pluginManifestList.add(toPluginManifest(plugin)); } @@ -50,20 +51,23 @@ public class PluginReferentialMetadataConverter { return pluginManifestList; } - private static PluginManifest toPluginManifest(PluginMetadata metadata) { + private static PluginManifest toPluginManifest(PluginInfo metadata) { PluginManifest pluginManifest = new PluginManifest(); pluginManifest.setKey(metadata.getKey()); pluginManifest.setName(metadata.getName()); - pluginManifest.setVersion(metadata.getVersion()); + Version version = metadata.getVersion(); + if (version != null) { + pluginManifest.setVersion(version.getName()); + } pluginManifest.setDescription(metadata.getDescription()); pluginManifest.setMainClass(metadata.getMainClass()); - pluginManifest.setOrganization(metadata.getOrganization()); + pluginManifest.setOrganization(metadata.getOrganizationName()); pluginManifest.setOrganizationUrl(metadata.getOrganizationUrl()); pluginManifest.setLicense(metadata.getLicense()); - pluginManifest.setHomepage(metadata.getHomepage()); + pluginManifest.setHomepage(metadata.getHomepageUrl()); pluginManifest.setIssueTrackerUrl(metadata.getIssueTrackerUrl()); pluginManifest.setBasePlugin(metadata.getBasePlugin()); - pluginManifest.setRequirePlugins(metadata.getRequiredPlugins().toArray(new String []{})); + pluginManifest.setRequirePlugins(metadata.getRequiredPlugins().toArray(new String[] {})); return pluginManifest; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java index c084470711c..e9297de4353 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java @@ -24,40 +24,43 @@ import com.google.common.collect.ListMultimap; import org.sonar.api.Extension; import org.sonar.api.ExtensionProvider; import org.sonar.api.Plugin; +import org.sonar.api.ServerComponent; import org.sonar.api.ServerExtension; -import org.sonar.api.platform.ComponentContainer; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import java.util.Map; /** - * This class adds to picocontainer the server extensions provided by plugins + * Loads the plugins server extensions and injects them to DI container */ -public class ServerExtensionInstaller { - private PluginRepository pluginRepository; +public class ServerExtensionInstaller implements ServerComponent { + + private final PluginRepository pluginRepository; public ServerExtensionInstaller(PluginRepository pluginRepository) { this.pluginRepository = pluginRepository; } public void installExtensions(ComponentContainer container) { - ListMultimap<PluginMetadata, Object> installedExtensionsByPlugin = ArrayListMultimap.create(); + ListMultimap<PluginInfo, Object> installedExtensionsByPlugin = ArrayListMultimap.create(); - for (PluginMetadata pluginMetadata : pluginRepository.getMetadata()) { - Plugin plugin = pluginRepository.getPlugin(pluginMetadata.getKey()); - container.addExtension(pluginMetadata, plugin); + for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { + String pluginKey = pluginInfo.getKey(); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); + container.addExtension(pluginInfo, plugin); for (Object extension : plugin.getExtensions()) { - if (installExtension(container, pluginMetadata, extension, true) != null) { - installedExtensionsByPlugin.put(pluginMetadata, extension); + if (installExtension(container, pluginInfo, extension, true) != null) { + installedExtensionsByPlugin.put(pluginInfo, extension); } else { - container.declareExtension(pluginMetadata, extension); + container.declareExtension(pluginInfo, extension); } } } - for (Map.Entry<PluginMetadata, Object> entry : installedExtensionsByPlugin.entries()) { - PluginMetadata plugin = entry.getKey(); + for (Map.Entry<PluginInfo, Object> entry : installedExtensionsByPlugin.entries()) { + PluginInfo plugin = entry.getKey(); Object extension = entry.getValue(); if (isExtensionProvider(extension)) { ExtensionProvider provider = (ExtensionProvider) container.getComponentByKey(extension); @@ -66,25 +69,25 @@ public class ServerExtensionInstaller { } } - private void installProvider(ComponentContainer container, PluginMetadata plugin, ExtensionProvider provider) { + private void installProvider(ComponentContainer container, PluginInfo pluginInfo, ExtensionProvider provider) { Object obj = provider.provide(); if (obj != null) { if (obj instanceof Iterable) { for (Object ext : (Iterable) obj) { - installExtension(container, plugin, ext, false); + installExtension(container, pluginInfo, ext, false); } } else { - installExtension(container, plugin, obj, false); + installExtension(container, pluginInfo, obj, false); } } } - Object installExtension(ComponentContainer container, PluginMetadata pluginMetadata, Object extension, boolean acceptProvider) { + Object installExtension(ComponentContainer container, PluginInfo pluginInfo, Object extension, boolean acceptProvider) { if (isType(extension, ServerExtension.class)) { if (!acceptProvider && isExtensionProvider(extension)) { throw new IllegalStateException("ExtensionProvider can not include providers itself: " + extension); } - container.addExtension(pluginMetadata, extension); + container.addExtension(pluginInfo, extension); return extension; } return null; diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarInstaller.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarInstaller.java deleted file mode 100644 index 777a197defd..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarInstaller.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.plugins; - -import org.apache.commons.io.FileUtils; -import org.sonar.api.utils.ZipUtils; -import org.sonar.core.plugins.DefaultPluginMetadata; -import org.sonar.core.plugins.PluginJarInstaller; - -import java.io.File; -import java.io.IOException; -import java.util.zip.ZipEntry; - -public class ServerPluginJarInstaller extends PluginJarInstaller { - - public void installToDir(DefaultPluginMetadata metadata, File pluginBasedir) { - try { - File pluginFile = metadata.getFile(); - File deployedPlugin = copyPlugin(pluginBasedir, pluginFile); - install(metadata, pluginBasedir, deployedPlugin); - } catch (IOException e) { - throw new IllegalStateException(FAIL_TO_INSTALL_PLUGIN + metadata, e); - } - } - - private File copyPlugin(File pluginBasedir, File pluginFile) throws IOException { - FileUtils.forceMkdir(pluginBasedir); - File targetFile = new File(pluginBasedir, pluginFile.getName()); - FileUtils.copyFile(pluginFile, targetFile); - return targetFile; - } - - @Override - protected File extractPluginDependencies(File pluginFile, File pluginBasedir) throws IOException { - ZipUtils.unzip(pluginFile, pluginBasedir, new LibFilter()); - return pluginBasedir; - } - - private static final class LibFilter implements ZipUtils.ZipEntryFilter { - @Override - public boolean accept(ZipEntry entry) { - return entry.getName().startsWith("META-INF/lib"); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarsInstaller.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarsInstaller.java deleted file mode 100644 index 9b9ce32dc81..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarsInstaller.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.plugins; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.Maps; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.Server; -import org.sonar.api.platform.ServerUpgradeStatus; -import org.sonar.api.utils.MessageException; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.log.Profiler; -import org.sonar.core.plugins.DefaultPluginMetadata; -import org.sonar.server.platform.DefaultServerFileSystem; -import org.sonar.updatecenter.common.PluginReferential; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; -import static java.lang.String.format; -import static org.apache.commons.io.FileUtils.cleanDirectory; -import static org.apache.commons.io.FileUtils.copyFile; -import static org.apache.commons.io.FileUtils.deleteDirectory; -import static org.apache.commons.io.FileUtils.deleteQuietly; -import static org.apache.commons.io.FileUtils.forceMkdir; -import static org.apache.commons.io.FileUtils.listFiles; -import static org.apache.commons.io.FileUtils.moveFile; -import static org.apache.commons.io.FileUtils.moveFileToDirectory; -import static org.apache.commons.lang.StringUtils.isNotBlank; - -public class ServerPluginJarsInstaller { - - private static final Logger LOG = Loggers.get(ServerPluginJarsInstaller.class); - private static final String FILE_EXTENSION_JAR = "jar"; - private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls(); - - private final Server server; - private final DefaultServerFileSystem fs; - private final ServerPluginJarInstaller installer; - private final Map<String, PluginMetadata> pluginByKeys = Maps.newHashMap(); - private final ServerUpgradeStatus serverUpgradeStatus; - private static final Set<String> BLACKLISTED_PLUGINS = new HashSet<String>(Arrays.asList("scmactivity", "issuesreport")); - - public ServerPluginJarsInstaller(Server server, ServerUpgradeStatus serverUpgradeStatus, - DefaultServerFileSystem fs, ServerPluginJarInstaller installer) { - this.server = server; - this.serverUpgradeStatus = serverUpgradeStatus; - this.fs = fs; - this.installer = installer; - } - - public void install() { - Profiler profiler = Profiler.create(LOG).startInfo("Install plugins"); - deleteTrash(); - loadInstalledPlugins(); - copyBundledPlugins(); - moveDownloadedPlugins(); - loadCorePlugins(); - deployPlugins(); - profiler.stopDebug(); - } - - private void deleteTrash() { - File trashDir = fs.getTrashPluginsDir(); - try { - if (trashDir.exists()) { - deleteDirectory(trashDir); - } - } catch (IOException e) { - throw new IllegalStateException("Fail to clean the plugin trash directory: " + trashDir, e); - } - } - - private void loadInstalledPlugins() { - for (File file : fs.getUserPlugins()) { - PluginMetadata metadata = installer.fileToPlugin().apply(file); - if (isNotBlank(metadata.getKey())) { - loadInstalledPlugin(metadata); - } - } - } - - private void loadInstalledPlugin(PluginMetadata metadata) { - if (BLACKLISTED_PLUGINS.contains(metadata.getKey())) { - LOG.warn("Plugin {} is blacklisted. Please uninstall it.", metadata.getName()); - } else { - PluginMetadata existing = pluginByKeys.put(metadata.getKey(), metadata); - if (existing != null) { - throw MessageException.of(format("Found two files for the same plugin '%s': %s and %s", - metadata.getKey(), metadata.getFile().getName(), existing.getFile().getName())); - } - } - } - - private void moveDownloadedPlugins() { - if (fs.getDownloadedPluginsDir().exists()) { - for (File sourceFile : listJarFiles(fs.getDownloadedPluginsDir())) { - overridePlugin(sourceFile, true); - } - } - } - - private void copyBundledPlugins() { - if (serverUpgradeStatus.isFreshInstall()) { - for (File sourceFile : fs.getBundledPlugins()) { - PluginMetadata metadata = installer.fileToPlugin().apply(sourceFile); - // lib/bundled-plugins should be copied only if the plugin is not already - // available in extensions/plugins - if (!pluginByKeys.containsKey(metadata.getKey())) { - overridePlugin(sourceFile, false); - } - } - } - } - - private void overridePlugin(File sourceFile, boolean deleteSource) { - File destDir = fs.getUserPluginsDir(); - File destFile = new File(destDir, sourceFile.getName()); - if (destFile.exists()) { - // plugin with same filename already installed - deleteQuietly(destFile); - } - - try { - if (deleteSource) { - moveFile(sourceFile, destFile); - } else { - copyFile(sourceFile, destFile, true); - } - } catch (IOException e) { - LOG.error(format("Fail to move or copy plugin: %s to %s", - sourceFile.getAbsolutePath(), destFile.getAbsolutePath()), e); - } - - PluginMetadata metadata = installer.fileToPlugin().apply(destFile); - if (isNotBlank(metadata.getKey())) { - PluginMetadata existing = pluginByKeys.put(metadata.getKey(), metadata); - if (existing != null) { - if (!existing.getFile().getName().equals(destFile.getName())) { - deleteQuietly(existing.getFile()); - } - LOG.info("Plugin " + metadata.getKey() + " replaced by new version"); - } - } - } - - private void loadCorePlugins() { - for (File file : fs.getCorePlugins()) { - PluginMetadata metadata = installer.fileToCorePlugin().apply(file); - PluginMetadata existing = pluginByKeys.put(metadata.getKey(), metadata); - if (existing != null) { - throw new IllegalStateException("Found two plugins with the same key '" + metadata.getKey() + "': " + metadata.getFile().getName() + " and " - + existing.getFile().getName()); - } - } - } - - public void uninstall(String pluginKey) { - for (String key : getPluginReferential().findLastReleasesWithDependencies(pluginKey)) { - uninstallPlugin(key); - } - } - - private void uninstallPlugin(String pluginKey) { - PluginMetadata metadata = pluginByKeys.get(pluginKey); - if (metadata != null && !metadata.isCore()) { - try { - File masterFile = new File(fs.getUserPluginsDir(), metadata.getFile().getName()); - moveFileToDirectory(masterFile, fs.getTrashPluginsDir(), true); - } catch (IOException e) { - throw new IllegalStateException("Fail to uninstall plugin: " + pluginKey, e); - } - } - } - - public List<String> getUninstalledPluginFilenames() { - if (!fs.getTrashPluginsDir().exists()) { - return Collections.emptyList(); - } - - return newArrayList(transform(listJarFiles(fs.getTrashPluginsDir()), FileToName.INSTANCE)); - } - - /** - * @return the list of plugins to be uninstalled as {@link DefaultPluginMetadata} instances - */ - public Collection<DefaultPluginMetadata> getUninstalledPlugins() { - if (!fs.getTrashPluginsDir().exists()) { - return Collections.emptyList(); - } - - return newArrayList(transform(listJarFiles(fs.getTrashPluginsDir()), installer.fileToPlugin())); - } - - public void cancelUninstalls() { - if (fs.getTrashPluginsDir().exists()) { - for (File file : listJarFiles(fs.getTrashPluginsDir())) { - try { - moveFileToDirectory(file, fs.getUserPluginsDir(), false); - } catch (IOException e) { - throw new IllegalStateException("Fail to cancel plugin uninstalls", e); - } - } - } - } - - private void deployPlugins() { - for (PluginMetadata metadata : pluginByKeys.values()) { - deploy((DefaultPluginMetadata) metadata); - } - } - - private void deploy(DefaultPluginMetadata plugin) { - LOG.info("Deploy plugin {}", SLASH_JOINER.join(plugin.getName(), plugin.getVersion(), plugin.getImplementationBuild())); - - if (!plugin.isCompatibleWith(server.getVersion())) { - throw MessageException.of(format( - "Plugin %s needs a more recent version of SonarQube than %s. At least %s is expected", - plugin.getKey(), server.getVersion(), plugin.getSonarVersion())); - } - - try { - File pluginDeployDir = new File(fs.getDeployedPluginsDir(), plugin.getKey()); - forceMkdir(pluginDeployDir); - cleanDirectory(pluginDeployDir); - - installer.installToDir(plugin, pluginDeployDir); - } catch (IOException e) { - throw new IllegalStateException("Fail to deploy the plugin " + plugin, e); - } - } - - public Collection<PluginMetadata> getMetadata() { - return pluginByKeys.values(); - } - - public PluginMetadata getMetadata(String pluginKey) { - return pluginByKeys.get(pluginKey); - } - - private PluginReferential getPluginReferential() { - return PluginReferentialMetadataConverter.getInstalledPluginReferential(getMetadata()); - } - - private static Collection<File> listJarFiles(File pluginDir) { - return listFiles(pluginDir, new String[] {FILE_EXTENSION_JAR}, false); - } - - private enum FileToName implements Function<File, String> { - INSTANCE; - - @Override - public String apply(@Nonnull File file) { - return file.getName(); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java index 16028617966..cb9db1f4042 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java @@ -19,76 +19,361 @@ */ package org.sonar.server.plugins; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Ordering; +import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; -import org.sonar.api.utils.log.Loggers; import org.sonar.api.Plugin; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; -import org.sonar.core.plugins.PluginClassloaders; +import org.sonar.api.platform.Server; +import org.sonar.api.platform.ServerUpgradeStatus; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginLoader; +import org.sonar.core.platform.PluginRepository; +import org.sonar.server.platform.DefaultServerFileSystem; + +import javax.annotation.Nonnull; -import javax.annotation.CheckForNull; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; + +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Lists.newArrayList; +import static java.lang.String.format; +import static org.apache.commons.io.FileUtils.copyFile; +import static org.apache.commons.io.FileUtils.deleteQuietly; +import static org.apache.commons.io.FileUtils.moveFile; +import static org.apache.commons.io.FileUtils.moveFileToDirectory; /** - * This class loads JAR files and initializes classloaders of plugins + * Manages installation and loading of plugins: + * <ul> + * <li>installation of bundled plugins on first server startup</li> + * <li>installation of new plugins (effective after server startup)</li> + * <li>un-installation of plugins (effective after server startup)</li> + * <li>cancel pending installations/un-installations</li> + * <li>load plugin bytecode</li> + * </ul> */ public class ServerPluginRepository implements PluginRepository, Startable { - private final ServerPluginJarsInstaller jarsInstaller; - private final PluginClassloaders classloaders; - private Map<String, Plugin> pluginsByKey; + private static final Logger LOG = Loggers.get(ServerPluginRepository.class); + private static final String FILE_EXTENSION_JAR = "jar"; + private static final Set<String> DEFAULT_BLACKLISTED_PLUGINS = ImmutableSet.of("scmactivity", "issuesreport"); + private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls(); + + private final Server server; + private final DefaultServerFileSystem fs; + private final ServerUpgradeStatus upgradeStatus; + private final PluginLoader loader; + private Set<String> blacklistedPluginKeys = DEFAULT_BLACKLISTED_PLUGINS; - public ServerPluginRepository(ServerPluginJarsInstaller jarsInstaller) { - this.classloaders = new PluginClassloaders(getClass().getClassLoader()); - this.jarsInstaller = jarsInstaller; + // following fields are available after startup + private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>(); + private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>(); + + public ServerPluginRepository(Server server, ServerUpgradeStatus upgradeStatus, + DefaultServerFileSystem fs, PluginLoader loader) { + this.server = server; + this.upgradeStatus = upgradeStatus; + this.fs = fs; + this.loader = loader; + } + + @VisibleForTesting + void setBlacklistedPluginKeys(Set<String> keys) { + this.blacklistedPluginKeys = keys; } @Override public void start() { - jarsInstaller.install(); - Collection<PluginMetadata> metadata = jarsInstaller.getMetadata(); - pluginsByKey = classloaders.init(metadata); + loadPreInstalledPlugins(); + copyBundledPlugins(); + moveDownloadedPlugins(); + loadCorePlugins(); + unloadIncompatiblePlugins(); + logInstalledPlugins(); + loadInstances(); } @Override public void stop() { - classloaders.clean(); + // close classloaders + loader.unload(pluginInstancesByKeys.values()); + + pluginInstancesByKeys.clear(); + pluginInfosByKeys.clear(); } - @Override - public Plugin getPlugin(String key) { - return pluginsByKey.get(key); + /** + * Load the plugins that are located in extensions/plugins. Blacklisted plugins are + * deleted. + */ + private void loadPreInstalledPlugins() { + for (File file : listJarFiles(fs.getInstalledPluginsDir())) { + PluginInfo info = PluginInfo.create(file); + registerPluginInfo(info); + } + } + + /** + * Move the plugins recently downloaded to extensions/plugins. + */ + private void moveDownloadedPlugins() { + if (fs.getDownloadedPluginsDir().exists()) { + for (File sourceFile : listJarFiles(fs.getDownloadedPluginsDir())) { + overrideAndRegisterPlugin(sourceFile, true); + } + } + } + + /** + * Copies the plugins bundled with SonarQube distribution to directory extensions/plugins. + * Does nothing if not a fresh installation. + */ + private void copyBundledPlugins() { + if (upgradeStatus.isFreshInstall()) { + for (File sourceFile : listJarFiles(fs.getBundledPluginsDir())) { + PluginInfo info = PluginInfo.create(sourceFile); + // lib/bundled-plugins should be copied only if the plugin is not already + // available in extensions/plugins + if (!pluginInfosByKeys.containsKey(info.getKey())) { + overrideAndRegisterPlugin(sourceFile, false); + } + } + } + } + + private void registerPluginInfo(PluginInfo info) { + if (blacklistedPluginKeys.contains(info.getKey())) { + LOG.warn("Plugin {} [{}] is blacklisted and is being uninstalled.", info.getName(), info.getKey()); + deleteQuietly(info.getFile()); + return; + } + PluginInfo existing = pluginInfosByKeys.put(info.getKey(), info); + if (existing != null) { + throw MessageException.of(format("Found two files for the same plugin [%s]: %s and %s", + info.getKey(), info.getFile().getName(), existing.getFile().getName())); + } + + } + + /** + * Move or copy plugin to directory extensions/plugins. If a version of this plugin + * already exists then it's deleted. + */ + private void overrideAndRegisterPlugin(File sourceFile, boolean deleteSource) { + File destDir = fs.getInstalledPluginsDir(); + File destFile = new File(destDir, sourceFile.getName()); + if (destFile.exists()) { + // plugin with same filename already installed + deleteQuietly(destFile); + } + + try { + if (deleteSource) { + moveFile(sourceFile, destFile); + } else { + copyFile(sourceFile, destFile, true); + } + } catch (IOException e) { + LOG.error(format("Fail to move or copy plugin: %s to %s", + sourceFile.getAbsolutePath(), destFile.getAbsolutePath()), e); + } + + PluginInfo info = PluginInfo.create(destFile); + PluginInfo existing = pluginInfosByKeys.put(info.getKey(), info); + if (existing != null) { + if (!existing.getFile().getName().equals(destFile.getName())) { + deleteQuietly(existing.getFile()); + } + LOG.info("Plugin {} [{}] updated to version {}", info.getName(), info.getKey(), info.getVersion()); + } else { + LOG.info("Plugin {} [{}] installed", info.getName(), info.getKey()); + } + } + + private void loadCorePlugins() { + for (File file : listJarFiles(fs.getCorePluginsDir())) { + PluginInfo info = PluginInfo.create(file).setCore(true); + registerPluginInfo(info); + } + } + + /** + * Removes the plugins that are not compatible with current environment. In some cases + * plugin files can be deleted. + */ + private void unloadIncompatiblePlugins() { + // loop as long as the previous loop ignored some plugins. That allows to support dependencies + // on many levels, for example D extends C, which extends B, which requires A. If A is not installed, + // then B, C and D must be ignored. That's not possible to achieve this algorithm with a single + // iteration over plugins. + List<String> removedKeys = new ArrayList<>(); + do { + removedKeys.clear(); + for (PluginInfo plugin : pluginInfosByKeys.values()) { + if (!Strings.isNullOrEmpty(plugin.getBasePlugin()) && !pluginInfosByKeys.containsKey(plugin.getBasePlugin())) { + // this plugin extends a plugin that is not installed + LOG.warn("Plugin {} [{}] is ignored because its base plugin [{}] is not installed", plugin.getName(), plugin.getKey(), plugin.getBasePlugin()); + removedKeys.add(plugin.getKey()); + } + + if (!plugin.isCompatibleWith(server.getVersion())) { + LOG.warn("Plugin {} [{}] is ignored because it requires at least SonarQube {}", plugin.getName(), plugin.getKey(), plugin.getMinimalSqVersion()); + removedKeys.add(plugin.getKey()); + } + + for (PluginInfo.RequiredPlugin requiredPlugin : plugin.getRequiredPlugins()) { + PluginInfo available = pluginInfosByKeys.get(requiredPlugin.getKey()); + if (available == null) { + // this plugin requires a plugin that is not installed + LOG.warn("Plugin {} [{}] is ignored because the required plugin [{}] is not installed", plugin.getName(), plugin.getKey(), requiredPlugin.getKey()); + removedKeys.add(plugin.getKey()); + } else if (requiredPlugin.getMinimalVersion().compareToIgnoreQualifier(available.getVersion()) > 0) { + LOG.warn("Plugin {} [{}] is ignored because the version {}Â of required plugin [{}] is not supported", plugin.getName(), plugin.getKey(), + requiredPlugin.getKey(), requiredPlugin.getMinimalVersion()); + removedKeys.add(plugin.getKey()); + } + } + } + for (String removedKey : removedKeys) { + pluginInfosByKeys.remove(removedKey); + } + } while (!removedKeys.isEmpty()); + } + + private void logInstalledPlugins() { + List<PluginInfo> orderedPlugins = Ordering.natural().sortedCopy(pluginInfosByKeys.values()); + for (PluginInfo plugin : orderedPlugins) { + LOG.info("Plugin: {}", SLASH_JOINER.join(plugin.getName(), plugin.getVersion(), plugin.getImplementationBuild())); + } } - @CheckForNull - public ClassLoader getClassLoader(String pluginKey) { - return classloaders.get(pluginKey); + private void loadInstances() { + pluginInstancesByKeys.clear(); + pluginInstancesByKeys.putAll(loader.load(pluginInfosByKeys)); } - @CheckForNull - public Class getClass(String pluginKey, String classname) { - Class clazz = null; - ClassLoader classloader = getClassLoader(pluginKey); - if (classloader != null) { + /** + * Uninstall a plugin and its dependents (the plugins that require the plugin to be uninstalled) + */ + public void uninstall(String pluginKey) { + for (PluginInfo otherPlugin : pluginInfosByKeys.values()) { + if (!otherPlugin.getKey().equals(pluginKey)) { + for (PluginInfo.RequiredPlugin requiredPlugin : otherPlugin.getRequiredPlugins()) { + if (requiredPlugin.getKey().equals(pluginKey)) { + uninstall(otherPlugin.getKey()); + } + } + } + } + + PluginInfo info = pluginInfosByKeys.get(pluginKey); + if (!info.isCore()) { try { - clazz = classloader.loadClass(classname); + // we don't reuse info.getFile() just to be sure that file is located in from extensions/plugins + File masterFile = new File(fs.getInstalledPluginsDir(), info.getFile().getName()); + moveFileToDirectory(masterFile, uninstalledPluginsDir(), true); + } catch (IOException e) { + throw new IllegalStateException("Fail to uninstall plugin [" + pluginKey + "]", e); + } + } + } + + public List<String> getUninstalledPluginFilenames() { + return newArrayList(transform(listJarFiles(uninstalledPluginsDir()), FileToName.INSTANCE)); + } - } catch (ClassNotFoundException e) { - Loggers.get(getClass()).warn("Class not found in plugin " + pluginKey + ": " + classname, e); + /** + * @return the list of plugins to be uninstalled as {@link PluginInfo} instances + */ + public Collection<PluginInfo> getUninstalledPlugins() { + return newArrayList(transform(listJarFiles(uninstalledPluginsDir()), PluginInfo.JarToPluginInfo.INSTANCE)); + } + + public void cancelUninstalls() { + for (File file : listJarFiles(uninstalledPluginsDir())) { + try { + moveFileToDirectory(file, fs.getInstalledPluginsDir(), false); + } catch (IOException e) { + throw new IllegalStateException("Fail to cancel plugin uninstalls", e); } } - return clazz; + } + + public Map<String, PluginInfo> getPluginInfosByKeys() { + return pluginInfosByKeys; + } + + @Override + public Collection<PluginInfo> getPluginInfos() { + return pluginInfosByKeys.values(); + } + + @Override + public PluginInfo getPluginInfo(String key) { + PluginInfo info = pluginInfosByKeys.get(key); + if (info == null) { + throw new IllegalArgumentException(String.format("Plugin [%s] does not exist", key)); + } + return info; } @Override - public Collection<PluginMetadata> getMetadata() { - return jarsInstaller.getMetadata(); + public Plugin getPluginInstance(String key) { + Plugin plugin = pluginInstancesByKeys.get(key); + if (plugin == null) { + throw new IllegalArgumentException(String.format("Plugin [%s] does not exist", key)); + } + return plugin; } @Override - public PluginMetadata getMetadata(String pluginKey) { - return jarsInstaller.getMetadata(pluginKey); + public boolean hasPlugin(String key) { + return pluginInfosByKeys.containsKey(key); } + private enum FileToName implements Function<File, String> { + INSTANCE; + + @Override + public String apply(@Nonnull File file) { + return file.getName(); + } + + } + + /** + * @return existing trash dir + */ + private File uninstalledPluginsDir() { + File dir = new File(fs.getTempDir(), "uninstalled-plugins"); + try { + FileUtils.forceMkdir(dir); + return dir; + } catch (IOException e) { + throw new IllegalStateException("Fail to create temp directory: " + dir.getAbsolutePath(), e); + } + } + + private static Collection<File> listJarFiles(File dir) { + if (dir.exists()) { + return FileUtils.listFiles(dir, new String[] {FILE_EXTENSION_JAR}, false); + } + return Collections.emptyList(); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginUnzipper.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginUnzipper.java new file mode 100644 index 00000000000..59bc5a850fa --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginUnzipper.java @@ -0,0 +1,65 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.plugins; + +import org.apache.commons.io.FileUtils; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.ZipUtils; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginUnzipper; +import org.sonar.core.platform.UnzippedPlugin; +import org.sonar.server.platform.DefaultServerFileSystem; + +import java.io.File; + +import static org.apache.commons.io.FileUtils.cleanDirectory; +import static org.apache.commons.io.FileUtils.forceMkdir; + +public class ServerPluginUnzipper extends PluginUnzipper implements ServerComponent { + + private final DefaultServerFileSystem fs; + + public ServerPluginUnzipper(DefaultServerFileSystem fs) { + this.fs = fs; + } + + /** + * JAR files of directory extensions/plugins can be moved when server is up and plugins are uninstalled. + * For this reason these files must not be locked by classloaders. They are copied to the directory + * web/deploy/plugins in order to be loaded by {@link org.sonar.core.platform.PluginLoader}. + */ + @Override + public UnzippedPlugin unzip(PluginInfo pluginInfo) { + File toDir = new File(fs.getDeployedPluginsDir(), pluginInfo.getKey()); + try { + forceMkdir(toDir); + cleanDirectory(toDir); + + File jarSource = pluginInfo.getFile(); + File jarTarget = new File(toDir, jarSource.getName()); + FileUtils.copyFile(jarSource, jarTarget); + ZipUtils.unzip(jarSource, toDir, newLibFilter()); + return UnzippedPlugin.createFromUnzippedDir(pluginInfo.getKey(), jarTarget, toDir); + } catch (Exception e) { + throw new IllegalStateException(String.format( + "Fail to unzip plugin [%s] %s to %s", pluginInfo.getKey(), pluginInfo.getFile().getAbsolutePath(), toDir.getAbsolutePath()), e); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java index 8d24a1ae5f0..9a7ab9c3690 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java @@ -22,8 +22,10 @@ package org.sonar.server.plugins; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.sonar.api.Plugin; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.core.platform.PluginRepository; import org.sonar.server.platform.Platform; import javax.servlet.ServletException; @@ -45,29 +47,25 @@ public class StaticResourcesServlet extends HttpServlet { String pluginKey = getPluginKey(request); String resource = getResourcePath(request); - ServerPluginRepository pluginRepository = Platform.getInstance().getContainer().getComponentByType(ServerPluginRepository.class); - ClassLoader classLoader = pluginRepository.getClassLoader(pluginKey); - if (classLoader == null) { - LOG.error("Plugin not found: " + pluginKey); + PluginRepository pluginRepository = Platform.getInstance().getContainer().getComponentByType(PluginRepository.class); + if (!pluginRepository.hasPlugin(pluginKey)) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } InputStream in = null; OutputStream out = null; try { - in = classLoader.getResourceAsStream(resource); + in = pluginRepository.getPluginInstance(pluginKey).getClass().getClassLoader().getResourceAsStream(resource); if (in != null) { // mime type must be set before writing response body completeContentType(response, resource); out = response.getOutputStream(); IOUtils.copy(in, out); - } else { - LOG.error("Unable to find resource '" + resource + "' in plugin '" + pluginKey + "'"); response.sendError(HttpServletResponse.SC_NOT_FOUND); } } catch (Exception e) { - LOG.error("Unable to load static resource '" + resource + "' from plugin '" + pluginKey + "'", e); + LOG.error(String.format("Unable to load resource [%s] from plugin [%s]", resource, pluginKey), e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } finally { IOUtils.closeQuietly(in); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java index 93124251589..3899e50985d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java @@ -24,17 +24,17 @@ import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.core.permission.GlobalPermissions; import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.user.UserSession; public class CancelAllPluginsWsAction implements PluginsWsAction { private final PluginDownloader pluginDownloader; - private final ServerPluginJarsInstaller pluginJarsInstaller; + private final ServerPluginRepository pluginRepository; - public CancelAllPluginsWsAction(PluginDownloader pluginDownloader, ServerPluginJarsInstaller pluginJarsInstaller) { + public CancelAllPluginsWsAction(PluginDownloader pluginDownloader, ServerPluginRepository pluginRepository) { this.pluginDownloader = pluginDownloader; - this.pluginJarsInstaller = pluginJarsInstaller; + this.pluginRepository = pluginRepository; } @Override @@ -52,7 +52,7 @@ public class CancelAllPluginsWsAction implements PluginsWsAction { UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); pluginDownloader.cancelDownloads(); - pluginJarsInstaller.cancelUninstalls(); + pluginRepository.cancelUninstalls(); response.noContent(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java index c84b730c96c..dc2f3ff4be7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java @@ -22,14 +22,15 @@ package org.sonar.server.plugins.ws; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSortedSet; import com.google.common.io.Resources; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.platform.PluginInfo; +import org.sonar.server.plugins.ServerPluginRepository; import javax.annotation.Nullable; + import java.util.Collection; import java.util.SortedSet; @@ -42,10 +43,10 @@ import static org.sonar.server.plugins.ws.PluginWSCommons.NAME_KEY_PLUGIN_METADA public class InstalledPluginsWsAction implements PluginsWsAction { private static final String ARRAY_PLUGINS = "plugins"; - private final PluginRepository pluginRepository; + private final ServerPluginRepository pluginRepository; private final PluginWSCommons pluginWSCommons; - public InstalledPluginsWsAction(PluginRepository pluginRepository, + public InstalledPluginsWsAction(ServerPluginRepository pluginRepository, PluginWSCommons pluginWSCommons) { this.pluginRepository = pluginRepository; this.pluginWSCommons = pluginWSCommons; @@ -62,39 +63,39 @@ public class InstalledPluginsWsAction implements PluginsWsAction { @Override public void handle(Request request, Response response) throws Exception { - Collection<PluginMetadata> pluginMetadatas = retrieveAndSortPluginMetadata(); + Collection<PluginInfo> infos = retrieveAndSortPluginMetadata(); JsonWriter jsonWriter = response.newJsonWriter(); jsonWriter.setSerializeEmptys(false); jsonWriter.beginObject(); - writeMetadataList(jsonWriter, pluginMetadatas); + writeMetadataList(jsonWriter, infos); jsonWriter.endObject(); jsonWriter.close(); } - private SortedSet<PluginMetadata> retrieveAndSortPluginMetadata() { + private SortedSet<PluginInfo> retrieveAndSortPluginMetadata() { return ImmutableSortedSet.copyOf( NAME_KEY_PLUGIN_METADATA_COMPARATOR, - filter(pluginRepository.getMetadata(), NotCorePluginsPredicate.INSTANCE) + filter(pluginRepository.getPluginInfos(), NotCorePluginsPredicate.INSTANCE) ); } - private void writeMetadataList(JsonWriter jsonWriter, Collection<PluginMetadata> pluginMetadatas) { + private void writeMetadataList(JsonWriter jsonWriter, Collection<PluginInfo> pluginMetadatas) { jsonWriter.name(ARRAY_PLUGINS); jsonWriter.beginArray(); - for (PluginMetadata pluginMetadata : pluginMetadatas) { + for (PluginInfo pluginMetadata : pluginMetadatas) { pluginWSCommons.writePluginMetadata(jsonWriter, pluginMetadata); } jsonWriter.endArray(); } - private enum NotCorePluginsPredicate implements Predicate<PluginMetadata> { + private enum NotCorePluginsPredicate implements Predicate<PluginInfo> { INSTANCE; @Override - public boolean apply(@Nullable PluginMetadata input) { + public boolean apply(@Nullable PluginInfo input) { return input != null && !input.isCore(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java index 8c55caeaf68..29eb3cc9b53 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java @@ -19,14 +19,13 @@ */ package org.sonar.server.plugins.ws; -import org.sonar.api.platform.PluginMetadata; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import java.util.Collection; @@ -43,24 +42,24 @@ public class PendingPluginsWsAction implements PluginsWsAction { private static final String ARRAY_REMOVING = "removing"; private final PluginDownloader pluginDownloader; - private final ServerPluginJarsInstaller serverPluginJarsInstaller; + private final ServerPluginRepository installer; private final PluginWSCommons pluginWSCommons; public PendingPluginsWsAction(PluginDownloader pluginDownloader, - ServerPluginJarsInstaller serverPluginJarsInstaller, - PluginWSCommons pluginWSCommons) { + ServerPluginRepository installer, + PluginWSCommons pluginWSCommons) { this.pluginDownloader = pluginDownloader; - this.serverPluginJarsInstaller = serverPluginJarsInstaller; + this.installer = installer; this.pluginWSCommons = pluginWSCommons; } @Override public void define(WebService.NewController controller) { controller.createAction("pending") - .setDescription("Get the list of plugins which will either be installed or removed at the next startup of the SonarQube instance, sorted by plugin name") - .setSince("5.2") - .setHandler(this) - .setResponseExample(getResource(this.getClass(), "example-pending_plugins.json")); + .setDescription("Get the list of plugins which will either be installed or removed at the next startup of the SonarQube instance, sorted by plugin name") + .setSince("5.2") + .setHandler(this) + .setResponseExample(getResource(this.getClass(), "example-pending_plugins.json")); } @Override @@ -80,8 +79,8 @@ public class PendingPluginsWsAction implements PluginsWsAction { private void writeInstalling(JsonWriter jsonWriter) { jsonWriter.name(ARRAY_INSTALLING); jsonWriter.beginArray(); - Collection<DefaultPluginMetadata> plugins = pluginDownloader.getDownloadedPlugins(); - for (PluginMetadata pluginMetadata : copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins)) { + Collection<PluginInfo> plugins = pluginDownloader.getDownloadedPlugins(); + for (PluginInfo pluginMetadata : copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins)) { pluginWSCommons.writePluginMetadata(jsonWriter, pluginMetadata); } jsonWriter.endArray(); @@ -90,8 +89,8 @@ public class PendingPluginsWsAction implements PluginsWsAction { private void writeRemoving(JsonWriter jsonWriter) { jsonWriter.name(ARRAY_REMOVING); jsonWriter.beginArray(); - Collection<DefaultPluginMetadata> plugins = serverPluginJarsInstaller.getUninstalledPlugins(); - for (PluginMetadata pluginMetadata : copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins)) { + Collection<PluginInfo> plugins = installer.getUninstalledPlugins(); + for (PluginInfo pluginMetadata : copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins)) { pluginWSCommons.writePluginMetadata(jsonWriter, pluginMetadata); } jsonWriter.endArray(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java index 08cc27555b4..c2f8a8b6b63 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java @@ -24,13 +24,14 @@ import com.google.common.base.Function; import com.google.common.collect.Ordering; import java.util.Comparator; import javax.annotation.Nonnull; -import org.sonar.api.platform.PluginMetadata; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.platform.PluginInfo; import org.sonar.updatecenter.common.Artifact; import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.PluginUpdate; import org.sonar.updatecenter.common.Release; import org.sonar.updatecenter.common.UpdateCenter; +import org.sonar.updatecenter.common.Version; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; @@ -59,7 +60,7 @@ public class PluginWSCommons { static final String PROPERTY_IMPLEMENTATION_BUILD = "implementationBuild"; static final String PROPERTY_CHANGE_LOG_URL = "changeLogUrl"; - public static final Ordering<PluginMetadata> NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural() + public static final Ordering<PluginInfo> NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural() .onResultOf(PluginMetadataToName.INSTANCE) .compound(Ordering.natural().onResultOf(PluginMetadataToKey.INSTANCE)); public static final Comparator<Plugin> NAME_KEY_PLUGIN_ORDERING = Ordering.from(CASE_INSENSITIVE_ORDER) @@ -70,23 +71,26 @@ public class PluginWSCommons { public static final Comparator<PluginUpdate> NAME_KEY_PLUGIN_UPDATE_ORDERING = Ordering.from(NAME_KEY_PLUGIN_ORDERING) .onResultOf(PluginUpdateToPlugin.INSTANCE); - public void writePluginMetadata(JsonWriter jsonWriter, PluginMetadata pluginMetadata) { + public void writePluginMetadata(JsonWriter jsonWriter, PluginInfo info) { jsonWriter.beginObject(); - writeMetadata(jsonWriter, pluginMetadata); + writeMetadata(jsonWriter, info); jsonWriter.endObject(); } - public void writeMetadata(JsonWriter jsonWriter, PluginMetadata pluginMetadata) { + public void writeMetadata(JsonWriter jsonWriter, PluginInfo pluginMetadata) { jsonWriter.prop(PROPERTY_KEY, pluginMetadata.getKey()); jsonWriter.prop(PROPERTY_NAME, pluginMetadata.getName()); jsonWriter.prop(PROPERTY_DESCRIPTION, pluginMetadata.getDescription()); - jsonWriter.prop(PROPERTY_VERSION, pluginMetadata.getVersion()); + Version version = pluginMetadata.getVersion(); + if (version != null) { + jsonWriter.prop(PROPERTY_VERSION, version.getName()); + } jsonWriter.prop(PROPERTY_LICENSE, pluginMetadata.getLicense()); - jsonWriter.prop(PROPERTY_ORGANIZATION_NAME, pluginMetadata.getOrganization()); + jsonWriter.prop(PROPERTY_ORGANIZATION_NAME, pluginMetadata.getOrganizationName()); jsonWriter.prop(PROPERTY_ORGANIZATION_URL, pluginMetadata.getOrganizationUrl()); - jsonWriter.prop(PROPERTY_HOMEPAGE, pluginMetadata.getHomepage()); + jsonWriter.prop(PROPERTY_HOMEPAGE, pluginMetadata.getHomepageUrl()); jsonWriter.prop(PROPERTY_ISSUE_TRACKER_URL, pluginMetadata.getIssueTrackerUrl()); jsonWriter.prop(PROPERTY_IMPLEMENTATION_BUILD, pluginMetadata.getImplementationBuild()); } @@ -221,20 +225,20 @@ public class PluginWSCommons { } } - private enum PluginMetadataToName implements Function<PluginMetadata, String> { + private enum PluginMetadataToName implements Function<PluginInfo, String> { INSTANCE; @Override - public String apply(@Nonnull PluginMetadata input) { + public String apply(@Nonnull PluginInfo input) { return input.getName(); } } - private enum PluginMetadataToKey implements Function<PluginMetadata, String> { + private enum PluginMetadataToKey implements Function<PluginInfo, String> { INSTANCE; @Override - public String apply(@Nonnull PluginMetadata input) { + public String apply(@Nonnull PluginInfo input) { return input.getKey(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java index bb2e9e1c43a..98399499acd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java @@ -19,18 +19,13 @@ */ package org.sonar.server.plugins.ws; -import com.google.common.base.Predicate; -import javax.annotation.Nullable; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.user.UserSession; -import static com.google.common.collect.Iterables.find; import static java.lang.String.format; /** @@ -39,12 +34,10 @@ import static java.lang.String.format; public class UninstallPluginsWsAction implements PluginsWsAction { private static final String PARAM_KEY = "key"; - private final PluginRepository pluginRepository; - private final ServerPluginJarsInstaller pluginJarsInstaller; + private final ServerPluginRepository pluginRepository; - public UninstallPluginsWsAction(PluginRepository pluginRepository, ServerPluginJarsInstaller pluginJarsInstaller) { + public UninstallPluginsWsAction(ServerPluginRepository pluginRepository) { this.pluginRepository = pluginRepository; - this.pluginJarsInstaller = pluginJarsInstaller; } @Override @@ -66,27 +59,14 @@ public class UninstallPluginsWsAction implements PluginsWsAction { UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); String key = request.mandatoryParam(PARAM_KEY); ensurePluginIsInstalled(key); - pluginJarsInstaller.uninstall(key); + pluginRepository.uninstall(key); response.noContent(); } + // FIXME should be moved to {@link ServerPluginRepository#uninstall(String)} private void ensurePluginIsInstalled(String key) { - if (find(pluginRepository.getMetadata(), new PluginKeyPredicate(key), null) == null) { - throw new IllegalArgumentException( - format("No plugin with key '%s' or plugin '%s' is not installed", key, key)); - } - } - - private static class PluginKeyPredicate implements Predicate<PluginMetadata> { - private final String key; - - public PluginKeyPredicate(String key) { - this.key = key; - } - - @Override - public boolean apply(@Nullable PluginMetadata input) { - return input != null && key.equals(input.getKey()); + if (!pluginRepository.hasPlugin(key)) { + throw new IllegalArgumentException(format("Plugin [%s] is not installed", key)); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java index c29cd525cc9..d20d2c4e825 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java @@ -29,7 +29,7 @@ import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.update.UpdateRequest; import org.sonar.api.ServerComponent; -import org.sonar.api.platform.ComponentContainer; +import org.sonar.core.platform.ComponentContainer; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.cluster.WorkQueue; diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java b/server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java index d79adb553c2..4fe91fb829f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java @@ -22,9 +22,9 @@ package org.sonar.server.startup; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.CharUtils; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.api.ServerComponent; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import org.sonar.core.plugins.RemotePlugin; import org.sonar.server.platform.DefaultServerFileSystem; @@ -33,10 +33,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; -/** - * @since 2.11 - */ -public final class GeneratePluginIndex { +public final class GeneratePluginIndex implements ServerComponent { private DefaultServerFileSystem fileSystem; private PluginRepository repository; @@ -54,8 +51,8 @@ public final class GeneratePluginIndex { FileUtils.forceMkdir(indexFile.getParentFile()); Writer writer = new FileWriter(indexFile, false); try { - for (PluginMetadata metadata : repository.getMetadata()) { - writer.append(RemotePlugin.create((DefaultPluginMetadata) metadata).marshal()); + for (PluginInfo pluginInfo : repository.getPluginInfos()) { + writer.append(RemotePlugin.create(pluginInfo).marshal()); writer.append(CharUtils.LF); } writer.flush(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 4c2ed9deef1..71a39f1e1f9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -27,13 +27,12 @@ import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + +import org.sonar.api.Plugin; import org.sonar.api.config.License; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; -import org.sonar.api.platform.ComponentContainer; import org.sonar.api.platform.NewUserHandler; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.resources.Language; import org.sonar.api.resources.ResourceType; import org.sonar.api.resources.ResourceTypes; @@ -44,6 +43,9 @@ import org.sonar.api.web.RubyRailsWebservice; import org.sonar.api.web.Widget; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.core.timemachine.Periods; import org.sonar.process.ProcessProperties; @@ -58,7 +60,6 @@ import org.sonar.server.platform.ServerSettings; import org.sonar.server.platform.SettingsChangeNotifier; import org.sonar.server.plugins.InstalledPluginReferentialFactory; import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.rule.RuleRepositories; @@ -149,15 +150,15 @@ public final class JRubyFacade { } public void uninstallPlugin(String pluginKey) { - get(ServerPluginJarsInstaller.class).uninstall(pluginKey); + get(ServerPluginRepository.class).uninstall(pluginKey); } public void cancelPluginUninstalls() { - get(ServerPluginJarsInstaller.class).cancelUninstalls(); + get(ServerPluginRepository.class).cancelUninstalls(); } public List<String> getPluginUninstalls() { - return get(ServerPluginJarsInstaller.class).getUninstalledPluginFilenames(); + return get(ServerPluginRepository.class).getUninstalledPluginFilenames(); } public UpdateCenter getUpdatePluginCenter(boolean forceReload) { @@ -173,12 +174,11 @@ public final class JRubyFacade { return get(PropertyDefinitions.class); } - public boolean hasPlugin(String key) { - return get(PluginRepository.class).getPlugin(key) != null; - } - - public Collection<PluginMetadata> getPluginsMetadata() { - return get(PluginRepository.class).getMetadata(); + /** + * Used for WS api/updatecenter/installed_plugins, to be replaced by api/plugins/installed. + */ + public Collection<PluginInfo> getPluginInfos() { + return get(PluginRepository.class).getPluginInfos(); } public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage, Object[] availableMeasures) { @@ -285,12 +285,13 @@ public final class JRubyFacade { } public Object getComponentByClassname(String pluginKey, String className) { - Object component = null; - Class<?> componentClass = get(ServerPluginRepository.class).getClass(pluginKey, className); - if (componentClass != null) { - component = get(componentClass); + Plugin plugin = get(PluginRepository.class).getPluginInstance(pluginKey); + try { + Class componentClass = plugin.getClass().getClassLoader().loadClass(className); + return get(componentClass); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(String.format("Class [%s] not found in plugin [%s]", className, pluginKey), e); } - return component; } private JRubyI18n getJRubyI18n() { @@ -391,7 +392,7 @@ public final class JRubyFacade { ComponentContainer container = Platform.getInstance().getContainer(); DatabaseMigration databaseMigration = container.getComponentByType(DatabaseMigration.class); if (databaseMigration.status() == DatabaseMigration.Status.RUNNING - || databaseMigration.status() == DatabaseMigration.Status.FAILED) { + || databaseMigration.status() == DatabaseMigration.Status.FAILED) { return false; } if (databaseMigration.status() == DatabaseMigration.Status.SUCCEEDED) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java index 942bb973c09..730584d829b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java @@ -28,8 +28,8 @@ import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.sonar.api.SonarPlugin; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import java.io.FileInputStream; import java.io.InputStream; @@ -54,17 +54,15 @@ public class DebtModelPluginRepositoryTest { @Test public void test_component_initialization() throws Exception { // we do have the "csharp-model.xml" file in src/test/resources - PluginMetadata csharpPluginMetadata = mock(PluginMetadata.class); - when(csharpPluginMetadata.getKey()).thenReturn("csharp"); + PluginInfo csharpPluginMetadata = new PluginInfo("csharp"); // but we don' have the "php-model.xml" one - PluginMetadata phpPluginMetadata = mock(PluginMetadata.class); - when(phpPluginMetadata.getKey()).thenReturn("php"); + PluginInfo phpPluginMetadata = new PluginInfo("php"); PluginRepository repository = mock(PluginRepository.class); - when(repository.getMetadata()).thenReturn(Lists.newArrayList(csharpPluginMetadata, phpPluginMetadata)); + when(repository.getPluginInfos()).thenReturn(Lists.newArrayList(csharpPluginMetadata, phpPluginMetadata)); FakePlugin fakePlugin = new FakePlugin(); - when(repository.getPlugin(anyString())).thenReturn(fakePlugin); + when(repository.getPluginInstance(anyString())).thenReturn(fakePlugin); modelFinder = new DebtModelPluginRepository(repository, TEST_XML_PREFIX_PATH); // when diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java index 5c49de4aef1..e8df89bb003 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java @@ -41,7 +41,7 @@ import org.elasticsearch.node.Node; import org.elasticsearch.node.NodeBuilder; import org.elasticsearch.search.SearchHit; import org.junit.rules.ExternalResource; -import org.sonar.api.platform.ComponentContainer; +import org.sonar.core.platform.ComponentContainer; import org.sonar.server.search.BaseDoc; import org.sonar.test.TestUtils; diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java index c06a7887022..57bf62c771a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java @@ -38,20 +38,6 @@ public class DefaultServerFileSystemTest { public TemporaryFolder temp = new TemporaryFolder(); @Test - public void find_plugins() throws Exception { - List<File> plugins = new DefaultServerFileSystem( - new File(Resources.getResource(PATH + "shouldFindPlugins").toURI()), temp.newFolder(), null).getUserPlugins(); - assertThat(plugins).hasSize(2); - } - - @Test - public void not_fail_if_no_plugins() throws Exception { - List<File> plugins = new DefaultServerFileSystem( - new File(Resources.getResource(PATH + "shouldNotFailIfNoPlugins").toURI()), temp.newFolder(), null).getUserPlugins(); - assertThat(plugins).isEmpty(); - } - - @Test public void find_checkstyle_extensions() throws Exception { ServerFileSystem fs = new DefaultServerFileSystem( new File(Resources.getResource(PATH + "shouldFindCheckstyleExtensions").toURI()), temp.newFolder(), null); diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java index a64486ca9d9..d110225210d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java @@ -23,9 +23,9 @@ import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.platform.ServerFileSystem; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import java.io.File; import java.net.URL; @@ -77,7 +77,7 @@ public class RailsAppsDeployerTest { when(fileSystem.getTempDir()).thenReturn(tempDir); PluginRepository pluginRepository = mock(PluginRepository.class); - when(pluginRepository.getMetadata()).thenReturn(Collections.<PluginMetadata>emptyList()); + when(pluginRepository.getPluginInfos()).thenReturn(Collections.<PluginInfo>emptyList()); new RailsAppsDeployer(fileSystem, pluginRepository).start(); File appDir = new File(tempDir, "ror"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java index ef082c716b2..9e70d80a699 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java @@ -21,22 +21,22 @@ package org.sonar.server.platform.monitoring; import org.junit.Test; -import org.sonar.api.Plugin; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; +import org.sonar.updatecenter.common.Version; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class PluginsMonitorTest { - PluginsMonitor sut = new PluginsMonitor(new FakePluginRepository());; + PluginRepository repo = mock(PluginRepository.class); + PluginsMonitor sut = new PluginsMonitor(repo); @Test public void name() { @@ -44,42 +44,28 @@ public class PluginsMonitorTest { } @Test - public void plugin_name_and_version() { + public void plugin_name_and_version() throws Exception { + when(repo.getPluginInfos()).thenReturn(Arrays.asList( + new PluginInfo("key-1") + .setName("plugin-1") + .setVersion(Version.create("1.1")), + new PluginInfo("key-2") + .setName("plugin-2") + .setVersion(Version.create("2.2")), + new PluginInfo("no-version") + .setName("No Version"))); + LinkedHashMap<String, Object> attributes = sut.attributes(); assertThat(attributes).containsKeys("key-1", "key-2"); assertThat((Map) attributes.get("key-1")) .containsEntry("Name", "plugin-1") .containsEntry("Version", "1.1"); - assertThat((Map)attributes.get("key-2")) + assertThat((Map) attributes.get("key-2")) .containsEntry("Name", "plugin-2") .containsEntry("Version", "2.2"); - } - - private static class FakePluginRepository implements PluginRepository { - - @Override - public Plugin getPlugin(String key) { - return null; - } - - @Override - public Collection<PluginMetadata> getMetadata() { - List<PluginMetadata> plugins = new ArrayList<>(); - plugins.add(DefaultPluginMetadata - .create("key-1") - .setName("plugin-1") - .setVersion("1.1")); - plugins.add(DefaultPluginMetadata - .create("key-2") - .setName("plugin-2") - .setVersion("2.2")); - return plugins; - } - - @Override - public PluginMetadata getMetadata(String pluginKey) { - return null; - } + assertThat((Map) attributes.get("no-version")) + .containsEntry("Name", "No Version") + .doesNotContainKey("Version"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java index 40e1ada4f35..58c0e12c449 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java @@ -20,9 +20,8 @@ package org.sonar.server.plugins; import org.junit.Test; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; @@ -33,23 +32,22 @@ public class InstalledPluginReferentialFactoryTest { @Test public void should_create_plugin_referential() { - PluginMetadata metadata = mock(DefaultPluginMetadata.class); - when(metadata.getKey()).thenReturn("foo"); + PluginInfo info = new PluginInfo("foo"); PluginRepository pluginRepository = mock(PluginRepository.class); - when(pluginRepository.getMetadata()).thenReturn(newArrayList(metadata)); - InstalledPluginReferentialFactory installedPluginReferentialFactory = new InstalledPluginReferentialFactory(pluginRepository); + when(pluginRepository.getPluginInfos()).thenReturn(newArrayList(info)); + InstalledPluginReferentialFactory factory = new InstalledPluginReferentialFactory(pluginRepository); - assertThat(installedPluginReferentialFactory.getInstalledPluginReferential()).isNull(); - installedPluginReferentialFactory.start(); - assertThat(installedPluginReferentialFactory.getInstalledPluginReferential()).isNotNull(); - assertThat(installedPluginReferentialFactory.getInstalledPluginReferential().getPlugins()).hasSize(1); + assertThat(factory.getInstalledPluginReferential()).isNull(); + factory.start(); + assertThat(factory.getInstalledPluginReferential()).isNotNull(); + assertThat(factory.getInstalledPluginReferential().getPlugins()).hasSize(1); } @Test(expected = RuntimeException.class) public void should_encapsulate_exception() { PluginRepository pluginRepository = mock(PluginRepository.class); - when(pluginRepository.getMetadata()).thenThrow(new IllegalArgumentException()); - InstalledPluginReferentialFactory installedPluginReferentialFactory = new InstalledPluginReferentialFactory(pluginRepository); - installedPluginReferentialFactory.start(); + when(pluginRepository.getPluginInfos()).thenThrow(new IllegalArgumentException()); + InstalledPluginReferentialFactory factory = new InstalledPluginReferentialFactory(pluginRepository); + factory.start(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java index 6c5f4412026..25b5b4978c4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java @@ -20,6 +20,7 @@ package org.sonar.server.plugins; import org.junit.Test; +import org.sonar.test.TestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -35,4 +36,9 @@ public class MimeTypesTest { assertThat(MimeTypes.getByFilename("static/sqale/sqale.css")).isEqualTo("text/css"); assertThat(MimeTypes.getByFilename("sqale.css")).isEqualTo("text/css"); } + + @Test + public void only_statics() throws Exception { + assertThat(TestUtils.hasOnlyPrivateConstructors(MimeTypes.class)).isTrue(); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java index bf460fffd2b..a5178f40383 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java @@ -29,11 +29,12 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.SonarException; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.server.platform.DefaultServerFileSystem; import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.Release; import org.sonar.updatecenter.common.UpdateCenter; +import org.sonar.updatecenter.common.Version; import java.io.File; import java.net.URI; @@ -66,7 +67,6 @@ public class PluginDownloaderTest { UpdateCenter updateCenter; HttpDownloader httpDownloader; PluginDownloader pluginDownloader; - ServerPluginJarInstaller installer; @Before public void before() throws Exception { @@ -88,8 +88,7 @@ public class PluginDownloaderTest { downloadDir = testFolder.newFolder("downloads"); when(defaultServerFileSystem.getDownloadedPluginsDir()).thenReturn(downloadDir); - installer = new ServerPluginJarInstaller(); - pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem, installer); + pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem); } @After @@ -155,7 +154,7 @@ public class PluginDownloaderTest { File downloadDir = testFolder.newFile(); when(defaultServerFileSystem.getDownloadedPluginsDir()).thenReturn(downloadDir); - pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem, installer); + pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem); try { pluginDownloader.start(); fail(); @@ -220,26 +219,18 @@ public class PluginDownloaderTest { pluginDownloader.start(); assertThat(pluginDownloader.getDownloadedPluginFilenames()).hasSize(0); - copyFileToDirectory(new File(resource("foo-plugin-1.0.jar")), downloadDir); + copyFileToDirectory(TestProjectUtils.jarOf("test-base-plugin"), downloadDir); assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(1); - DefaultPluginMetadata metadata = pluginDownloader.getDownloadedPlugins().iterator().next(); - assertThat(metadata.getKey()).isEqualTo("foo"); - assertThat(metadata.getName()).isEqualTo("Foo"); - assertThat(metadata.getVersion()).isEqualTo("1.0"); - assertThat(metadata.getOrganization()).isEqualTo("SonarSource"); - assertThat(metadata.getOrganizationUrl()).isEqualTo("http://www.sonarsource.org"); - assertThat(metadata.getLicense()).isEqualTo("LGPL 3"); - assertThat(metadata.getMainClass()).isEqualTo("foo.Main"); - } - - private URI resource(String fileName) throws URISyntaxException { - URL resource = getClass().getResource(getClass().getSimpleName() + "/" + fileName); - return resource.toURI(); + PluginInfo info = pluginDownloader.getDownloadedPlugins().iterator().next(); + assertThat(info.getKey()).isEqualTo("testbase"); + assertThat(info.getName()).isEqualTo("Base Plugin"); + assertThat(info.getVersion()).isEqualTo(Version.create("0.1-SNAPSHOT")); + assertThat(info.getMainClass()).isEqualTo("BasePlugin"); } @Test - public void getDownloadedPluginFilenames_reads_plugin_metadata_of_files_in_download_folder() throws Exception { + public void getDownloadedPluginFilenames_reads_plugin_info_of_files_in_download_folder() throws Exception { pluginDownloader.start(); assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(0); diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java index a7cc85c9694..c7891203caa 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java @@ -20,23 +20,19 @@ package org.sonar.server.plugins; import org.junit.Test; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.updatecenter.common.PluginReferential; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class PluginReferentialMetadataConverterTest { @Test - public void should_convert_metadata_to_plugin_referential() { - PluginMetadata metadata = mock(DefaultPluginMetadata.class); - when(metadata.getKey()).thenReturn("foo"); + public void should_convert_info_to_plugin_referential() { + PluginInfo info = new PluginInfo("foo"); - PluginReferential pluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(newArrayList(metadata)); + PluginReferential pluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(newArrayList(info)); assertThat(pluginReferential).isNotNull(); assertThat(pluginReferential.getPlugins()).hasSize(1); assertThat(pluginReferential.getPlugins().get(0).getKey()).isEqualTo("foo"); @@ -44,11 +40,9 @@ public class PluginReferentialMetadataConverterTest { @Test public void should_not_add_core_plugin() { - PluginMetadata metadata = mock(DefaultPluginMetadata.class); - when(metadata.getKey()).thenReturn("foo"); - when(metadata.isCore()).thenReturn(true); + PluginInfo info = new PluginInfo("foo").setCore(true); - PluginReferential pluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(newArrayList(metadata)); + PluginReferential pluginReferential = PluginReferentialMetadataConverter.getInstalledPluginReferential(newArrayList(info)); assertThat(pluginReferential).isNotNull(); assertThat(pluginReferential.getPlugins()).hasSize(0); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java deleted file mode 100644 index 8edde701e28..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.plugins; - -import com.google.common.io.Resources; -import org.apache.commons.io.FileUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.Server; -import org.sonar.api.platform.ServerUpgradeStatus; -import org.sonar.api.utils.MessageException; -import org.sonar.core.plugins.DefaultPluginMetadata; -import org.sonar.server.platform.DefaultServerFileSystem; - -import java.io.File; -import java.util.Collection; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ServerPluginJarsInstallerTest { - - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - DefaultServerFileSystem fileSystem; - File homeDir, pluginsDir, downloadsDir, bundledDir, trashDir, coreDir; - ServerPluginJarInstaller jarInstaller; - ServerPluginJarsInstaller jarsInstaller; - Server server = mock(Server.class); - ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class); - - @Before - public void before() throws Exception { - when(server.getVersion()).thenReturn("3.1"); - when(server.getDeployDir()).thenReturn(temp.newFolder("deploy")); - - homeDir = temp.newFolder("home"); - pluginsDir = new File(homeDir, "extensions/plugins"); - FileUtils.forceMkdir(pluginsDir); - downloadsDir = new File(homeDir, "extensions/downloads"); - trashDir = new File(homeDir, "extensions/trash"); - bundledDir = new File(homeDir, "lib/bundled-plugins"); - coreDir = new File(homeDir, "lib/core-plugins"); - FileUtils.forceMkdir(bundledDir); - - fileSystem = new DefaultServerFileSystem(homeDir, temp.newFolder(), server); - jarInstaller = new ServerPluginJarInstaller(); - jarsInstaller = new ServerPluginJarsInstaller(server, upgradeStatus, fileSystem, jarInstaller); - } - - private File jar(String name) throws Exception { - return new File(Resources.getResource(getClass(), "ServerPluginJarsInstallerTest/" + name).toURI()); - } - - @Test - public void copy_bundled_plugin_on_fresh_install() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(true); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), bundledDir); - - jarsInstaller.install(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(new File(pluginsDir, "foo-plugin-1.0.jar")).exists().isFile(); - PluginMetadata plugin = jarsInstaller.getMetadata("foo"); - assertThat(plugin.getName()).isEqualTo("Foo"); - assertThat(plugin.getDeployedFiles()).hasSize(1); - assertThat(plugin.isCore()).isFalse(); - assertThat(plugin.isUseChildFirstClassLoader()).isFalse(); - } - - @Test - public void do_not_copy_bundled_plugin_on_non_fresh_install() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), bundledDir); - - jarsInstaller.install(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false)).isEmpty(); - } - - @Test - public void do_not_copy_bundled_plugin_if_already_installed() throws Exception { - // fresh install but plugins are already packaged in extensions/plugins - when(upgradeStatus.isFreshInstall()).thenReturn(true); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), bundledDir); - FileUtils.copyFileToDirectory(jar("foo-plugin-2.0.jar"), pluginsDir); - FileUtils.copyFileToDirectory(jar("bar-plugin-1.0.jar"), pluginsDir); - - jarsInstaller.install(); - - // do not copy foo 1.0 - assertThat(FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false)).hasSize(2); - assertThat(new File(pluginsDir, "foo-plugin-2.0.jar")).exists().isFile(); - assertThat(new File(pluginsDir, "bar-plugin-1.0.jar")).exists().isFile(); - PluginMetadata plugin = jarsInstaller.getMetadata("foo"); - assertThat(plugin.getVersion()).isEqualTo("2.0"); - } - - @Test - public void deploy_installed_plugin() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - - jarsInstaller.install(); - - // check that the plugin is registered - assertThat(jarsInstaller.getMetadata()).hasSize(1); - PluginMetadata plugin = jarsInstaller.getMetadata("foo"); - assertThat(plugin.getName()).isEqualTo("Foo"); - assertThat(plugin.getDeployedFiles()).hasSize(1); - assertThat(plugin.isCore()).isFalse(); - assertThat(plugin.isUseChildFirstClassLoader()).isFalse(); - - // check that the file is still present in extensions/plugins - assertThat(FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false)).hasSize(1); - assertThat(new File(pluginsDir, "foo-plugin-1.0.jar")).exists().isFile(); - } - - @Test - public void ignore_non_plugin_jars() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("not-a-plugin.jar"), pluginsDir); - - jarsInstaller.install(); - - // nothing to install but keep the file - assertThat(jarsInstaller.getMetadata()).isEmpty(); - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(new File(pluginsDir, "not-a-plugin.jar")).exists().isFile(); - } - - @Test - public void fail_if_plugin_requires_greater_SQ_version() throws Exception { - exception.expect(MessageException.class); - exception.expectMessage("Plugin switchoffviolations needs a more recent version of SonarQube than 2.0. At least 2.5 is expected"); - - when(upgradeStatus.isFreshInstall()).thenReturn(false); - when(server.getVersion()).thenReturn("2.0"); - FileUtils.copyFileToDirectory(jar("require-sq-2.5.jar"), pluginsDir); - - jarsInstaller.install(); - } - - @Test - public void move_downloaded_plugins() throws Exception { - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), downloadsDir); - when(upgradeStatus.isFreshInstall()).thenReturn(false); - - jarsInstaller.install(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false)).hasSize(1); - assertThat(FileUtils.listFiles(downloadsDir, new String[] {"jar"}, false)).isEmpty(); - assertThat(new File(pluginsDir, "foo-plugin-1.0.jar")).exists().isFile(); - } - - @Test - public void downloaded_plugins_overrides_existing_plugin() throws Exception { - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - FileUtils.copyFileToDirectory(jar("foo-plugin-2.0.jar"), downloadsDir); - when(upgradeStatus.isFreshInstall()).thenReturn(false); - - jarsInstaller.install(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(FileUtils.listFiles(downloadsDir, new String[] {"jar"}, false)).isEmpty(); - assertThat(new File(pluginsDir, "foo-plugin-2.0.jar")).exists().isFile(); - } - - @Test - public void downloaded_plugins_overrides_existing_plugin_even_if_same_filename() throws Exception { - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir, true); - // foo-plugin-1.0.jar in extensions/downloads is in fact version 2.0. It's used to verify - // that it has correctly overridden extensions/plugins/foo-plugin-1.0.jar - FileUtils.copyFile(jar("foo-plugin-2.0.jar"), new File(downloadsDir, "foo-plugin-1.0.jar")); - when(upgradeStatus.isFreshInstall()).thenReturn(false); - - jarsInstaller.install(); - - PluginMetadata plugin = jarsInstaller.getMetadata("foo"); - assertThat(plugin).isNotNull(); - assertThat(plugin.getVersion()).isEqualTo("2.0"); - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(FileUtils.listFiles(downloadsDir, new String[] {"jar"}, false)).isEmpty(); - File installed = new File(pluginsDir, "foo-plugin-1.0.jar"); - assertThat(installed).exists().isFile(); - } - - @Test - public void delete_trash() throws Exception { - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), trashDir); - when(upgradeStatus.isFreshInstall()).thenReturn(false); - - jarsInstaller.install(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).isEmpty(); - assertThat(trashDir).doesNotExist(); - } - - @Test - public void fail_if_two_installed_plugins_with_same_key() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - FileUtils.copyFileToDirectory(jar("foo-plugin-2.0.jar"), pluginsDir); - - try { - jarsInstaller.install(); - fail(); - } catch (MessageException e) { - assertThat(e.getMessage()) - .contains("Found two files for the same plugin 'foo'") - .contains("foo-plugin-1.0.jar") - .contains("foo-plugin-2.0.jar"); - } - } - - @Test - public void uninstall_plugin() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - - jarsInstaller.install(); - jarsInstaller.uninstall("foo"); - - assertThat(FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false)).isEmpty(); - assertThat(FileUtils.listFiles(trashDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(jarsInstaller.getUninstalledPluginFilenames()).containsOnly("foo-plugin-1.0.jar"); - } - - @Test - public void pending_removals_reads_metadata() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - - jarsInstaller.install(); - jarsInstaller.uninstall("foo"); - - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).isEmpty(); - assertThat(FileUtils.listFiles(trashDir, new String[] {"jar"}, false)).hasSize(1); - Collection<DefaultPluginMetadata> removals = jarsInstaller.getUninstalledPlugins(); - assertThat(removals).hasSize(1); - PluginMetadata metadata = removals.iterator().next(); - assertThat(metadata.getKey()).isEqualTo("foo"); - assertThat(metadata.getName()).isEqualTo("Foo"); - assertThat(metadata.getVersion()).isEqualTo("1.0"); - assertThat(metadata.getOrganization()).isEqualTo("SonarSource"); - assertThat(metadata.getOrganizationUrl()).isEqualTo("http://www.sonarsource.org"); - assertThat(metadata.getLicense()).isEqualTo("LGPL 3"); - assertThat(metadata.getMainClass()).isEqualTo("foo.Main"); - } - - @Test - public void cancel_uninstallation() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), pluginsDir); - - jarsInstaller.install(); - jarsInstaller.uninstall("foo"); - jarsInstaller.cancelUninstalls(); - - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(1); - assertThat(FileUtils.listFiles(trashDir, new String[] {"jar"}, false)).hasSize(0); - assertThat(jarsInstaller.getUninstalledPluginFilenames()).isEmpty(); - } - - @Test - public void deploy_core_plugins() throws Exception { - when(upgradeStatus.isFreshInstall()).thenReturn(false); - FileUtils.copyFileToDirectory(jar("foo-plugin-1.0.jar"), coreDir); - - jarsInstaller.install(); - - // do not deploy in extensions/plugins - assertThat(FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false)).hasSize(0); - - // do not remove from lib/core-plugins - assertThat(FileUtils.listFiles(coreDir, new String[] {"jar"}, false)).hasSize(1); - - PluginMetadata plugin = jarsInstaller.getMetadata("foo"); - assertThat(plugin).isNotNull(); - assertThat(plugin.isCore()).isTrue(); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java index 5f1898dae31..ea87cb3af88 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java @@ -19,47 +19,298 @@ */ package org.sonar.server.plugins; -import com.google.common.io.Resources; +import com.google.common.collect.ImmutableSet; +import org.apache.commons.io.FileUtils; import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.platform.Server; +import org.sonar.api.platform.ServerUpgradeStatus; +import org.sonar.api.utils.MessageException; +import org.sonar.core.platform.PluginLoader; +import org.sonar.server.platform.DefaultServerFileSystem; +import org.sonar.updatecenter.common.Version; import java.io.File; -import java.util.Arrays; +import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ServerPluginRepositoryTest { - ServerPluginRepository repository; + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + Server server = mock(Server.class); + ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class); + DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS); + PluginLoader pluginLoader = new PluginLoader(new ServerPluginUnzipper(fs)); + ServerPluginRepository underTest = new ServerPluginRepository(server, upgradeStatus, fs, pluginLoader); + + @Before + public void setUp() throws IOException { + when(fs.getBundledPluginsDir()).thenReturn(temp.newFolder()); + when(fs.getCorePluginsDir()).thenReturn(temp.newFolder()); + when(fs.getDeployedPluginsDir()).thenReturn(temp.newFolder()); + when(fs.getDownloadedPluginsDir()).thenReturn(temp.newFolder()); + when(fs.getHomeDir()).thenReturn(temp.newFolder()); + when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder()); + when(fs.getTempDir()).thenReturn(temp.newFolder()); + when(server.getVersion()).thenReturn("5.2"); + } @After - public void stop() { - if (repository != null) { - repository.stop(); + public void tearDown() throws Exception { + underTest.stop(); + } + + /** + * The first server startup (fresh db) installs bundled plugins and instantiates bundled + core plugins. + */ + @Test + public void first_startup_installs_bundled_plugins() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getBundledPluginsDir()); + copyTestPluginTo("test-core-plugin", fs.getCorePluginsDir()); + when(upgradeStatus.isFreshInstall()).thenReturn(true); + + underTest.start(); + + // both plugins are installed + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("core", "testbase"); + assertThat(underTest.getPluginInstance("core").getClass().getName()).isEqualTo("CorePlugin"); + assertThat(underTest.getPluginInstance("testbase").getClass().getName()).isEqualTo("BasePlugin"); + assertThat(underTest.hasPlugin("testbase")).isTrue(); + } + + @Test + public void bundled_plugins_are_not_installed_if_not_fresh_server() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getBundledPluginsDir()); + when(upgradeStatus.isFreshInstall()).thenReturn(false); + + underTest.start(); + + assertThat(underTest.getPluginInfos()).isEmpty(); + } + + @Test + public void standard_startup_loads_core_and_installed_plugins() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + copyTestPluginTo("test-core-plugin", fs.getCorePluginsDir()); + + underTest.start(); + + // both plugins are installed + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("core", "testbase"); + assertThat(underTest.getPluginInstance("core").getClass().getName()).isEqualTo("CorePlugin"); + assertThat(underTest.getPluginInstance("testbase").getClass().getName()).isEqualTo("BasePlugin"); + } + + /** + * That sounds impossible, there are still core plugins for now, but it's still valuable + * to test sensibility to null values. + */ + @Test + public void no_plugins_at_all_on_startup() throws Exception { + underTest.start(); + + assertThat(underTest.getPluginInfos()).isEmpty(); + assertThat(underTest.getPluginInfosByKeys()).isEmpty(); + assertThat(underTest.getUninstalledPlugins()).isEmpty(); + assertThat(underTest.hasPlugin("testbase")).isFalse(); + } + + @Test + public void fail_if_multiple_jars_for_same_installed_plugin_on_startup() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + copyTestPluginTo("test-base-plugin-v2", fs.getInstalledPluginsDir()); + + try { + underTest.start(); + fail(); + } catch (MessageException e) { + assertThat(e) + .hasMessageStartingWith("Found two files for the same plugin [testbase]: ") + // order is not guaranteed, so assertion is split + .hasMessageContaining("test-base-plugin-0.1-SNAPSHOT.jar") + .hasMessageContaining("test-base-plugin-0.2-SNAPSHOT.jar"); } } @Test - public void testStart() throws Exception { - ServerPluginJarsInstaller deployer = mock(ServerPluginJarsInstaller.class); - File pluginFile = new File(Resources.getResource("org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar").toURI()); - PluginMetadata plugin = DefaultPluginMetadata.create(pluginFile) - .setKey("artifactsize") - .setMainClass("org.sonar.plugins.artifactsize.ArtifactSizePlugin") - .addDeployedFile(pluginFile); - when(deployer.getMetadata()).thenReturn(Arrays.asList(plugin)); + public void install_downloaded_plugins_on_startup() throws Exception { + File downloadedJar = copyTestPluginTo("test-base-plugin", fs.getDownloadedPluginsDir()); + + underTest.start(); + + // plugin is moved to extensions/plugins then loaded + assertThat(downloadedJar).doesNotExist(); + assertThat(new File(fs.getInstalledPluginsDir(), downloadedJar.getName())).isFile().exists(); + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase"); + assertThat(underTest.getPluginInstance("testbase").getClass().getName()).isEqualTo("BasePlugin"); + } + + @Test + public void downloaded_file_overrides_existing_installed_file_on_startup() throws Exception { + File installedV1 = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + File downloadedV2 = copyTestPluginTo("test-base-plugin-v2", fs.getDownloadedPluginsDir()); + + underTest.start(); + + // plugin is moved to extensions/plugins and replaces v1 + assertThat(downloadedV2).doesNotExist(); + assertThat(installedV1).doesNotExist(); + assertThat(new File(fs.getInstalledPluginsDir(), downloadedV2.getName())).exists(); + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase"); + assertThat(underTest.getPluginInfo("testbase").getVersion()).isEqualTo(Version.create("0.2-SNAPSHOT")); + } + + @Test + public void blacklisted_plugin_is_automatically_uninstalled_on_startup() throws Exception { + underTest.setBlacklistedPluginKeys(ImmutableSet.of("testbase", "issuesreport")); + File jar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); - repository = new ServerPluginRepository(deployer); - repository.start(); + // plugin is not installed and file is deleted + assertThat(underTest.getPluginInfos()).isEmpty(); + assertThat(jar).doesNotExist(); + } + + @Test + public void test_plugin_requirements_at_startup() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // both plugins are installed + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testrequire"); + } + + @Test + public void plugin_is_ignored_if_required_plugin_is_missing_at_startup() throws Exception { + copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // plugin is not installed as test-base-plugin is missing + assertThat(underTest.getPluginInfosByKeys()).isEmpty(); + } + + @Test + public void plugin_is_ignored_if_required_plugin_is_too_old_at_startup() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + copyTestPluginTo("test-requirenew-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // the plugin "requirenew" is not installed as it requires base 0.2+ to be installed. + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase"); + } + + @Test + public void plugin_is_ignored_at_startup_if_unsupported_sq() throws Exception { + when(server.getVersion()).thenReturn("1.0"); + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // plugin requires SQ 4.5.1 but SQ 1.0 is installed + assertThat(underTest.getPluginInfos()).isEmpty(); + } + + @Test + public void uninstall() throws Exception { + File installedJar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase"); + underTest.uninstall("testbase"); + + assertThat(installedJar).doesNotExist(); + // still up. Will be dropped after next startup + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase"); + assertThat(underTest.getUninstalledPluginFilenames()).containsOnly(installedJar.getName()); + assertThat(underTest.getUninstalledPlugins()).extracting("key").containsOnly("testbase"); + } + + @Test + public void uninstall_dependents() throws Exception { + File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + assertThat(underTest.getPluginInfos()).hasSize(2); + underTest.uninstall("testbase"); + + assertThat(base).doesNotExist(); + assertThat(extension).doesNotExist(); + assertThat(underTest.getUninstalledPluginFilenames()).containsOnly(base.getName(), extension.getName()); + assertThat(underTest.getUninstalledPlugins()).extracting("key").containsOnly("testbase", "testrequire"); + } + + @Test + public void cancel_uninstall() throws Exception { + File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + underTest.start(); + + underTest.uninstall("testbase"); + assertThat(base).doesNotExist(); + + underTest.cancelUninstalls(); + assertThat(base).exists(); + assertThat(underTest.getUninstalledPluginFilenames()).isEmpty(); + assertThat(underTest.getUninstalledPlugins()).isEmpty(); + } + + @Test + public void install_plugin_and_its_extension_plugins_at_startup() throws Exception { + copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir()); + copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // both plugins are installed + assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testextend"); + } + + @Test + public void extension_plugin_is_ignored_if_base_plugin_is_missing_at_startup() throws Exception { + copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir()); + + underTest.start(); + + // plugin is not installed as its base plugin is not installed + assertThat(underTest.getPluginInfos()).isEmpty(); + } + + @Test + public void fail_is_missing_required_plugin() throws Exception { + try { + underTest.getPluginInfo("unknown"); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Plugin [unknown] does not exist"); + } + + try { + underTest.getPluginInstance("unknown"); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Plugin [unknown] does not exist"); + } + } - assertThat(repository.getPlugin("artifactsize")).isNotNull(); - assertThat(repository.getClassLoader("artifactsize")).isNotNull(); - assertThat(repository.getClass("artifactsize", "org.sonar.plugins.artifactsize.ArtifactSizeMetrics")).isNotNull(); - assertThat(repository.getClass("artifactsize", "org.Unknown")).isNull(); - assertThat(repository.getClass("other", "org.sonar.plugins.artifactsize.ArtifactSizeMetrics")).isNull(); + private File copyTestPluginTo(String testPluginName, File toDir) throws IOException { + File jar = TestProjectUtils.jarOf(testPluginName); + // file is copied because it's supposed to be moved by the test + FileUtils.copyFileToDirectory(jar, toDir); + return new File(toDir, jar.getName()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginUnzipperTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginUnzipperTest.java new file mode 100644 index 00000000000..efea25d39e8 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginUnzipperTest.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.plugins; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.UnzippedPlugin; +import org.sonar.server.platform.DefaultServerFileSystem; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ServerPluginUnzipperTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class); + ServerPluginUnzipper underTest = new ServerPluginUnzipper(fs); + + @Test + public void copy_all_classloader_files_to_dedicated_directory() throws Exception { + File deployDir = temp.newFolder(); + when(fs.getDeployedPluginsDir()).thenReturn(deployDir); + File jar = TestProjectUtils.jarOf("test-libs-plugin"); + PluginInfo info = PluginInfo.create(jar); + + UnzippedPlugin unzipped = underTest.unzip(info); + + // all the files loaded by classloaders (JAR + META-INF/libs/*.jar) are copied to the dedicated directory + // web/deploy/{pluginKey} + File pluginDeployDir = new File(deployDir, "testlibs"); + + assertThat(unzipped.getKey()).isEqualTo("testlibs"); + assertThat(unzipped.getMain()).isFile().exists().hasParent(pluginDeployDir); + assertThat(unzipped.getLibs()).extracting("name").containsOnly("commons-daemon-1.0.15.jar", "commons-email-20030310.165926.jar"); + for (File lib : unzipped.getLibs()) { + assertThat(lib).exists().isFile(); + assertThat(lib.getCanonicalPath()).startsWith(pluginDeployDir.getCanonicalPath()); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/SonarHomeTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/TestProjectUtils.java index 66a4069fba1..00d579118ee 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/SonarHomeTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/TestProjectUtils.java @@ -17,13 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.platform; +package org.sonar.server.plugins; -import org.junit.Test; +import org.apache.commons.io.FileUtils; -public class SonarHomeTest { - @Test - public void iDontKnowHowToSimplyTestThisClass() { +import java.io.File; +import java.util.Collection; +public class TestProjectUtils { + + /** + * Get the artifact of plugins stored in src/test/projects + */ + public static File jarOf(String dirName) { + File target = FileUtils.toFile(TestProjectUtils.class.getResource(String.format("/%s/target/", dirName))); + Collection<File> jars = FileUtils.listFiles(target, new String[] {"jar"}, false); + if (jars == null || jars.size() != 1) { + throw new IllegalArgumentException("Test project is badly defined: " + dirName); + } + return jars.iterator().next(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java index 4ff9f43f340..3d2260eba4f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java @@ -28,7 +28,7 @@ import org.sonar.api.server.ws.WebService; import org.sonar.core.permission.GlobalPermissions; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -41,8 +41,8 @@ public class CancelAllPluginsWsActionTest { private static final String DUMMY_CONTROLLER_KEY = "dummy"; private PluginDownloader pluginDownloader = mock(PluginDownloader.class); - private ServerPluginJarsInstaller pluginJarsInstaller = mock(ServerPluginJarsInstaller.class); - private CancelAllPluginsWsAction underTest = new CancelAllPluginsWsAction(pluginDownloader, pluginJarsInstaller); + private ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); + private CancelAllPluginsWsAction underTest = new CancelAllPluginsWsAction(pluginDownloader, pluginRepository); private Request request = mock(Request.class); private WsTester.TestResponse response = new WsTester.TestResponse(); @@ -90,7 +90,7 @@ public class CancelAllPluginsWsActionTest { underTest.handle(request, response); verify(pluginDownloader, times(1)).cancelDownloads(); - verify(pluginJarsInstaller, times(1)).cancelUninstalls(); + verify(pluginRepository, times(1)).cancelUninstalls(); assertThat(response.outputAsString()).isEmpty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java index 3780d88af00..11a544e1d23 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java @@ -19,17 +19,17 @@ */ package org.sonar.server.plugins.ws; -import java.io.File; import org.junit.Test; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Version; + +import java.io.File; import static com.google.common.collect.ImmutableList.of; -import static java.lang.String.valueOf; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -42,12 +42,12 @@ public class InstalledPluginsWsActionTest { " \"plugins\":" + "[]" + "}"; - private PluginRepository pluginRepository = mock(PluginRepository.class); + private ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); private InstalledPluginsWsAction underTest = new InstalledPluginsWsAction(pluginRepository, new PluginWSCommons()); private Request request = mock(Request.class); private WsTester.TestResponse response = new WsTester.TestResponse(); - private PluginMetadata corePlugin = corePlugin("core1", 10); + private PluginInfo corePlugin = corePlugin("core1", "1.0"); @Test public void action_installed_is_defined() { @@ -75,7 +75,7 @@ public class InstalledPluginsWsActionTest { @Test public void core_plugin_are_not_returned() throws Exception { - when(pluginRepository.getMetadata()).thenReturn(of(corePlugin)); + when(pluginRepository.getPluginInfos()).thenReturn(of(corePlugin)); underTest.handle(request, response); @@ -84,9 +84,9 @@ public class InstalledPluginsWsActionTest { @Test public void empty_fields_are_not_serialized_to_json() throws Exception { - when(pluginRepository.getMetadata()).thenReturn( + when(pluginRepository.getPluginInfos()).thenReturn( of( - (PluginMetadata) DefaultPluginMetadata.create("").setName("").setCore(false) + new PluginInfo("").setName("").setCore(false) ) ); @@ -98,14 +98,14 @@ public class InstalledPluginsWsActionTest { @Test public void verify_properties_displayed_in_json_per_plugin() throws Exception { String jarFilename = getClass().getSimpleName() + "/" + "some.jar"; - when(pluginRepository.getMetadata()).thenReturn(of( - (PluginMetadata) DefaultPluginMetadata.create("plugKey").setName("plugName").setCore(false) + when(pluginRepository.getPluginInfos()).thenReturn(of( + new PluginInfo("plugKey").setName("plugName").setCore(false) .setDescription("desc_it") - .setVersion(valueOf(10)) + .setVersion(Version.create("1.0")) .setLicense("license_hey") - .setOrganization("org_name") + .setOrganizationName("org_name") .setOrganizationUrl("org_url") - .setHomepage("homepage_url") + .setHomepageUrl("homepage_url") .setIssueTrackerUrl("issueTracker_url") .setFile(new File(getClass().getResource(jarFilename).toURI())) .setImplementationBuild("sou_rev_sha1") @@ -122,7 +122,7 @@ public class InstalledPluginsWsActionTest { " \"key\": \"plugKey\"," + " \"name\": \"plugName\"," + " \"description\": \"desc_it\"," + - " \"version\": \"10\"," + + " \"version\": \"1.0\"," + " \"license\": \"license_hey\"," + " \"organizationName\": \"org_name\"," + " \"organizationUrl\": \"org_url\"," + @@ -137,7 +137,7 @@ public class InstalledPluginsWsActionTest { @Test public void plugins_are_sorted_by_name_then_key_and_only_one_plugin_can_have_a_specific_name() throws Exception { - when(pluginRepository.getMetadata()).thenReturn( + when(pluginRepository.getPluginInfos()).thenReturn( of( plugin("A", "name2"), plugin("B", "name1"), @@ -163,7 +163,7 @@ public class InstalledPluginsWsActionTest { @Test public void only_one_plugin_can_have_a_specific_name_and_key() throws Exception { - when(pluginRepository.getMetadata()).thenReturn( + when(pluginRepository.getPluginInfos()).thenReturn( of( plugin("A", "name2"), plugin("A", "name2") @@ -183,15 +183,15 @@ public class InstalledPluginsWsActionTest { assertThat(response.outputAsString()).containsOnlyOnce("name2"); } - private static PluginMetadata corePlugin(String key, int version) { - return DefaultPluginMetadata.create(key).setName(key).setCore(true).setVersion(valueOf(version)); + private static PluginInfo corePlugin(String key, String version) { + return new PluginInfo(key).setName(key).setCore(true).setVersion(Version.create(version)); } - private static PluginMetadata plugin(String key, String name, int version) { - return DefaultPluginMetadata.create(key).setName(name).setCore(false).setVersion(valueOf(version)); + private static PluginInfo plugin(String key, String name, String version) { + return new PluginInfo(key).setName(name).setCore(false).setVersion(Version.create(version)); } - private static PluginMetadata plugin(String key, String name) { - return DefaultPluginMetadata.create(key).setName(name).setCore(false).setVersion("1.0"); + private static PluginInfo plugin(String key, String name) { + return new PluginInfo(key).setName(name).setCore(false).setVersion(Version.create("1.0")); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java index 558ed57839a..1849151312a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java @@ -23,39 +23,39 @@ import java.io.File; import org.junit.Test; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Version; import static com.google.common.collect.ImmutableList.of; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.sonar.core.plugins.DefaultPluginMetadata.create; import static org.sonar.test.JsonAssert.assertJson; public class PendingPluginsWsActionTest { - public static final DefaultPluginMetadata GIT_PLUGIN_METADATA = create("scmgit") + public static final PluginInfo GIT_PLUGIN_INFO = new PluginInfo("scmgit") .setName("Git") .setDescription("Git SCM Provider.") - .setVersion("1.0") + .setVersion(Version.create("1.0")) .setLicense("GNU LGPL 3") - .setOrganization("SonarSource") + .setOrganizationName("SonarSource") .setOrganizationUrl("http://www.sonarsource.com") - .setHomepage("http://redirect.sonarsource.com/plugins/scmgit.html") + .setHomepageUrl("http://redirect.sonarsource.com/plugins/scmgit.html") .setIssueTrackerUrl("http://jira.codehaus.org/browse/SONARSCGIT") .setFile(new File("/home/user/sonar-scm-git-plugin-1.0.jar")) .setImplementationBuild("9ce9d330c313c296fab051317cc5ad4b26319e07"); private static final String DUMMY_CONTROLLER_KEY = "dummy"; - public static final DefaultPluginMetadata PLUGIN_2_2 = create("key2").setName("name2"); - public static final DefaultPluginMetadata PLUGIN_2_1 = create("key1").setName("name2"); - public static final DefaultPluginMetadata PLUGIN_0_0 = create("key0").setName("name0"); + public static final PluginInfo PLUGIN_2_2 = new PluginInfo("key2").setName("name2"); + public static final PluginInfo PLUGIN_2_1 = new PluginInfo("key1").setName("name2"); + public static final PluginInfo PLUGIN_0_0 = new PluginInfo("key0").setName("name0"); private PluginDownloader pluginDownloader = mock(PluginDownloader.class); - private ServerPluginJarsInstaller serverPluginJarsInstaller = mock(ServerPluginJarsInstaller.class); - private PendingPluginsWsAction underTest = new PendingPluginsWsAction(pluginDownloader, serverPluginJarsInstaller, new PluginWSCommons()); + private ServerPluginRepository serverPluginRepository = mock(ServerPluginRepository.class); + private PendingPluginsWsAction underTest = new PendingPluginsWsAction(pluginDownloader, serverPluginRepository, new PluginWSCommons()); private Request request = mock(Request.class); private WsTester.TestResponse response = new WsTester.TestResponse(); @@ -91,7 +91,7 @@ public class PendingPluginsWsActionTest { @Test public void verify_properties_displayed_in_json_per_installing_plugin() throws Exception { - when(pluginDownloader.getDownloadedPlugins()).thenReturn(of(GIT_PLUGIN_METADATA)); + when(pluginDownloader.getDownloadedPlugins()).thenReturn(of(GIT_PLUGIN_INFO)); underTest.handle(request, response); @@ -119,7 +119,7 @@ public class PendingPluginsWsActionTest { @Test public void verify_properties_displayed_in_json_per_removing_plugin() throws Exception { - when(serverPluginJarsInstaller.getUninstalledPlugins()).thenReturn(of(GIT_PLUGIN_METADATA)); + when(serverPluginRepository.getUninstalledPlugins()).thenReturn(of(GIT_PLUGIN_INFO)); underTest.handle(request, response); @@ -180,7 +180,7 @@ public class PendingPluginsWsActionTest { @Test public void removing_plugin_are_sorted_and_unique() throws Exception { - when(serverPluginJarsInstaller.getUninstalledPlugins()).thenReturn(of( + when(serverPluginRepository.getUninstalledPlugins()).thenReturn(of( PLUGIN_2_2, PLUGIN_2_1, PLUGIN_2_2, diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java index 831bdbd7b78..e055635b343 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java @@ -22,7 +22,7 @@ package org.sonar.server.plugins.ws; import java.io.File; import org.junit.Test; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; import org.sonar.server.ws.WsTester; import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.PluginUpdate; @@ -31,7 +31,6 @@ import org.sonar.updatecenter.common.Version; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.utils.DateUtils.parseDate; -import static org.sonar.core.plugins.DefaultPluginMetadata.create; import static org.sonar.server.plugins.ws.PluginWSCommons.toJSon; import static org.sonar.test.JsonAssert.assertJson; import static org.sonar.updatecenter.common.PluginUpdate.Status.COMPATIBLE; @@ -40,14 +39,14 @@ import static org.sonar.updatecenter.common.PluginUpdate.Status.INCOMPATIBLE; import static org.sonar.updatecenter.common.PluginUpdate.Status.REQUIRE_SONAR_UPGRADE; public class PluginWSCommonsTest { - private static final DefaultPluginMetadata GIT_PLUGIN_METADATA = create("scmgit") + private static final PluginInfo GIT_PLUGIN_METADATA = new PluginInfo("scmgit") .setName("Git") .setDescription("Git SCM Provider.") - .setVersion("1.0") + .setVersion(Version.create("1.0")) .setLicense("GNU LGPL 3") - .setOrganization("SonarSource") + .setOrganizationName("SonarSource") .setOrganizationUrl("http://www.sonarsource.com") - .setHomepage("http://redirect.sonarsource.com/plugins/scmgit.html") + .setHomepageUrl("http://redirect.sonarsource.com/plugins/scmgit.html") .setIssueTrackerUrl("http://jira.codehaus.org/browse/SONARSCGIT") .setFile(new File("/home/user/sonar-scm-git-plugin-1.0.jar")); private static final Plugin PLUGIN = new Plugin("p_key") @@ -202,7 +201,7 @@ public class PluginWSCommonsTest { PluginUpdate pluginUpdate = new PluginUpdate(); pluginUpdate.setRelease( new Release(PLUGIN, version("1.0")).addOutgoingDependency(RELEASE) - ); + ); jsonWriter.beginObject(); underTest.writeUpdate(jsonWriter, pluginUpdate); diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java index fa8adaa0f98..85a3bba714f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java @@ -19,19 +19,15 @@ */ package org.sonar.server.plugins.ws; -import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.plugins.DefaultPluginMetadata; import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -45,11 +41,10 @@ public class UninstallPluginsWsActionTest { private static final String CONTROLLER_KEY = "api/plugins"; private static final String ACTION_KEY = "uninstall"; private static final String KEY_PARAM = "key"; - private static final String PLUGIN_KEY = "pluginKey"; + private static final String PLUGIN_KEY = "findbugs"; - private PluginRepository pluginRepository = mock(PluginRepository.class); - private ServerPluginJarsInstaller pluginJarsInstaller = mock(ServerPluginJarsInstaller.class); - private UninstallPluginsWsAction underTest = new UninstallPluginsWsAction(pluginRepository, pluginJarsInstaller); + private ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); + private UninstallPluginsWsAction underTest = new UninstallPluginsWsAction(pluginRepository); private WsTester wsTester = new WsTester(new PluginsWs(underTest)); private Request invalidRequest = wsTester.newGetRequest(CONTROLLER_KEY, ACTION_KEY); @@ -109,20 +104,18 @@ public class UninstallPluginsWsActionTest { @Test public void IAE_is_raised_when_plugin_is_not_installed() throws Exception { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("No plugin with key 'pluginKey'"); + expectedException.expectMessage("Plugin [findbugs] is not installed"); underTest.handle(validRequest, response); } @Test public void if_plugin_is_installed_uninstallation_is_triggered() throws Exception { - when(pluginRepository.getMetadata()).thenReturn(ImmutableList.<PluginMetadata>of( - DefaultPluginMetadata.create(PLUGIN_KEY) - )); + when(pluginRepository.hasPlugin(PLUGIN_KEY)).thenReturn(true); underTest.handle(validRequest, response); - verify(pluginJarsInstaller).uninstall(PLUGIN_KEY); + verify(pluginRepository).uninstall(PLUGIN_KEY); assertThat(response.outputAsString()).isEmpty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java index cc96395bc62..8af9cb1eb7c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java @@ -25,9 +25,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.platform.PluginMetadata; -import org.sonar.api.platform.PluginRepository; -import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.platform.PluginRepository; import org.sonar.server.platform.DefaultServerFileSystem; import java.io.File; @@ -45,8 +44,8 @@ public class GeneratePluginIndexTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private DefaultServerFileSystem fileSystem; - private File index; + DefaultServerFileSystem fileSystem; + File index; @Before public void createIndexFile() { @@ -58,9 +57,9 @@ public class GeneratePluginIndexTest { @Test public void shouldWriteIndex() throws IOException { PluginRepository repository = mock(PluginRepository.class); - PluginMetadata sqale = newMetadata("sqale"); - PluginMetadata checkstyle = newMetadata("checkstyle"); - when(repository.getMetadata()).thenReturn(Arrays.asList(sqale, checkstyle)); + PluginInfo sqale = newInfo("sqale"); + PluginInfo checkstyle = newInfo("checkstyle"); + when(repository.getPluginInfos()).thenReturn(Arrays.asList(sqale, checkstyle)); new GeneratePluginIndex(fileSystem, repository).start(); @@ -70,11 +69,7 @@ public class GeneratePluginIndexTest { assertThat(lines.get(1), containsString("checkstyle")); } - private PluginMetadata newMetadata(String pluginKey) throws IOException { - PluginMetadata plugin = mock(DefaultPluginMetadata.class); - when(plugin.getKey()).thenReturn(pluginKey); - File pluginFile = temp.newFile(pluginKey + ".jar"); - when(plugin.getFile()).thenReturn(pluginFile); - return plugin; + private PluginInfo newInfo(String pluginKey) throws IOException { + return new PluginInfo(pluginKey).setFile(temp.newFile(pluginKey + ".jar")); } } diff --git a/server/sonar-server/src/test/projects/.gitignore b/server/sonar-server/src/test/projects/.gitignore new file mode 100644 index 00000000000..a945b8525e6 --- /dev/null +++ b/server/sonar-server/src/test/projects/.gitignore @@ -0,0 +1,7 @@ +# see README.txt +!*/target/ +*/target/classes/ +*/target/maven-archiver/ +*/target/maven-status/ +*/target/test-*/ + diff --git a/server/sonar-server/src/test/projects/README.txt b/server/sonar-server/src/test/projects/README.txt new file mode 100644 index 00000000000..c53a66d52f2 --- /dev/null +++ b/server/sonar-server/src/test/projects/README.txt @@ -0,0 +1,3 @@ +This directory provides the fake plugins used by tests. These tests are rarely changed, so generated +artifacts are stored in Git repository (see .gitignore). It avoids from adding unnecessary modules +to build. diff --git a/server/sonar-server/src/test/projects/pom.xml b/server/sonar-server/src/test/projects/pom.xml new file mode 100644 index 00000000000..f244768228e --- /dev/null +++ b/server/sonar-server/src/test/projects/pom.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>parent</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>pom</packaging> + <modules> + <module>test-base-plugin</module> + <module>test-base-plugin-v2</module> + <module>test-core-plugin</module> + <module>test-extend-plugin</module> + <module>test-libs-plugin</module> + <module>test-require-plugin</module> + <module>test-requirenew-plugin</module> + </modules> + +</project> diff --git a/server/sonar-server/src/test/projects/test-base-plugin-v2/pom.xml b/server/sonar-server/src/test/projects/test-base-plugin-v2/pom.xml new file mode 100644 index 00000000000..21f7ed5558d --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin-v2/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-base-plugin</artifactId> + <version>0.2-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Base Plugin</name> + <description>Simple standalone plugin. Used by other fake plugins.</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testbase</pluginKey> + <pluginClass>BasePlugin</pluginClass> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-base-plugin-v2/src/BasePlugin.java b/server/sonar-server/src/test/projects/test-base-plugin-v2/src/BasePlugin.java new file mode 100644 index 00000000000..57b4a5dfb98 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin-v2/src/BasePlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class BasePlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..9bd9e0e0fc1 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-base-plugin/pom.xml b/server/sonar-server/src/test/projects/test-base-plugin/pom.xml new file mode 100644 index 00000000000..61b994c4754 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-base-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Base Plugin</name> + <description>Simple standalone plugin. Used by other fake plugins.</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testbase</pluginKey> + <pluginClass>BasePlugin</pluginClass> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-base-plugin/src/BasePlugin.java b/server/sonar-server/src/test/projects/test-base-plugin/src/BasePlugin.java new file mode 100644 index 00000000000..57b4a5dfb98 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin/src/BasePlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class BasePlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..2a6148ce7b6 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-core-plugin/pom.xml b/server/sonar-server/src/test/projects/test-core-plugin/pom.xml new file mode 100644 index 00000000000..fc3f082f6ec --- /dev/null +++ b/server/sonar-server/src/test/projects/test-core-plugin/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-core-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Test Core Plugin</name> + <description>Fake core plugin used by tests</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>core</pluginKey> + <pluginClass>CorePlugin</pluginClass> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-core-plugin/src/CorePlugin.java b/server/sonar-server/src/test/projects/test-core-plugin/src/CorePlugin.java new file mode 100644 index 00000000000..910204d87ea --- /dev/null +++ b/server/sonar-server/src/test/projects/test-core-plugin/src/CorePlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class CorePlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-core-plugin/target/test-core-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-core-plugin/target/test-core-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..62eba2aa80f --- /dev/null +++ b/server/sonar-server/src/test/projects/test-core-plugin/target/test-core-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-extend-plugin/pom.xml b/server/sonar-server/src/test/projects/test-extend-plugin/pom.xml new file mode 100644 index 00000000000..9b20de15a6a --- /dev/null +++ b/server/sonar-server/src/test/projects/test-extend-plugin/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-extend-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Test Extend Plugin</name> + <description>Fake plugin that extends the plugin with key "base"</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testextend</pluginKey> + <pluginClass>ExtendPlugin</pluginClass> + <basePlugin>testbase</basePlugin> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-extend-plugin/src/ExtendPlugin.java b/server/sonar-server/src/test/projects/test-extend-plugin/src/ExtendPlugin.java new file mode 100644 index 00000000000..826e1842bbb --- /dev/null +++ b/server/sonar-server/src/test/projects/test-extend-plugin/src/ExtendPlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class ExtendPlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..7a81fdf0cce --- /dev/null +++ b/server/sonar-server/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-libs-plugin/pom.xml b/server/sonar-server/src/test/projects/test-libs-plugin/pom.xml new file mode 100644 index 00000000000..e7d242135db --- /dev/null +++ b/server/sonar-server/src/test/projects/test-libs-plugin/pom.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-libs-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Test Libs Plugin</name> + <description>Fake plugin that embeds some libraries</description> + + <dependencies> + <!-- embedded libs. Chosen because small ! --> + <dependency> + <groupId>commons-email</groupId> + <artifactId>commons-email</artifactId> + <version>20030310.165926</version> + </dependency> + <dependency> + <groupId>commons-daemon</groupId> + <artifactId>commons-daemon</artifactId> + <version>1.0.15</version> + </dependency> + + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testlibs</pluginKey> + <pluginClass>LibsPlugin</pluginClass> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-libs-plugin/src/LibsPlugin.java b/server/sonar-server/src/test/projects/test-libs-plugin/src/LibsPlugin.java new file mode 100644 index 00000000000..965c9ac7496 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-libs-plugin/src/LibsPlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class LibsPlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..85e4772f474 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-require-plugin/pom.xml b/server/sonar-server/src/test/projects/test-require-plugin/pom.xml new file mode 100644 index 00000000000..1f77e233f62 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-require-plugin/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-require-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Test Require Plugin</name> + <description>This fake plugin depends on test-base-plugin</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testrequire</pluginKey> + <pluginClass>RequirePlugin</pluginClass> + <requirePlugins>testbase:0.1</requirePlugins> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-require-plugin/src/RequirePlugin.java b/server/sonar-server/src/test/projects/test-require-plugin/src/RequirePlugin.java new file mode 100644 index 00000000000..440f73bfb58 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-require-plugin/src/RequirePlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class RequirePlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..ac1f9f68e46 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/projects/test-requirenew-plugin/pom.xml b/server/sonar-server/src/test/projects/test-requirenew-plugin/pom.xml new file mode 100644 index 00000000000..ca207b10c19 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-requirenew-plugin/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.codehaus.sonar.tests</groupId> + <artifactId>test-requirenew-plugin</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>sonar-plugin</packaging> + <name>Test Require New Plugin</name> + <description>This fake plugin requires a version of test-base-plugin that is not installed</description> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>4.5.4</version> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.13</version> + <extensions>true</extensions> + <configuration> + <pluginKey>testrequire</pluginKey> + <pluginClass>RequirePlugin</pluginClass> + <requirePlugins>testbase:0.2</requirePlugins> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/server/sonar-server/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java b/server/sonar-server/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java new file mode 100644 index 00000000000..440f73bfb58 --- /dev/null +++ b/server/sonar-server/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java @@ -0,0 +1,11 @@ +import org.sonar.api.SonarPlugin; + +import java.util.Collections; +import java.util.List; + +public class RequirePlugin extends SonarPlugin { + + public List getExtensions() { + return Collections.emptyList(); + } +} diff --git a/server/sonar-server/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar b/server/sonar-server/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar Binary files differnew file mode 100644 index 00000000000..3437dfee71c --- /dev/null +++ b/server/sonar-server/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/extension.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/extension.jar Binary files differdeleted file mode 100644 index e788522ba71..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/extension.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/foo-plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/foo-plugin.jar Binary files differdeleted file mode 100644 index 7bcf027151a..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/foo-plugin.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/sonar-build-breaker-plugin-0.1.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/sonar-build-breaker-plugin-0.1.jar Binary files differdeleted file mode 100644 index 0eb5f40bb8c..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/sonar-build-breaker-plugin-0.1.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginDownloaderTest/foo-plugin-1.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginDownloaderTest/foo-plugin-1.0.jar Binary files differdeleted file mode 100644 index 3b3ed4b1b78..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginDownloaderTest/foo-plugin-1.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version1/extension.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version1/extension.jar Binary files differdeleted file mode 100644 index e788522ba71..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version1/extension.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version2/extension.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version2/extension.jar Binary files differdeleted file mode 100644 index 636176b3092..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version2/extension.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-1.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-1.0.jar Binary files differdeleted file mode 100644 index b60ea353a21..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-1.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-2.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-2.0.jar Binary files differdeleted file mode 100644 index 2e0488cebdf..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-2.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/not-a-plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/not-a-plugin.jar Binary files differdeleted file mode 100644 index f35e77146cc..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/not-a-plugin.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/old-plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/old-plugin.jar Binary files differdeleted file mode 100644 index abb19c057b5..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/old-plugin.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataTest/foo-plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataTest/foo-plugin.jar Binary files differdeleted file mode 100644 index 7bcf027151a..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataTest/foo-plugin.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/bar-plugin-1.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/bar-plugin-1.0.jar Binary files differdeleted file mode 100644 index acf4fa40269..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/bar-plugin-1.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-1.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-1.0.jar Binary files differdeleted file mode 100644 index 3b3ed4b1b78..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-1.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-2.0.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-2.0.jar Binary files differdeleted file mode 100644 index b781205e0d0..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-2.0.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/not-a-plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/not-a-plugin.jar Binary files differdeleted file mode 100644 index 11b72f4f8eb..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/not-a-plugin.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/require-sq-2.5.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/require-sq-2.5.jar Binary files differdeleted file mode 100644 index 8044dff8988..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/require-sq-2.5.jar +++ /dev/null diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar Binary files differdeleted file mode 100644 index 19533234582..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar +++ /dev/null diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb index 555eb77744d..74833832017 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb @@ -48,7 +48,7 @@ class Api::UpdatecenterController < Api::ApiController hash={} hash['key']=plugin.getKey() hash['name']=plugin.getName() - hash['version']=plugin.getVersion() || '-' + hash['version']=plugin.getVersion().getName() hash end @@ -58,13 +58,13 @@ class Api::UpdatecenterController < Api::ApiController xml.plugin do xml.key(plugin.getKey()) xml.name(plugin.getName()) - xml.version(plugin.getVersion() || '-') + xml.version(plugin.getVersion().getName()) end end end end def user_plugins - java_facade.getPluginsMetadata().select{|plugin| !plugin.isCore()}.to_a.sort + java_facade.getPluginInfos().select{|plugin| !plugin.isCore()}.to_a.sort end end |