aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-07-31 17:09:13 +0200
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2015-08-06 14:20:48 +0200
commitd2599d66acb0df2b9a9be95dfbd09e396f54310b (patch)
treef56a6e2e415daeae18a21158c506d7ab57f4ac2d /sonar-core
parent1afd5f707409534390890118bec1d7d8ee02896e (diff)
downloadsonarqube-d2599d66acb0df2b9a9be95dfbd09e396f54310b.tar.gz
sonarqube-d2599d66acb0df2b9a9be95dfbd09e396f54310b.zip
SONAR-6749 add serverExtension support to Plugin class loaders
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderDef.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java5
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java23
-rw-r--r--sonar-core/src/test/java/org/sonar/classloader/MaskReader.java41
-rw-r--r--sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java29
5 files changed, 101 insertions, 7 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderDef.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderDef.java
index 9939c1a1c42..a78403fa49e 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderDef.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderDef.java
@@ -46,6 +46,8 @@ class PluginClassloaderDef {
*/
private boolean compatibilityMode = false;
+ private boolean serverExtension = false;
+
PluginClassloaderDef(String basePluginKey) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(basePluginKey));
this.basePluginKey = basePluginKey;
@@ -93,6 +95,14 @@ class PluginClassloaderDef {
this.compatibilityMode = b;
}
+ boolean isServerExtension() {
+ return serverExtension;
+ }
+
+ void setServerExtension(boolean serverExtension) {
+ this.serverExtension = serverExtension;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java
index f321e2bab72..3750e9e925a 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java
@@ -65,9 +65,9 @@ public class PluginClassloaderFactory {
public Map<PluginClassloaderDef, ClassLoader> create(Collection<PluginClassloaderDef> defs) {
ClassloaderBuilder builder = new ClassloaderBuilder();
builder.newClassloader(API_CLASSLOADER_KEY, baseClassloader());
- builder.setMask(API_CLASSLOADER_KEY, apiMask());
for (PluginClassloaderDef def : defs) {
+ builder.setMask(API_CLASSLOADER_KEY, def.isServerExtension() ? new Mask() : apiMask());
builder.newClassloader(def.getBasePluginKey());
builder.setParent(def.getBasePluginKey(), API_CLASSLOADER_KEY, new Mask());
builder.setLoadingOrder(def.getBasePluginKey(), def.isSelfFirstStrategy() ? SELF_FIRST : PARENT_FIRST);
@@ -145,7 +145,7 @@ public class PluginClassloaderFactory {
*/
private static Mask apiMask() {
return new Mask()
- .addInclusion("org/sonar/api/")
+ .addInclusion("org/sonar/api/")
.addInclusion("org/sonar/channel/")
.addInclusion("org/sonar/check/")
.addInclusion("org/sonar/colorizer/")
@@ -169,6 +169,7 @@ public class PluginClassloaderFactory {
.addInclusion("org/sonar/server/platform/")
.addInclusion("org/sonar/core/persistence/")
.addInclusion("org/sonar/core/properties/")
+ .addInclusion("org/sonar/server/views/")
// API exclusions
.addExclusion("org/sonar/api/internal/");
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java
index c3a28428141..3428358e6fd 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java
@@ -21,10 +21,12 @@ package org.sonar.core.platform;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
import java.io.Closeable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.lang.SystemUtils;
import org.sonar.api.Plugin;
import org.sonar.api.utils.log.Loggers;
@@ -44,11 +46,17 @@ import static java.util.Arrays.asList;
* Plugins have their own isolated classloader, inheriting only from API classes.
* Some plugins can extend a "base" plugin, sharing the same classloader.
* <p/>
- * This class is stateless. It does not keep pointers to classloaders and {@link Plugin}.
+ * This class is stateless. It does not keep pointers to classloaders and {@link org.sonar.api.SonarPlugin}.
*/
public class PluginLoader {
private static final String[] DEFAULT_SHARED_RESOURCES = {"org/sonar/plugins", "com/sonar/plugins", "com/sonarsource/plugins"};
+ /**
+ * Defines the base keys (defined by {@link #basePluginKey(PluginInfo, Map)}) of the plugins which are allowed to
+ * run a full server extensions.
+ */
+ private static final Set<String> SYSTEM_EXTENSION_PLUGINS_BASE_KEYS = ImmutableSet.of("views");
+
public static final Version COMPATIBILITY_MODE_MAX_VERSION = Version.create("5.2");
private final PluginJarExploder jarExploder;
@@ -85,9 +93,9 @@ public class PluginLoader {
def.addFiles(explodedPlugin.getLibs());
def.addMainClass(info.getKey(), info.getMainClass());
- for (String defaultSharedResource : DEFAULT_SHARED_RESOURCES) {
- def.getExportMask().addInclusion(String.format("%s/%s/api/", defaultSharedResource, info.getKey()));
- }
+ for (String defaultSharedResource : DEFAULT_SHARED_RESOURCES) {
+ def.getExportMask().addInclusion(String.format("%s/%s/api/", defaultSharedResource, info.getKey()));
+ }
// The plugins that extend other plugins can only add some files to classloader.
// They can't change metadata like ordering strategy or compatibility mode.
@@ -96,6 +104,7 @@ public class PluginLoader {
Version minSqVersion = info.getMinimalSqVersion();
boolean compatibilityMode = minSqVersion != null && minSqVersion.compareToIgnoreQualifier(COMPATIBILITY_MODE_MAX_VERSION) < 0;
def.setCompatibilityMode(compatibilityMode);
+ def.setServerExtension(isServerExtension(baseKey));
if (compatibilityMode) {
Loggers.get(getClass()).debug("API compatibility mode is enabled on plugin {} [{}] " +
"(built with API lower than {})",
@@ -106,8 +115,12 @@ public class PluginLoader {
return classloadersByBasePlugin.values();
}
+ private static boolean isServerExtension(String basePluginKey) {
+ return SYSTEM_EXTENSION_PLUGINS_BASE_KEYS.contains(basePluginKey);
+ }
+
/**
- * Instantiates collection of ({@link Plugin} according to given metadata and classloaders
+ * Instantiates collection of {@link org.sonar.api.SonarPlugin} according to given metadata and classloaders
*
* @return the instances grouped by plugin key
* @throws IllegalStateException if at least one plugin can't be correctly loaded
diff --git a/sonar-core/src/test/java/org/sonar/classloader/MaskReader.java b/sonar-core/src/test/java/org/sonar/classloader/MaskReader.java
new file mode 100644
index 00000000000..2df4ca9c57f
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/classloader/MaskReader.java
@@ -0,0 +1,41 @@
+/*
+ * 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.classloader;
+
+import java.util.List;
+
+/**
+ * Gives access to protected read methods of {@link Mask}.
+ */
+public class MaskReader {
+ private final Mask mask;
+
+ public MaskReader(Mask mask) {
+ this.mask = mask;
+ }
+
+ public List<String> getInclusions() {
+ return mask.getInclusions();
+ }
+
+ public List<String> getExclusions() {
+ return mask.getExclusions();
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java b/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java
index fbeae3a2483..3e4a2634740 100644
--- a/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java
@@ -21,6 +21,7 @@ package org.sonar.core.platform;
import com.google.common.collect.ImmutableMap;
import java.io.File;
+import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -146,6 +147,34 @@ public class PluginLoaderTest {
// TODO test mask - require change in sonar-classloader
}
+ @Test
+ public void plugin_is_recognised_as_server_extension_if_key_is_views_and_extends_no_other_plugin_and_runs_in_compatibility_mode() throws IOException {
+ PluginInfo views = create52PluginInfo("views");
+
+ Collection<PluginClassloaderDef> defs = loader.defineClassloaders(ImmutableMap.of("views", views));
+
+ assertThat(defs.iterator().next().isServerExtension()).isTrue();
+ }
+
+ @Test
+ public void plugin_is_not_recognised_as_system_extension_if_key_is_views_and_extends_another_plugin() throws IOException {
+ PluginInfo foo = create52PluginInfo("foo");
+ PluginInfo views = create52PluginInfo("views")
+ .setBasePlugin("foo");
+
+ Collection<PluginClassloaderDef> defs = loader.defineClassloaders(ImmutableMap.of("foo", foo, "views", views));
+
+ assertThat(defs).extracting("compatibilityMode").containsOnly(false, false);
+ }
+
+ private PluginInfo create52PluginInfo(String pluginKey) throws IOException {
+ File jarFile = temp.newFile();
+ return new PluginInfo(pluginKey)
+ .setJarFile(jarFile)
+ .setMainClass("org.foo." + pluginKey + "Plugin")
+ .setMinimalSqVersion(Version.create("5.2"));
+ }
+
/**
* Does not unzip jar file. It directly returns the JAR file defined on PluginInfo.
*/