aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2016-03-23 14:35:41 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2016-03-24 14:54:02 +0100
commit067f651cd32f86f543296262729bd6db665e1f24 (patch)
treea39e4522582832b5a142b41eccf168999f45cf04 /server/sonar-ce
parent0d6edb21c09e328980a272dd21a37befa49be92a (diff)
downloadsonarqube-067f651cd32f86f543296262729bd6db665e1f24.tar.gz
sonarqube-067f651cd32f86f543296262729bd6db665e1f24.zip
SONAR-6731 Fix loading of plugins by CE when running on MS Windows
Diffstat (limited to 'server/sonar-ce')
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java60
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java120
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java6
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java123
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java159
-rw-r--r--server/sonar-ce/src/test/plugins/.gitignore7
-rw-r--r--server/sonar-ce/src/test/plugins/README.txt3
-rw-r--r--server/sonar-ce/src/test/plugins/pom.xml13
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test-plugin/pom.xml36
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test-plugin/src/TestPlugin.java11
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jarbin0 -> 2480 bytes
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test2-plugin/pom.xml36
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test2-plugin/src/Test2Plugin.java11
-rw-r--r--server/sonar-ce/src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jarbin0 -> 2492 bytes
14 files changed, 581 insertions, 4 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java
new file mode 100644
index 00000000000..a9d64dd3c37
--- /dev/null
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.ce.container;
+
+import java.io.File;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.platform.ServerFileSystem;
+import org.sonar.api.utils.ZipUtils;
+import org.sonar.core.platform.ExplodedPlugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginJarExploder;
+
+/**
+ * Explodes the plugin JARs of extensions/plugins/ into a temporary directory
+ * dedicated to compute engine.
+ */
+public class CePluginJarExploder extends PluginJarExploder {
+
+ private static final String TEMP_RELATIVE_PATH = "ce-exploded-plugins";
+ private final ServerFileSystem fs;
+
+ public CePluginJarExploder(ServerFileSystem fs) {
+ this.fs = fs;
+ }
+
+ @Override
+ public ExplodedPlugin explode(PluginInfo pluginInfo) {
+ File tempDir = new File(fs.getTempDir(), TEMP_RELATIVE_PATH);
+ File toDir = new File(tempDir, pluginInfo.getKey());
+ try {
+ org.sonar.core.util.FileUtils.cleanDirectory(toDir);
+
+ File jarSource = pluginInfo.getNonNullJarFile();
+ File jarTarget = new File(toDir, jarSource.getName());
+ FileUtils.copyFile(jarSource, jarTarget);
+ ZipUtils.unzip(jarSource, toDir, newLibFilter());
+ return explodeFromUnzippedDir(pluginInfo.getKey(), jarTarget, toDir);
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format(
+ "Fail to unzip plugin [%s] %s to %s", pluginInfo.getKey(), pluginInfo.getNonNullJarFile().getAbsolutePath(), toDir.getAbsolutePath()), e);
+ }
+ }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java
new file mode 100644
index 00000000000..0f24f3d6b1d
--- /dev/null
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java
@@ -0,0 +1,120 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.ce.container;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.commons.io.FileUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.Plugin;
+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 static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+/**
+ * Entry point to load plugins on startup. It assumes that plugins
+ * have been correctly installed/uninstalled/updated during web server startup
+ */
+public class CePluginRepository implements PluginRepository, Startable {
+
+ private static final String[] JAR_FILE_EXTENSIONS = new String[] {"jar"};
+ private static final String NOT_STARTED_YET = "not started yet";
+
+ private final DefaultServerFileSystem fs;
+ private final PluginLoader loader;
+ private final AtomicBoolean started = new AtomicBoolean(false);
+
+ // following fields are available after startup
+ private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>();
+ private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>();
+
+ public CePluginRepository(DefaultServerFileSystem fs, PluginLoader loader) {
+ this.fs = fs;
+ this.loader = loader;
+ }
+
+ @Override
+ public void start() {
+ Loggers.get(getClass()).info("Load plugins");
+ for (File file : listJarFiles(fs.getInstalledPluginsDir())) {
+ PluginInfo info = PluginInfo.create(file);
+ pluginInfosByKeys.put(info.getKey(), info);
+ }
+ pluginInstancesByKeys.putAll(loader.load(pluginInfosByKeys));
+ started.set(true);
+ }
+
+ @Override
+ public void stop() {
+ // close classloaders
+ loader.unload(pluginInstancesByKeys.values());
+ pluginInstancesByKeys.clear();
+ pluginInfosByKeys.clear();
+ started.set(false);
+ }
+
+ @Override
+ public Collection<PluginInfo> getPluginInfos() {
+ checkState(started.get(), NOT_STARTED_YET);
+ return ImmutableList.copyOf(pluginInfosByKeys.values());
+ }
+
+ @Override
+ public PluginInfo getPluginInfo(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ PluginInfo info = pluginInfosByKeys.get(key);
+ if (info == null) {
+ throw new IllegalArgumentException(format("Plugin [%s] does not exist", key));
+ }
+ return info;
+ }
+
+ @Override
+ public Plugin getPluginInstance(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ Plugin plugin = pluginInstancesByKeys.get(key);
+ checkArgument(plugin != null, "Plugin [%s] does not exist", key);
+ return plugin;
+ }
+
+ @Override
+ public boolean hasPlugin(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ return pluginInfosByKeys.containsKey(key);
+ }
+
+ private static Collection<File> listJarFiles(File dir) {
+ if (dir.exists()) {
+ return FileUtils.listFiles(dir, JAR_FILE_EXTENSIONS, false);
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
index 7354d63978c..42527bf4e8b 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
@@ -110,8 +110,6 @@ import org.sonar.server.platform.ServerLogging;
import org.sonar.server.platform.TempFolderProvider;
import org.sonar.server.plugins.InstalledPluginReferentialFactory;
import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.ServerPluginJarExploder;
-import org.sonar.server.plugins.ServerPluginRepository;
import org.sonar.server.properties.ProjectSettingsFactory;
import org.sonar.server.qualityprofile.BuiltInProfiles;
import org.sonar.server.qualityprofile.QProfileComparison;
@@ -187,9 +185,9 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
// plugins
PluginClassloaderFactory.class,
- ServerPluginJarExploder.class,
+ CePluginJarExploder.class,
PluginLoader.class,
- ServerPluginRepository.class,
+ CePluginRepository.class,
InstalledPluginReferentialFactory.class,
ServerExtensionInstaller.class,
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java
new file mode 100644
index 00000000000..ae7dcac8378
--- /dev/null
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java
@@ -0,0 +1,123 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.ce.container;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.platform.ServerFileSystem;
+import org.sonar.core.platform.ExplodedPlugin;
+import org.sonar.core.platform.PluginInfo;
+
+import static org.apache.commons.io.FileUtils.sizeOfDirectory;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CePluginJarExploderTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ DumbFileSystem fs = new DumbFileSystem(temp);
+ CePluginJarExploder underTest = new CePluginJarExploder(fs);
+
+ @Test
+ public void explode_jar_to_temp_directory() throws Exception {
+ PluginInfo info = PluginInfo.create(plugin1Jar());
+
+ ExplodedPlugin exploded = underTest.explode(info);
+
+ // all the files loaded by classloaders (JAR + META-INF/libs/*.jar) are copied to a dedicated temp directory
+ File copiedJar = exploded.getMain();
+
+ assertThat(exploded.getKey()).isEqualTo("test");
+ assertThat(copiedJar).isFile().exists();
+ assertThat(copiedJar.getParentFile()).isDirectory().hasName("test");
+ assertThat(copiedJar.getParentFile().getParentFile()).isDirectory().hasName("ce-exploded-plugins");
+ }
+
+ @Test
+ public void plugins_do_not_overlap() throws Exception {
+ PluginInfo info1 = PluginInfo.create(plugin1Jar());
+ PluginInfo info2 = PluginInfo.create(plugin2Jar());
+
+ ExplodedPlugin exploded1 = underTest.explode(info1);
+ ExplodedPlugin exploded2 = underTest.explode(info2);
+
+ assertThat(exploded1.getKey()).isEqualTo("test");
+ assertThat(exploded1.getMain()).isFile().exists().hasName("sonar-test-plugin-0.1-SNAPSHOT.jar");
+ assertThat(exploded2.getKey()).isEqualTo("test2");
+ assertThat(exploded2.getMain()).isFile().exists().hasName("sonar-test2-plugin-0.1-SNAPSHOT.jar");
+ }
+
+ @Test
+ public void explode_is_reentrant() throws Exception {
+ PluginInfo info = PluginInfo.create(plugin1Jar());
+
+ ExplodedPlugin exploded1 = underTest.explode(info);
+ long dirSize1 = sizeOfDirectory(exploded1.getMain().getParentFile());
+
+ ExplodedPlugin exploded2 = underTest.explode(info);
+ long dirSize2 = sizeOfDirectory(exploded2.getMain().getParentFile());
+ assertThat(exploded2.getMain().getCanonicalPath()).isEqualTo(exploded1.getMain().getCanonicalPath());
+ assertThat(dirSize1).isEqualTo(dirSize2);
+ }
+
+ private File plugin1Jar() {
+ return new File("src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jar");
+ }
+
+ private File plugin2Jar() {
+ return new File("src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jar");
+ }
+
+ private class DumbFileSystem implements ServerFileSystem {
+ private final TemporaryFolder temp;
+ private File tempDir;
+
+ public DumbFileSystem(TemporaryFolder temp) {
+ this.temp = temp;
+ }
+
+ @Override
+ public File getHomeDir() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public File getTempDir() {
+ if (tempDir == null) {
+ try {
+ this.tempDir = temp.newFolder();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return tempDir;
+ }
+
+ @Override
+ public List<File> getExtensions(String dirName, String... suffixes) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java
new file mode 100644
index 00000000000..a7ebbc8eff3
--- /dev/null
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java
@@ -0,0 +1,159 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.ce.container;
+
+import com.google.common.collect.Maps;
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.sonar.api.Plugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginLoader;
+import org.sonar.server.platform.DefaultServerFileSystem;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CePluginRepositoryTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
+ PluginLoader pluginLoader = new DumbPluginLoader();
+ CePluginRepository underTest = new CePluginRepository(fs, pluginLoader);
+
+ @After
+ public void tearDown() {
+ underTest.stop();
+ }
+
+ @Test
+ public void empty_plugins() throws Exception {
+ // empty folder
+ when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder());
+
+ underTest.start();
+
+ assertThat(underTest.getPluginInfos()).isEmpty();
+ assertThat(underTest.hasPlugin("foo")).isFalse();
+ }
+
+ @Test
+ public void load_plugins() throws Exception {
+ String pluginKey = "test";
+ when(fs.getInstalledPluginsDir()).thenReturn(new File("src/test/plugins/sonar-test-plugin/target"));
+
+ underTest.start();
+
+ assertThat(underTest.getPluginInfos()).extracting("key").containsOnly(pluginKey);
+ assertThat(underTest.getPluginInfo(pluginKey).getKey()).isEqualTo(pluginKey);
+ assertThat(underTest.getPluginInstance(pluginKey)).isNotNull();
+ assertThat(underTest.hasPlugin(pluginKey)).isTrue();
+ }
+
+ @Test
+ public void getPluginInfo_fails_if_plugin_does_not_exist() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Plugin [foo] does not exist");
+
+ // empty folder
+ when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder());
+ underTest.start();
+ underTest.getPluginInfo("foo");
+ }
+
+ @Test
+ public void getPluginInstance_fails_if_plugin_does_not_exist() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Plugin [foo] does not exist");
+
+ // empty folder
+ when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder());
+ underTest.start();
+ underTest.getPluginInstance("foo");
+ }
+
+ @Test
+ public void getPluginInstance_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInstance("foo");
+ }
+
+ @Test
+ public void getPluginInfo_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInfo("foo");
+ }
+
+ @Test
+ public void hasPlugin_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.hasPlugin("foo");
+ }
+
+ @Test
+ public void getPluginInfos_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInfos();
+ }
+
+ private static class DumbPluginLoader extends PluginLoader {
+
+ public DumbPluginLoader() {
+ super(null, null);
+ }
+
+ /**
+ * Does nothing except returning the specified list of plugins
+ */
+ @Override
+ public Map<String, Plugin> load(Map<String, PluginInfo> infoByKeys) {
+ Map<String, Plugin> result = Maps.newHashMap();
+ for (String pluginKey : infoByKeys.keySet()) {
+ result.put(pluginKey, mock(Plugin.class));
+ }
+ return result;
+ }
+
+ @Override
+ public void unload(Collection<Plugin> plugins) {
+
+ }
+ }
+}
diff --git a/server/sonar-ce/src/test/plugins/.gitignore b/server/sonar-ce/src/test/plugins/.gitignore
new file mode 100644
index 00000000000..a945b8525e6
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/.gitignore
@@ -0,0 +1,7 @@
+# see README.txt
+!*/target/
+*/target/classes/
+*/target/maven-archiver/
+*/target/maven-status/
+*/target/test-*/
+
diff --git a/server/sonar-ce/src/test/plugins/README.txt b/server/sonar-ce/src/test/plugins/README.txt
new file mode 100644
index 00000000000..c53a66d52f2
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/README.txt
@@ -0,0 +1,3 @@
+This directory provides the fake plugins used by tests. These tests are rarely changed, so generated
+artifacts are stored in Git repository (see .gitignore). It avoids from adding unnecessary modules
+to build.
diff --git a/server/sonar-ce/src/test/plugins/pom.xml b/server/sonar-ce/src/test/plugins/pom.xml
new file mode 100644
index 00000000000..328eff0f237
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/pom.xml
@@ -0,0 +1,13 @@
+<?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.sonarsource.sonarqube.tests</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>test-base-plugin</module>
+ </modules>
+
+</project>
diff --git a/server/sonar-ce/src/test/plugins/sonar-test-plugin/pom.xml b/server/sonar-ce/src/test/plugins/sonar-test-plugin/pom.xml
new file mode 100644
index 00000000000..d2efd359b4a
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test-plugin/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>sonar-test-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test 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.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>test</pluginKey>
+ <pluginClass>TestPlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/server/sonar-ce/src/test/plugins/sonar-test-plugin/src/TestPlugin.java b/server/sonar-ce/src/test/plugins/sonar-test-plugin/src/TestPlugin.java
new file mode 100644
index 00000000000..a1f4376adc8
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test-plugin/src/TestPlugin.java
@@ -0,0 +1,11 @@
+import org.sonar.api.SonarPlugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class TestPlugin extends SonarPlugin {
+
+ public List getExtensions() {
+ return Collections.emptyList();
+ }
+}
diff --git a/server/sonar-ce/src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jar b/server/sonar-ce/src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jar
new file mode 100644
index 00000000000..cc9bbd8f7bf
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jar
Binary files differ
diff --git a/server/sonar-ce/src/test/plugins/sonar-test2-plugin/pom.xml b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/pom.xml
new file mode 100644
index 00000000000..fc4ecfa8e27
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>sonar-test2-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test 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.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>test2</pluginKey>
+ <pluginClass>Test2Plugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/server/sonar-ce/src/test/plugins/sonar-test2-plugin/src/Test2Plugin.java b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/src/Test2Plugin.java
new file mode 100644
index 00000000000..3c790702b07
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/src/Test2Plugin.java
@@ -0,0 +1,11 @@
+import org.sonar.api.SonarPlugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Test2Plugin extends SonarPlugin {
+
+ public List getExtensions() {
+ return Collections.emptyList();
+ }
+}
diff --git a/server/sonar-ce/src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jar b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jar
new file mode 100644
index 00000000000..c205080f968
--- /dev/null
+++ b/server/sonar-ce/src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jar
Binary files differ