Browse Source

SONAR-6370 isolate plugin classloader from core

tags/5.2-RC1
Simon Brandhof 9 years ago
parent
commit
14a5c982e5
100 changed files with 1760 additions and 1351 deletions
  1. 0
    3
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/package-info.java
  2. 0
    3
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/package-info.java
  3. 0
    3
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/notifications/alerts/package-info.java
  4. 0
    3
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/package-info.java
  5. 0
    3
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/package-info.java
  6. 5
    5
      pom.xml
  7. 12
    0
      server/sonar-server/pom.xml
  8. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java
  9. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java
  10. 10
    12
      server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java
  11. 7
    18
      server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
  12. 6
    5
      server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
  13. 15
    17
      server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java
  14. 6
    6
      server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
  15. 15
    11
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java
  16. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java
  17. 12
    12
      server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java
  18. 13
    9
      server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java
  19. 23
    20
      server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java
  20. 0
    62
      server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarInstaller.java
  21. 0
    286
      server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarsInstaller.java
  22. 320
    35
      server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
  23. 65
    0
      server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginUnzipper.java
  24. 6
    8
      server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java
  25. 5
    5
      server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java
  26. 13
    12
      server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java
  27. 14
    15
      server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java
  28. 16
    12
      server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java
  29. 7
    27
      server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java
  30. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java
  31. 6
    9
      server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java
  32. 20
    19
      server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
  33. 6
    8
      server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java
  34. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java
  35. 0
    14
      server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java
  36. 3
    3
      server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java
  37. 23
    37
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java
  38. 12
    14
      server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java
  39. 6
    0
      server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java
  40. 11
    20
      server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java
  41. 6
    12
      server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java
  42. 0
    309
      server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java
  43. 274
    23
      server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java
  44. 64
    0
      server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginUnzipperTest.java
  45. 13
    22
      server/sonar-server/src/test/java/org/sonar/server/plugins/TestProjectUtils.java
  46. 4
    4
      server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java
  47. 24
    24
      server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java
  48. 15
    15
      server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java
  49. 6
    7
      server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java
  50. 7
    14
      server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java
  51. 9
    14
      server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java
  52. 7
    0
      server/sonar-server/src/test/projects/.gitignore
  53. 3
    0
      server/sonar-server/src/test/projects/README.txt
  54. 19
    0
      server/sonar-server/src/test/projects/pom.xml
  55. 36
    0
      server/sonar-server/src/test/projects/test-base-plugin-v2/pom.xml
  56. 11
    0
      server/sonar-server/src/test/projects/test-base-plugin-v2/src/BasePlugin.java
  57. BIN
      server/sonar-server/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar
  58. 36
    0
      server/sonar-server/src/test/projects/test-base-plugin/pom.xml
  59. 11
    0
      server/sonar-server/src/test/projects/test-base-plugin/src/BasePlugin.java
  60. BIN
      server/sonar-server/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar
  61. 36
    0
      server/sonar-server/src/test/projects/test-core-plugin/pom.xml
  62. 11
    0
      server/sonar-server/src/test/projects/test-core-plugin/src/CorePlugin.java
  63. BIN
      server/sonar-server/src/test/projects/test-core-plugin/target/test-core-plugin-0.1-SNAPSHOT.jar
  64. 37
    0
      server/sonar-server/src/test/projects/test-extend-plugin/pom.xml
  65. 11
    0
      server/sonar-server/src/test/projects/test-extend-plugin/src/ExtendPlugin.java
  66. BIN
      server/sonar-server/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar
  67. 49
    0
      server/sonar-server/src/test/projects/test-libs-plugin/pom.xml
  68. 11
    0
      server/sonar-server/src/test/projects/test-libs-plugin/src/LibsPlugin.java
  69. BIN
      server/sonar-server/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar
  70. 37
    0
      server/sonar-server/src/test/projects/test-require-plugin/pom.xml
  71. 11
    0
      server/sonar-server/src/test/projects/test-require-plugin/src/RequirePlugin.java
  72. BIN
      server/sonar-server/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar
  73. 37
    0
      server/sonar-server/src/test/projects/test-requirenew-plugin/pom.xml
  74. 11
    0
      server/sonar-server/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java
  75. BIN
      server/sonar-server/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar
  76. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/extension.jar
  77. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/foo-plugin.jar
  78. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/sonar-build-breaker-plugin-0.1.jar
  79. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginDownloaderTest/foo-plugin-1.0.jar
  80. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version1/extension.jar
  81. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version2/extension.jar
  82. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-1.0.jar
  83. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-2.0.jar
  84. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/not-a-plugin.jar
  85. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/old-plugin.jar
  86. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataTest/foo-plugin.jar
  87. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/bar-plugin-1.0.jar
  88. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-1.0.jar
  89. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-2.0.jar
  90. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/not-a-plugin.jar
  91. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/require-sq-2.5.jar
  92. BIN
      server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar
  93. 3
    3
      server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb
  94. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
  95. 47
    23
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java
  96. 121
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginPredicate.java
  97. 39
    144
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
  98. 77
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginUnzipper.java
  99. 11
    12
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
  100. 0
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java

