@@ -17,9 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/** | |||
* Deprecated in 4.5.1. JFreechart charts are replaced by Javascript charts. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.plugins.core.charts; | |||
@@ -17,9 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/** | |||
* Deprecated in 4.5.1. JFreechart charts are replaced by Javascript charts. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.plugins.core.dashboards; | |||
@@ -17,9 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/** | |||
* Deprecated in 4.5.1. JFreechart charts are replaced by Javascript charts. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.plugins.core.notifications.alerts; | |||
@@ -17,9 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/** | |||
* Deprecated in 4.5.1. JFreechart charts are replaced by Javascript charts. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.plugins.core.security; | |||
@@ -17,9 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/** | |||
* Deprecated in 4.5.1. JFreechart charts are replaced by Javascript charts. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.plugins.core.timemachine; | |||
@@ -595,6 +595,11 @@ | |||
<artifactId>sonar-channel</artifactId> | |||
<version>4.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-classloader</artifactId> | |||
<version>1.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-markdown</artifactId> | |||
@@ -707,11 +712,6 @@ | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.plexus</groupId> | |||
<artifactId>plexus-classworlds</artifactId> | |||
<version>2.5.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.tinkerpop.blueprints</groupId> | |||
<artifactId>blueprints-core</artifactId> |
@@ -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> |
@@ -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; |
@@ -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; |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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)); |
@@ -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 | |||
*/ |
@@ -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 |
@@ -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 |
@@ -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(); | |||
} | |||
})); | |||
}); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; |
@@ -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"); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); |
@@ -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(); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); |
@@ -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(); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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(); |
@@ -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) { |
@@ -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 |
@@ -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; | |||
@@ -37,20 +37,6 @@ public class DefaultServerFileSystemTest { | |||
@Rule | |||
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( |
@@ -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"); |
@@ -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"); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -17,33 +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.core.plugins; | |||
package org.sonar.server.plugins; | |||
import com.google.common.collect.Lists; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.commons.io.FileUtils; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.io.File; | |||
import java.util.Collection; | |||
/** | |||
* This class loader is used to load resources from a list of URLs - see SONAR-1861. | |||
*/ | |||
public class ResourcesClassloader extends URLClassLoader { | |||
private Collection<URL> urls; | |||
public ResourcesClassloader(Collection<URL> urls, ClassLoader parent) { | |||
super(new URL[] {}, parent); | |||
this.urls = Lists.newArrayList(urls); | |||
} | |||
public class TestProjectUtils { | |||
@Override | |||
public URL findResource(String name) { | |||
for (URL url : urls) { | |||
if (StringUtils.endsWith(url.getPath(), name)) { | |||
return url; | |||
} | |||
/** | |||
* 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 null; | |||
return jars.iterator().next(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
@@ -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")); | |||
} | |||
} |
@@ -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, |
@@ -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); |
@@ -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(); | |||
} | |||
@@ -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")); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
# see README.txt | |||
!*/target/ | |||
*/target/classes/ | |||
*/target/maven-archiver/ | |||
*/target/maven-status/ | |||
*/target/test-*/ | |||
@@ -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. |
@@ -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> |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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 |
@@ -32,7 +32,7 @@ import org.sonar.api.batch.postjob.PostJob; | |||
import org.sonar.api.batch.postjob.PostJobContext; | |||
import org.sonar.api.batch.sensor.Sensor; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
import org.sonar.api.platform.ComponentContainer; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.utils.AnnotationUtils; | |||
import org.sonar.api.utils.dag.DirectAcyclicGraph; |
@@ -19,13 +19,14 @@ | |||
*/ | |||
package org.sonar.batch.bootstrap; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.collect.Lists; | |||
import org.apache.commons.lang.CharUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.SonarPlugin; | |||
import org.sonar.api.platform.PluginMetadata; | |||
import org.sonar.api.Plugin; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.plugins.RemotePlugin; | |||
import org.sonar.core.plugins.RemotePluginFile; | |||
import org.sonar.home.cache.FileCache; | |||
@@ -33,26 +34,52 @@ import org.sonar.home.cache.FileCache; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* A {@link PluginsRepository} implementation that put downloaded plugins in a FS cache. | |||
* Downloads the plugins installed on server and stores them in a local user cache | |||
* (see {@link FileCacheProvider}). | |||
*/ | |||
public class DefaultPluginsRepository implements PluginsRepository { | |||
public class BatchPluginInstaller implements PluginInstaller { | |||
private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginsRepository.class); | |||
private static final Logger LOG = Loggers.get(BatchPluginInstaller.class); | |||
private ServerClient server; | |||
private FileCache fileCache; | |||
private final ServerClient server; | |||
private final FileCache fileCache; | |||
private final BatchPluginPredicate pluginPredicate; | |||
public DefaultPluginsRepository(FileCache fileCache, ServerClient server) { | |||
public BatchPluginInstaller(ServerClient server, FileCache fileCache, BatchPluginPredicate pluginPredicate) { | |||
this.server = server; | |||
this.fileCache = fileCache; | |||
this.pluginPredicate = pluginPredicate; | |||
} | |||
@Override | |||
public File pluginFile(final RemotePlugin remote) { | |||
public Map<String, PluginInfo> installRemotes() { | |||
Map<String, PluginInfo> infosByKey = new HashMap<>(); | |||
for (RemotePlugin remotePlugin : listRemotePlugins()) { | |||
if (pluginPredicate.apply(remotePlugin.getKey())) { | |||
File jarFile = download(remotePlugin); | |||
PluginInfo info = PluginInfo.create(jarFile); | |||
infosByKey.put(info.getKey(), info); | |||
} | |||
} | |||
return infosByKey; | |||
} | |||
/** | |||
* Returns empty on purpose. This method is used only by tests. | |||
* @see org.sonar.batch.mediumtest.BatchMediumTester | |||
*/ | |||
@Override | |||
public Map<String, Plugin> installLocals() { | |||
return Collections.emptyMap(); | |||
} | |||
@VisibleForTesting | |||
File download(final RemotePlugin remote) { | |||
try { | |||
final RemotePluginFile file = remote.file(); | |||
return fileCache.get(file.getFilename(), file.getHash(), new FileCache.Downloader() { | |||
@@ -73,27 +100,24 @@ public class DefaultPluginsRepository implements PluginsRepository { | |||
} | |||
} | |||
@Override | |||
public List<RemotePlugin> pluginList() { | |||
/** | |||
* Gets information about the plugins installed on server (filename, checksum) | |||
*/ | |||
@VisibleForTesting | |||
List<RemotePlugin> listRemotePlugins() { | |||
String url = "/deploy/plugins/index.txt"; | |||
try { | |||
LOG.debug("Download index of plugins"); | |||
String indexContent = server.request(url); | |||
String[] rows = StringUtils.split(indexContent, CharUtils.LF); | |||
List<RemotePlugin> remoteLocations = Lists.newArrayList(); | |||
List<RemotePlugin> result = Lists.newArrayList(); | |||
for (String row : rows) { | |||
remoteLocations.add(RemotePlugin.unmarshal(row)); | |||
result.add(RemotePlugin.unmarshal(row)); | |||
} | |||
return remoteLocations; | |||
return result; | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to download plugins index: " + url, e); | |||
throw new IllegalStateException("Fail to download list of plugins: " + url, e); | |||
} | |||
} | |||
@Override | |||
public Map<PluginMetadata, SonarPlugin> localPlugins() { | |||
return Collections.emptyMap(); | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.Lists; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.BatchComponent; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import javax.annotation.Nonnull; | |||
import java.text.MessageFormat; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.Set; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static com.google.common.collect.Sets.newHashSet; | |||
/** | |||
* Filters the plugins to be enabled during analysis | |||
*/ | |||
public class BatchPluginPredicate implements Predicate<String>, BatchComponent { | |||
private static final Logger LOG = Loggers.get(BatchPluginPredicate.class); | |||
private static final String CORE_PLUGIN_KEY = "core"; | |||
private static final String BUILDBREAKER_PLUGIN_KEY = "buildbreaker"; | |||
private static final String PROPERTY_IS_DEPRECATED_MSG = "Property {0} is deprecated. Please use {1} instead."; | |||
private final Set<String> whites = newHashSet(), blacks = newHashSet(); | |||
private final DefaultAnalysisMode mode; | |||
public BatchPluginPredicate(Settings settings, DefaultAnalysisMode mode) { | |||
this.mode = mode; | |||
if (settings.hasKey(CoreProperties.BATCH_INCLUDE_PLUGINS)) { | |||
whites.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_INCLUDE_PLUGINS))); | |||
} | |||
if (settings.hasKey(CoreProperties.BATCH_EXCLUDE_PLUGINS)) { | |||
blacks.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_EXCLUDE_PLUGINS))); | |||
} | |||
if (mode.isPreview()) { | |||
// These default values are not supported by Settings because the class CorePlugin | |||
// is not loaded yet. | |||
if (settings.hasKey(CoreProperties.DRY_RUN_INCLUDE_PLUGINS)) { | |||
LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS)); | |||
whites.addAll(propertyValues(settings, | |||
CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} else { | |||
whites.addAll(propertyValues(settings, | |||
CoreProperties.PREVIEW_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} | |||
if (settings.hasKey(CoreProperties.DRY_RUN_EXCLUDE_PLUGINS)) { | |||
LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS)); | |||
blacks.addAll(propertyValues(settings, | |||
CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} else { | |||
blacks.addAll(propertyValues(settings, | |||
CoreProperties.PREVIEW_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} | |||
} | |||
if (!whites.isEmpty()) { | |||
LOG.info("Include plugins: " + Joiner.on(", ").join(whites)); | |||
} | |||
if (!blacks.isEmpty()) { | |||
LOG.info("Exclude plugins: " + Joiner.on(", ").join(blacks)); | |||
} | |||
} | |||
@Override | |||
public boolean apply(@Nonnull String pluginKey) { | |||
if (CORE_PLUGIN_KEY.equals(pluginKey)) { | |||
return !mode.isMediumTest(); | |||
} | |||
if (BUILDBREAKER_PLUGIN_KEY.equals(pluginKey) && mode.isPreview()) { | |||
LOG.info("Build Breaker plugin is no more supported in preview/incremental mode"); | |||
return false; | |||
} | |||
// FIXME what happens if there are only white-listed plugins ? | |||
List<String> mergeList = newArrayList(blacks); | |||
mergeList.removeAll(whites); | |||
return mergeList.isEmpty() || !mergeList.contains(pluginKey); | |||
} | |||
Set<String> getWhites() { | |||
return whites; | |||
} | |||
Set<String> getBlacks() { | |||
return blacks; | |||
} | |||
static List<String> propertyValues(Settings settings, String key, String defaultValue) { | |||
String s = StringUtils.defaultIfEmpty(settings.getString(key), defaultValue); | |||
return Lists.newArrayList(Splitter.on(",").trimResults().split(s)); | |||
} | |||
} |
@@ -19,174 +19,69 @@ | |||
*/ | |||
package org.sonar.batch.bootstrap; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.CoreProperties; | |||
import org.picocontainer.Startable; | |||
import org.sonar.api.Plugin; | |||
import org.sonar.api.SonarPlugin; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.platform.PluginMetadata; | |||
import org.sonar.api.platform.PluginRepository; | |||
import org.sonar.core.plugins.PluginClassloaders; | |||
import org.sonar.core.plugins.RemotePlugin; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.platform.PluginLoader; | |||
import org.sonar.core.platform.PluginRepository; | |||
import java.io.File; | |||
import java.text.MessageFormat; | |||
import java.util.*; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static com.google.common.collect.Sets.newHashSet; | |||
public class BatchPluginRepository implements PluginRepository, Startable { | |||
public class BatchPluginRepository implements PluginRepository { | |||
private final PluginInstaller installer; | |||
private final PluginLoader loader; | |||
private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class); | |||
private static final String CORE_PLUGIN = "core"; | |||
private Map<String, Plugin> pluginInstancesByKeys; | |||
private Map<String, PluginInfo> infosByKeys; | |||
private PluginsRepository pluginsReferential; | |||
private Map<String, Plugin> pluginsByKey; | |||
private Map<String, PluginMetadata> metadataByKey; | |||
private Settings settings; | |||
private PluginClassloaders classLoaders; | |||
private final DefaultAnalysisMode analysisMode; | |||
private final BatchPluginJarInstaller pluginInstaller; | |||
public BatchPluginRepository(PluginsRepository pluginsReferential, Settings settings, DefaultAnalysisMode analysisMode, | |||
BatchPluginJarInstaller pluginInstaller) { | |||
this.pluginsReferential = pluginsReferential; | |||
this.settings = settings; | |||
this.analysisMode = analysisMode; | |||
this.pluginInstaller = pluginInstaller; | |||
public BatchPluginRepository(PluginInstaller installer, PluginLoader loader) { | |||
this.installer = installer; | |||
this.loader = loader; | |||
} | |||
@Override | |||
public void start() { | |||
LOG.info("Install plugins"); | |||
doStart(pluginsReferential.pluginList()); | |||
Map<PluginMetadata, SonarPlugin> localPlugins = pluginsReferential.localPlugins(); | |||
if (!localPlugins.isEmpty()) { | |||
LOG.info("Install local plugins"); | |||
for (Map.Entry<PluginMetadata, SonarPlugin> pluginByMetadata : localPlugins.entrySet()) { | |||
metadataByKey.put(pluginByMetadata.getKey().getKey(), pluginByMetadata.getKey()); | |||
pluginsByKey.put(pluginByMetadata.getKey().getKey(), pluginByMetadata.getValue()); | |||
} | |||
} | |||
} | |||
infosByKeys = installer.installRemotes(); | |||
pluginInstancesByKeys = loader.load(infosByKeys); | |||
void doStart(List<RemotePlugin> remotePlugins) { | |||
PluginFilter filter = new PluginFilter(settings, analysisMode); | |||
metadataByKey = Maps.newHashMap(); | |||
for (RemotePlugin remote : remotePlugins) { | |||
if (filter.accepts(remote.getKey())) { | |||
File pluginFile = pluginsReferential.pluginFile(remote); | |||
PluginMetadata metadata = pluginInstaller.installToCache(pluginFile, remote.isCore()); | |||
if (StringUtils.isBlank(metadata.getBasePlugin()) || filter.accepts(metadata.getBasePlugin())) { | |||
metadataByKey.put(metadata.getKey(), metadata); | |||
} else { | |||
LOG.debug("Excluded plugin: " + metadata.getKey()); | |||
} | |||
} | |||
// this part is only used by tests | |||
for (Map.Entry<String, Plugin> entry : installer.installLocals().entrySet()) { | |||
String pluginKey = entry.getKey(); | |||
infosByKeys.put(pluginKey, new PluginInfo(pluginKey)); | |||
pluginInstancesByKeys.put(pluginKey, entry.getValue()); | |||
} | |||
classLoaders = new PluginClassloaders(Thread.currentThread().getContextClassLoader()); | |||
pluginsByKey = classLoaders.init(metadataByKey.values()); | |||
} | |||
@Override | |||
public void stop() { | |||
if (classLoaders != null) { | |||
classLoaders.clean(); | |||
classLoaders = null; | |||
} | |||
} | |||
// close plugin classloaders | |||
loader.unload(pluginInstancesByKeys.values()); | |||
@Override | |||
public Plugin getPlugin(String key) { | |||
return pluginsByKey.get(key); | |||
pluginInstancesByKeys.clear(); | |||
infosByKeys.clear(); | |||
} | |||
@Override | |||
public Collection<PluginMetadata> getMetadata() { | |||
return metadataByKey.values(); | |||
public Collection<PluginInfo> getPluginInfos() { | |||
return infosByKeys.values(); | |||
} | |||
@Override | |||
public PluginMetadata getMetadata(String pluginKey) { | |||
return metadataByKey.get(pluginKey); | |||
public PluginInfo getPluginInfo(String key) { | |||
// TODO check null result | |||
return infosByKeys.get(key); | |||
} | |||
public Map<PluginMetadata, Plugin> getPluginsByMetadata() { | |||
Map<PluginMetadata, Plugin> result = Maps.newHashMap(); | |||
for (Map.Entry<String, PluginMetadata> entry : metadataByKey.entrySet()) { | |||
String pluginKey = entry.getKey(); | |||
PluginMetadata metadata = entry.getValue(); | |||
result.put(metadata, pluginsByKey.get(pluginKey)); | |||
} | |||
return result; | |||
@Override | |||
public Plugin getPluginInstance(String key) { | |||
// TODO check null result | |||
return pluginInstancesByKeys.get(key); | |||
} | |||
static class PluginFilter { | |||
private static final String BUILDBREAKER_PLUGIN_KEY = "buildbreaker"; | |||
private static final String PROPERTY_IS_DEPRECATED_MSG = "Property {0} is deprecated. Please use {1} instead."; | |||
Set<String> whites = newHashSet(), blacks = newHashSet(); | |||
private DefaultAnalysisMode mode; | |||
PluginFilter(Settings settings, DefaultAnalysisMode mode) { | |||
this.mode = mode; | |||
if (settings.hasKey(CoreProperties.BATCH_INCLUDE_PLUGINS)) { | |||
whites.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_INCLUDE_PLUGINS))); | |||
} | |||
if (settings.hasKey(CoreProperties.BATCH_EXCLUDE_PLUGINS)) { | |||
blacks.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_EXCLUDE_PLUGINS))); | |||
} | |||
if (mode.isPreview()) { | |||
// These default values are not supported by Settings because the class CorePlugin | |||
// is not loaded yet. | |||
if (settings.hasKey(CoreProperties.DRY_RUN_INCLUDE_PLUGINS)) { | |||
LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS)); | |||
whites.addAll(propertyValues(settings, | |||
CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} else { | |||
whites.addAll(propertyValues(settings, | |||
CoreProperties.PREVIEW_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} | |||
if (settings.hasKey(CoreProperties.DRY_RUN_EXCLUDE_PLUGINS)) { | |||
LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS)); | |||
blacks.addAll(propertyValues(settings, | |||
CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} else { | |||
blacks.addAll(propertyValues(settings, | |||
CoreProperties.PREVIEW_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE)); | |||
} | |||
} | |||
if (!whites.isEmpty()) { | |||
LOG.info("Include plugins: " + Joiner.on(", ").join(whites)); | |||
} | |||
if (!blacks.isEmpty()) { | |||
LOG.info("Exclude plugins: " + Joiner.on(", ").join(blacks)); | |||
} | |||
} | |||
static List<String> propertyValues(Settings settings, String key, String defaultValue) { | |||
String s = StringUtils.defaultIfEmpty(settings.getString(key), defaultValue); | |||
return Lists.newArrayList(Splitter.on(",").trimResults().split(s)); | |||
} | |||
boolean accepts(String pluginKey) { | |||
if (CORE_PLUGIN.equals(pluginKey)) { | |||
return !mode.isMediumTest(); | |||
} | |||
if (BUILDBREAKER_PLUGIN_KEY.equals(pluginKey) && mode.isPreview()) { | |||
LOG.info("Build Breaker plugin is no more supported in preview/incremental mode"); | |||
return false; | |||
} | |||
List<String> mergeList = newArrayList(blacks); | |||
mergeList.removeAll(whites); | |||
return mergeList.isEmpty() || !mergeList.contains(pluginKey); | |||
} | |||
@Override | |||
public boolean hasPlugin(String key) { | |||
return infosByKeys.containsKey(key); | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import org.apache.commons.io.FileUtils; | |||
import org.sonar.api.BatchComponent; | |||
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.home.cache.FileCache; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
public class BatchPluginUnzipper extends PluginUnzipper implements BatchComponent { | |||
private final FileCache fileCache; | |||
public BatchPluginUnzipper(FileCache fileCache) { | |||
this.fileCache = fileCache; | |||
} | |||
@Override | |||
public UnzippedPlugin unzip(PluginInfo info) { | |||
try { | |||
File dir = unzipFile(info.getFile()); | |||
return UnzippedPlugin.createFromUnzippedDir(info.getKey(), info.getFile(), dir); | |||
} catch (Exception e) { | |||
throw new IllegalStateException(String.format("Fail to open plugin [%s]: %s", info.getKey(), info.getFile().getAbsolutePath()), e); | |||
} | |||
} | |||
private File unzipFile(File cachedFile) throws IOException { | |||
String filename = cachedFile.getName(); | |||
File destDir = new File(cachedFile.getParentFile(), filename + "_unzip"); | |||
File lockFile = new File(cachedFile.getParentFile(), filename + "_unzip.lock"); | |||
if (!destDir.exists()) { | |||
FileOutputStream out = new FileOutputStream(lockFile); | |||
try { | |||
java.nio.channels.FileLock lock = out.getChannel().lock(); | |||
try { | |||
// Recheck in case of concurrent processes | |||
if (!destDir.exists()) { | |||
File tempDir = fileCache.createTempDir(); | |||
ZipUtils.unzip(cachedFile, tempDir, newLibFilter()); | |||
FileUtils.moveDirectory(tempDir, destDir); | |||
} | |||
} finally { | |||
lock.release(); | |||
} | |||
} finally { | |||
out.close(); | |||
FileUtils.deleteQuietly(lockFile); | |||
} | |||
} | |||
return destDir; | |||
} | |||
} |
@@ -21,22 +21,22 @@ package org.sonar.batch.bootstrap; | |||
import org.sonar.api.ExtensionProvider; | |||
import org.sonar.api.Plugin; | |||
import org.sonar.api.platform.ComponentContainer; | |||
import org.sonar.api.platform.PluginMetadata; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.platform.PluginRepository; | |||
import javax.annotation.Nullable; | |||
import java.util.List; | |||
import java.util.Map; | |||
public class ExtensionInstaller { | |||
private final BatchPluginRepository pluginRepository; | |||
private final PluginRepository pluginRepository; | |||
private final EnvironmentInformation env; | |||
private final DefaultAnalysisMode analysisMode; | |||
public ExtensionInstaller(BatchPluginRepository pluginRepository, EnvironmentInformation env, DefaultAnalysisMode analysisMode) { | |||
public ExtensionInstaller(PluginRepository pluginRepository, EnvironmentInformation env, DefaultAnalysisMode analysisMode) { | |||
this.pluginRepository = pluginRepository; | |||
this.env = env; | |||
this.analysisMode = analysisMode; | |||
@@ -50,11 +50,10 @@ public class ExtensionInstaller { | |||
} | |||
// plugin extensions | |||
for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) { | |||
PluginMetadata metadata = entry.getKey(); | |||
Plugin plugin = entry.getValue(); | |||
for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { | |||
Plugin plugin = pluginRepository.getPluginInstance(pluginInfo.getKey()); | |||
for (Object extension : plugin.getExtensions()) { | |||
doInstall(container, matcher, metadata, extension); | |||
doInstall(container, matcher, pluginInfo, extension); | |||
} | |||
} | |||
List<ExtensionProvider> providers = container.getComponentsByType(ExtensionProvider.class); | |||
@@ -71,13 +70,13 @@ public class ExtensionInstaller { | |||
return this; | |||
} | |||
private void doInstall(ComponentContainer container, ExtensionMatcher matcher, @Nullable PluginMetadata metadata, Object extension) { | |||
private void doInstall(ComponentContainer container, ExtensionMatcher matcher, @Nullable PluginInfo pluginInfo, Object extension) { | |||
if (ExtensionUtils.supportsEnvironment(extension, env) | |||
&& (analysisMode.isDb() || !ExtensionUtils.requiresDB(extension)) | |||
&& matcher.accept(extension)) { | |||
container.addExtension(metadata, extension); | |||
container.addExtension(pluginInfo, extension); | |||
} else { | |||
container.declareExtension(metadata, extension); | |||
container.declareExtension(pluginInfo, extension); | |||
} | |||
} | |||