Browse Source

SONAR-6731 Fix loading of plugins by CE when running on MS Windows

tags/5.5-M11
Simon Brandhof 8 years ago
parent
commit
067f651cd3

+ 60
- 0
server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java View File

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

+ 120
- 0
server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java View File

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

+ 2
- 4
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java View File

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


+ 123
- 0
server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java View File

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

+ 159
- 0
server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java View File

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

}
}
}

+ 7
- 0
server/sonar-ce/src/test/plugins/.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-ce/src/test/plugins/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.

+ 13
- 0
server/sonar-ce/src/test/plugins/pom.xml View File

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

+ 36
- 0
server/sonar-ce/src/test/plugins/sonar-test-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.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>

+ 11
- 0
server/sonar-ce/src/test/plugins/sonar-test-plugin/src/TestPlugin.java View File

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

BIN
server/sonar-ce/src/test/plugins/sonar-test-plugin/target/sonar-test-plugin-0.1-SNAPSHOT.jar View File


+ 36
- 0
server/sonar-ce/src/test/plugins/sonar-test2-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.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>

+ 11
- 0
server/sonar-ce/src/test/plugins/sonar-test2-plugin/src/Test2Plugin.java View File

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

BIN
server/sonar-ce/src/test/plugins/sonar-test2-plugin/target/sonar-test2-plugin-0.1-SNAPSHOT.jar View File


+ 16
- 5
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java View File

@@ -23,6 +23,7 @@ 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.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import java.io.File;
@@ -34,6 +35,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import org.apache.commons.io.FileUtils;
import org.picocontainer.Startable;
@@ -49,6 +51,8 @@ import org.sonar.core.platform.PluginRepository;
import org.sonar.server.platform.DefaultServerFileSystem;
import org.sonar.updatecenter.common.Version;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
@@ -77,11 +81,13 @@ public class ServerPluginRepository implements PluginRepository, Startable {
// List of plugins that should prevent the server to finish its startup
private static final Set<String> FORBIDDEN_COMPATIBLE_PLUGINS = ImmutableSet.of("sqale", "report", "views");
private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls();
private static final String NOT_STARTED_YET = "not started yet";

private final Server server;
private final DefaultServerFileSystem fs;
private final ServerUpgradeStatus upgradeStatus;
private final PluginLoader loader;
private final AtomicBoolean started = new AtomicBoolean(false);
private Set<String> blacklistedPluginKeys = DEFAULT_BLACKLISTED_PLUGINS;

// following fields are available after startup
@@ -109,15 +115,16 @@ public class ServerPluginRepository implements PluginRepository, Startable {
unloadIncompatiblePlugins();
logInstalledPlugins();
loadInstances();
started.set(true);
}

@Override
public void stop() {
// close classloaders
loader.unload(pluginInstancesByKeys.values());

pluginInstancesByKeys.clear();
pluginInfosByKeys.clear();
started.set(true);
}

/**
@@ -285,6 +292,8 @@ public class ServerPluginRepository implements PluginRepository, Startable {
* Uninstall a plugin and its dependents
*/
public void uninstall(String pluginKey) {
checkState(started.get(), NOT_STARTED_YET);

Set<String> uninstallKeys = new HashSet<>();
uninstallKeys.add(pluginKey);
appendDependentPluginKeys(pluginKey, uninstallKeys);
@@ -342,11 +351,13 @@ public class ServerPluginRepository implements PluginRepository, Startable {

@Override
public Collection<PluginInfo> getPluginInfos() {
return pluginInfosByKeys.values();
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));
@@ -356,15 +367,15 @@ public class ServerPluginRepository implements PluginRepository, Startable {

@Override
public Plugin getPluginInstance(String key) {
checkState(started.get(), NOT_STARTED_YET);
Plugin plugin = pluginInstancesByKeys.get(key);
if (plugin == null) {
throw new IllegalArgumentException(format("Plugin [%s] does not exist", 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);
}


+ 40
- 7
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java View File

@@ -50,7 +50,7 @@ import static org.mockito.Mockito.when;
public class ServerPluginRepositoryTest {

@Rule
public ExpectedException thrown = ExpectedException.none();
public ExpectedException expectedException = ExpectedException.none();

@Rule
public TemporaryFolder temp = new TemporaryFolder();
@@ -291,6 +291,7 @@ public class ServerPluginRepositoryTest {

@Test
public void fail_to_get_missing_plugins() {
underTest.start();
try {
underTest.getPluginInfo("unknown");
fail();
@@ -317,8 +318,8 @@ public class ServerPluginRepositoryTest {
public void fail_when_views_is_installed() throws Exception {
copyTestPluginTo("fake-views-plugin", fs.getInstalledPluginsDir());

thrown.expect(MessageException.class);
thrown.expectMessage("Plugin 'views' is no more compatible with this version of SonarQube");
expectedException.expect(MessageException.class);
expectedException.expectMessage("Plugin 'views' is no more compatible with this version of SonarQube");
underTest.start();
}

@@ -326,8 +327,8 @@ public class ServerPluginRepositoryTest {
public void fail_when_sqale_plugin_is_installed() throws Exception {
copyTestPluginTo("fake-sqale-plugin", fs.getInstalledPluginsDir());

thrown.expect(MessageException.class);
thrown.expectMessage("Plugin 'sqale' is no more compatible with this version of SonarQube");
expectedException.expect(MessageException.class);
expectedException.expectMessage("Plugin 'sqale' is no more compatible with this version of SonarQube");
underTest.start();
}

@@ -335,8 +336,8 @@ public class ServerPluginRepositoryTest {
public void fail_when_report_is_installed() throws Exception {
copyTestPluginTo("fake-report-plugin", fs.getInstalledPluginsDir());

thrown.expect(MessageException.class);
thrown.expectMessage("Plugin 'report' is no more compatible with this version of SonarQube");
expectedException.expect(MessageException.class);
expectedException.expectMessage("Plugin 'report' is no more compatible with this version of SonarQube");
underTest.start();
}

@@ -352,6 +353,38 @@ public class ServerPluginRepositoryTest {
assertThat(ServerPluginRepository.isCompatible(plugin, server, plugins)).isTrue();
}

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

Loading…
Cancel
Save