+ 0
- 3
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/package-info.java View File

@@ -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;


+ 0
- 3
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/package-info.java View File

@@ -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;


+ 0
- 3
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/notifications/alerts/package-info.java View File

@@ -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;


+ 0
- 3
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/package-info.java View File

@@ -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;


+ 0
- 3
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/package-info.java View File

@@ -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;


+ 5
- 5
pom.xml View File

@@ -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>

+ 12
- 0
server/sonar-server/pom.xml View File

@@ -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>

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java View File

@@ -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;

+ 3
- 3
server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java View File

@@ -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;

+ 10
- 12
server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java View File

@@ -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);
}
}
}

+ 7
- 18
server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java View File

@@ -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));

+ 6
- 5
server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java View File

@@ -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
*/

+ 15
- 17
server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java View File

@@ -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

+ 6
- 6
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java View File

@@ -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

+ 15
- 11
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java View File

@@ -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();
}
}));
});
}
}

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/plugins/InstalledPluginReferentialFactory.java View File

@@ -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());
}

}

+ 12
- 12
server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java View File

@@ -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);
}
}

+ 13
- 9
server/sonar-server/src/main/java/org/sonar/server/plugins/PluginReferentialMetadataConverter.java View File

@@ -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;
}
}

+ 23
- 20
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java View File

@@ -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;

+ 0
- 62
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarInstaller.java View File

@@ -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");
}
}
}

+ 0
- 286
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarsInstaller.java View File

@@ -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();
}
}
}

+ 320
- 35
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java View File

@@ -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();
}
}

+ 65
- 0
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginUnzipper.java View File

@@ -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);
}
}
}

+ 6
- 8
server/sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java View File

@@ -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);

+ 5
- 5
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/CancelAllPluginsWsAction.java View File

@@ -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();
}

+ 13
- 12
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java View File

@@ -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();
}
}

+ 14
- 15
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PendingPluginsWsAction.java View File

@@ -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();

+ 16
- 12
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java View File

@@ -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();
}
}

+ 7
- 27
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java View File

@@ -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));
}
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/search/IndexQueue.java View File

@@ -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;

+ 6
- 9
server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java View File

@@ -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();

+ 20
- 19
server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java View File

@@ -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) {

+ 6
- 8
server/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java View File

@@ -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

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java View File

@@ -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;


+ 0
- 14
server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java View File

@@ -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(

+ 3
- 3
server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java View File

@@ -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");

+ 23
- 37
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java View File

@@ -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");
}
}

+ 12
- 14
server/sonar-server/src/test/java/org/sonar/server/plugins/InstalledPluginReferentialFactoryTest.java View File

@@ -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();
}
}

+ 6
- 0
server/sonar-server/src/test/java/org/sonar/server/plugins/MimeTypesTest.java View File

@@ -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();
}
}

+ 11
- 20
server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java View File

@@ -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);


+ 6
- 12
server/sonar-server/src/test/java/org/sonar/server/plugins/PluginReferentialMetadataConverterTest.java View File

@@ -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);
}

+ 0
- 309
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java View File

@@ -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();
}
}

+ 274
- 23
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java View File

@@ -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());
}
}

+ 64
- 0
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginUnzipperTest.java View File

@@ -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());
}
}
}

sonar-core/src/main/java/org/sonar/core/plugins/ResourcesClassloader.java → server/sonar-server/src/test/java/org/sonar/server/plugins/TestProjectUtils.java View File

@@ -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();
}
}

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/CancelAllPluginsWsActionTest.java View File

@@ -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();
}


+ 24
- 24
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java View File

@@ -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"));
}
}

+ 15
- 15
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PendingPluginsWsActionTest.java View File

@@ -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,

+ 6
- 7
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java View File

@@ -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);

+ 7
- 14
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java View File

@@ -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();
}


+ 9
- 14
server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java View File

@@ -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"));
}
}

+ 7
- 0
server/sonar-server/src/test/projects/.gitignore View File

@@ -0,0 +1,7 @@
# see README.txt
!*/target/
*/target/classes/
*/target/maven-archiver/
*/target/maven-status/
*/target/test-*/


+ 3
- 0
server/sonar-server/src/test/projects/README.txt View File

@@ -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.

+ 19
- 0
server/sonar-server/src/test/projects/pom.xml View File

@@ -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>

+ 36
- 0
server/sonar-server/src/test/projects/test-base-plugin-v2/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-base-plugin-v2/src/BasePlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar View File


+ 36
- 0
server/sonar-server/src/test/projects/test-base-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-base-plugin/src/BasePlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar View File


+ 36
- 0
server/sonar-server/src/test/projects/test-core-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-core-plugin/src/CorePlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-core-plugin/target/test-core-plugin-0.1-SNAPSHOT.jar View File


+ 37
- 0
server/sonar-server/src/test/projects/test-extend-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-extend-plugin/src/ExtendPlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar View File


+ 49
- 0
server/sonar-server/src/test/projects/test-libs-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-libs-plugin/src/LibsPlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar View File


+ 37
- 0
server/sonar-server/src/test/projects/test-require-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-require-plugin/src/RequirePlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar View File


+ 37
- 0
server/sonar-server/src/test/projects/test-requirenew-plugin/pom.xml View File

@@ -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>

+ 11
- 0
server/sonar-server/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java View File

@@ -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();
}
}

BIN
server/sonar-server/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/extension.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/foo-plugin.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginClassLoadersTest/sonar-build-breaker-plugin-0.1.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginDownloaderTest/foo-plugin-1.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version1/extension.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginExtensionMetadataTest/version2/extension.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-1.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/foo-plugin-2.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/not-a-plugin.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataLoaderTest/old-plugin.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/PluginMetadataTest/foo-plugin.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/bar-plugin-1.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-1.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/foo-plugin-2.0.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/not-a-plugin.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginJarsInstallerTest/require-sq-2.5.jar View File


BIN
server/sonar-server/src/test/resources/org/sonar/server/plugins/ServerPluginRepositoryTest/sonar-artifact-size-plugin-0.2.jar View File


+ 3
- 3
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/updatecenter_controller.rb View File

@@ -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

+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java View File

@@ -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;

sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsRepository.java → sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java View File

@@ -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();
}

}

+ 121
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginPredicate.java View File

@@ -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));
}
}

+ 39
- 144
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java View File

@@ -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);
}
}

+ 77
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginUnzipper.java View File

@@ -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;
}
}

+ 11
- 12
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java View File

@@ -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);
}
}


+ 0
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save