aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-task-projectanalysis/build.gradle3
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java2
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java16
-rw-r--r--server/sonar-server/build.gradle6
-rw-r--r--settings.gradle1
-rw-r--r--sonar-application/build.gradle4
-rw-r--r--sonar-core/build.gradle14
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginClassLoaderDef.java13
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginClassloaderFactory.java47
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java9
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/FileUtils.java2
-rw-r--r--sonar-core/src/test/java/org/sonar/core/platform/PluginClassloaderFactoryTest.java21
-rw-r--r--sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java52
-rw-r--r--sonar-duplications/build.gradle1
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/CodeFragment.java44
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/block/Block.java6
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/index/ClonePart.java6
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java3
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java1
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java (renamed from sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/TokensLine.java)8
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/statement/Statement.java5
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/PmdBlockChunkerTest.java6
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/TokenizerBridgeTest.java1
-rw-r--r--sonar-plugin-api-deps/build.gradle42
-rw-r--r--sonar-plugin-api/build.gradle3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/authentication/IdentityProvider.java2
-rw-r--r--sonar-scanner-engine/build.gradle1
29 files changed, 254 insertions, 70 deletions
diff --git a/server/sonar-ce-task-projectanalysis/build.gradle b/server/sonar-ce-task-projectanalysis/build.gradle
index b73d4b2e72b..8b52c4232f2 100644
--- a/server/sonar-ce-task-projectanalysis/build.gradle
+++ b/server/sonar-ce-task-projectanalysis/build.gradle
@@ -42,8 +42,7 @@ dependencies {
compileOnly project(':server:sonar-db-dao')
compileOnly project(':server:sonar-process')
compileOnly project(':server:sonar-server-common')
- compileOnly project(':sonar-plugin-api')
- compileOnly project(':sonar-duplications')
+ compileOnly project(path: ':sonar-plugin-api')
testCompile 'com.google.code.findbugs:jsr305'
testCompile 'com.h2database:h2'
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
index fc8243e4893..9f1328486fa 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
@@ -150,9 +150,7 @@ public class ComputeEngineContainerImplTest {
Properties properties = ProcessProperties.defaults();
File homeDir = tempFolder.newFolder();
File dataDir = new File(homeDir, "data");
- dataDir.mkdirs();
File tmpDir = new File(homeDir, "tmp");
- tmpDir.mkdirs();
properties.setProperty(PATH_HOME.getKey(), homeDir.getAbsolutePath());
properties.setProperty(PATH_DATA.getKey(), dataDir.getAbsolutePath());
properties.setProperty(PATH_TEMP.getKey(), tmpDir.getAbsolutePath());
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java b/server/sonar-server-common/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java
index d596044e8e3..9818d98584f 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java
@@ -20,8 +20,6 @@
package org.sonar.server.platform;
import java.io.File;
-import java.io.IOException;
-import org.apache.commons.io.FileUtils;
import org.picocontainer.Startable;
import org.sonar.api.config.Configuration;
import org.sonar.api.utils.log.Logger;
@@ -41,9 +39,9 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla
private final File uninstallDir;
public ServerFileSystemImpl(Configuration config) {
- this.homeDir = createDir(new File(config.get(PATH_HOME.getKey()).get()));
- this.tempDir = createDir(new File(config.get(PATH_TEMP.getKey()).get()));
- File dataDir = createDir(new File(config.get(PATH_DATA.getKey()).get()));
+ this.homeDir = new File(config.get(PATH_HOME.getKey()).get());
+ this.tempDir = new File(config.get(PATH_TEMP.getKey()).get());
+ File dataDir = new File(config.get(PATH_DATA.getKey()).get());
this.deployDir = new File(dataDir, "web/deploy");
this.uninstallDir = new File(getTempDir(), "uninstalled-plugins");
}
@@ -93,12 +91,4 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla
return uninstallDir;
}
- private static File createDir(File dir) {
- try {
- FileUtils.forceMkdir(dir);
- return dir;
- } catch (IOException e) {
- throw new IllegalStateException("Fail to create directory " + dir, e);
- }
- }
}
diff --git a/server/sonar-server/build.gradle b/server/sonar-server/build.gradle
index e5f7979f20b..1efcb4d3026 100644
--- a/server/sonar-server/build.gradle
+++ b/server/sonar-server/build.gradle
@@ -55,8 +55,10 @@ dependencies {
compile project(':server:sonar-server-common')
compile project(':sonar-core')
compile project(':sonar-scanner-protocol')
- compile project(':sonar-markdown')
- compile project(':sonar-duplications')
+ compile(project(':sonar-markdown')) {
+ // already shaded with sonar-plugin-api
+ exclude group: 'org.codehaus.sonar', module: 'sonar-channel'
+ }
runtime project(path: ':sonar-plugin-api', configuration: 'shadow')
compileOnly project(path: ':sonar-plugin-api')
compile project(':sonar-ws')
diff --git a/settings.gradle b/settings.gradle
index a07ccc2db72..e08017fd410 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -25,6 +25,7 @@ include 'sonar-core'
include 'sonar-duplications'
include 'sonar-markdown'
include 'sonar-plugin-api'
+include 'sonar-plugin-api-deps'
include 'sonar-scanner-engine'
include 'sonar-scanner-engine-shaded'
include 'sonar-scanner-protocol'
diff --git a/sonar-application/build.gradle b/sonar-application/build.gradle
index a943feb45f7..e50295de352 100644
--- a/sonar-application/build.gradle
+++ b/sonar-application/build.gradle
@@ -127,8 +127,8 @@ zip.doFirst {
}
// Check the size of the archive
zip.doLast {
- def minLength = 140000000
- def maxLength = 160000000
+ def minLength = 150000000
+ def maxLength = 170000000
def length = new File(distsDir, archiveName).length()
if (length < minLength)
throw new GradleException("$archiveName size ($length) too small. Min is $minLength")
diff --git a/sonar-core/build.gradle b/sonar-core/build.gradle
index aad84e6dbfe..a2d0f1bb189 100644
--- a/sonar-core/build.gradle
+++ b/sonar-core/build.gradle
@@ -4,6 +4,10 @@ sonarqube {
}
}
+configurations {
+ includeInResources
+}
+
dependencies {
// please keep list ordered
@@ -20,6 +24,8 @@ dependencies {
compileOnly 'com.google.code.findbugs:jsr305'
+ includeInResources project(path: ':sonar-plugin-api-deps', configuration: 'shadow')
+
testCompile 'com.tngtech.java:junit-dataprovider'
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
@@ -30,6 +36,14 @@ dependencies {
testCompileOnly 'com.google.code.findbugs:jsr305'
}
+// sonar-plugin-api.jar is copied into target JAR file
+processResources {
+ into('/') {
+ from configurations.includeInResources
+ rename '(.*)-' + project.version + '-all.jar', '$1.jar'
+ }
+}
+
// Used by sonar-db-core to run DB Unit Tests
artifactoryPublish.skip = false
publishing {
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 d228ee088bd..c1c3584b58a 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
@@ -41,6 +41,11 @@ class PluginClassLoaderDef {
private final Mask mask = new Mask();
private boolean selfFirstStrategy = false;
+ /**
+ * Compatibility with API classloader as defined before version 5.2
+ */
+ private boolean compatibilityMode = false;
+
PluginClassLoaderDef(String basePluginKey) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(basePluginKey));
this.basePluginKey = basePluginKey;
@@ -80,6 +85,14 @@ class PluginClassLoaderDef {
}
}
+ boolean isCompatibilityMode() {
+ return compatibilityMode;
+ }
+
+ void setCompatibilityMode(boolean b) {
+ this.compatibilityMode = b;
+ }
+
@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 bc584778d68..0c69f978a09 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
@@ -25,9 +25,11 @@ import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import org.apache.commons.io.FileUtils;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.TempFolder;
import org.sonar.classloader.ClassloaderBuilder;
import org.sonar.classloader.Mask;
@@ -38,6 +40,8 @@ import static org.sonar.classloader.ClassloaderBuilder.LoadingOrder.SELF_FIRST;
* Builds the graph of classloaders to be used to instantiate plugins. It deals with:
* <ul>
* <li>isolation of plugins against core classes (except api)</li>
+ * <li>backward-compatibility with plugins built for versions of SQ lower than 5.2. At that time
+ * API declared transitive dependencies that were automatically available to plugins</li>
* <li>sharing of some packages between plugins</li>
* <li>loading of the libraries embedded in plugin JAR files (directory META-INF/libs)</li>
* </ul>
@@ -50,6 +54,13 @@ public class PluginClassloaderFactory {
// underscores are used to not conflict with plugin keys (if someday a plugin key is "api")
private static final String API_CLASSLOADER_KEY = "_api_";
+ private final TempFolder temp;
+ private URL compatibilityModeJar;
+
+ public PluginClassloaderFactory(TempFolder temp) {
+ this.temp = temp;
+ }
+
/**
* Creates as many classloaders as requested by the input parameter.
*/
@@ -67,6 +78,9 @@ public class PluginClassloaderFactory {
for (File jar : def.getFiles()) {
builder.addURL(def.getBasePluginKey(), fileToUrl(jar));
}
+ if (def.isCompatibilityMode()) {
+ builder.addURL(def.getBasePluginKey(), extractCompatibilityModeJar());
+ }
exportResources(def, builder, defs);
}
@@ -106,6 +120,19 @@ public class PluginClassloaderFactory {
return getClass().getClassLoader();
}
+ private URL extractCompatibilityModeJar() {
+ if (compatibilityModeJar == null) {
+ File jar = temp.newFile("sonar-plugin-api-deps", "jar");
+ try {
+ FileUtils.copyURLToFile(getClass().getResource("/sonar-plugin-api-deps.jar"), jar);
+ compatibilityModeJar = jar.toURI().toURL();
+ } catch (Exception e) {
+ throw new IllegalStateException("Can not extract sonar-plugin-api-deps.jar to " + jar.getAbsolutePath(), e);
+ }
+ }
+ return compatibilityModeJar;
+ }
+
private static URL fileToUrl(File file) {
try {
return file.toURI().toURL();
@@ -122,25 +149,31 @@ 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/")
+ .addInclusion("org/sonar/duplications/")
+ .addInclusion("org/sonar/graph/")
+ .addInclusion("org/sonar/plugins/emailnotifications/api/")
+ .addInclusion("net/sourceforge/pmd/")
+ .addInclusion("org/apache/maven/")
.addInclusion("org/codehaus/stax2/")
.addInclusion("org/codehaus/staxmate/")
.addInclusion("com/ctc/wstx/")
.addInclusion("org/slf4j/")
+ .addInclusion("javax/servlet/")
// SLF4J bridges. Do not let plugins re-initialize and configure their logging system
.addInclusion("org/apache/commons/logging/")
.addInclusion("org/apache/log4j/")
.addInclusion("ch/qos/logback/")
- // Exposed by org.sonar.api.server.authentication.IdentityProvider
- .addInclusion("javax/servlet/")
-
- // required for some internal SonarSource plugins (billing, orchestrator, ...)
+ // required for internal libs at SonarSource
.addInclusion("org/sonar/server/platform/")
-
- // required for commercial plugins at SonarSource
+ .addInclusion("org/sonar/core/persistence/")
+ .addInclusion("org/sonar/core/properties/")
+ .addInclusion("org/sonar/server/views/")
.addInclusion("com/sonarsource/plugins/license/api/")
// API exclusions
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 7b045fe6961..f44a346336e 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
@@ -50,7 +50,7 @@ public class PluginLoader {
private static final String[] DEFAULT_SHARED_RESOURCES = {"org/sonar/plugins", "com/sonar/plugins", "com/sonarsource/plugins"};
- private static final Version COMPATIBILITY_MODE_MAX_VERSION = Version.create("5.2");
+ public static final Version COMPATIBILITY_MODE_MAX_VERSION = Version.create("5.2");
private final PluginJarExploder jarExploder;
private final PluginClassloaderFactory classloaderFactory;
@@ -96,14 +96,15 @@ public class PluginLoader {
def.setSelfFirstStrategy(info.isUseChildFirstClassLoader());
Version minSqVersion = info.getMinimalSqVersion();
boolean compatibilityMode = minSqVersion != null && minSqVersion.compareToIgnoreQualifier(COMPATIBILITY_MODE_MAX_VERSION) < 0;
+ def.setCompatibilityMode(compatibilityMode);
if (compatibilityMode) {
- Loggers.get(getClass()).warn("API compatibility mode is no longer supported. In case of error, plugin {} [{}] should package its dependencies.",
- info.getName(), info.getKey());
+ Loggers.get(getClass()).debug("API compatibility mode is enabled on plugin {} [{}] " +
+ "(built with API lower than {})",
+ info.getName(), info.getKey(), COMPATIBILITY_MODE_MAX_VERSION);
}
}
}
return classloadersByBasePlugin.values();
-
}
/**
diff --git a/sonar-core/src/main/java/org/sonar/core/util/FileUtils.java b/sonar-core/src/main/java/org/sonar/core/util/FileUtils.java
index 079fa552539..5b91a8055f3 100644
--- a/sonar-core/src/main/java/org/sonar/core/util/FileUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/util/FileUtils.java
@@ -114,7 +114,7 @@ public final class FileUtils {
* <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
* </ul>
*
- * @param path file or directory to delete, can be {@code null}
+ * @param file file or directory to delete, can be {@code null}
* @return {@code true} if the file or directory was deleted, otherwise {@code false}
*/
public static boolean deleteQuietly(@Nullable Path path) {
diff --git a/sonar-core/src/test/java/org/sonar/core/platform/PluginClassloaderFactoryTest.java b/sonar-core/src/test/java/org/sonar/core/platform/PluginClassloaderFactoryTest.java
index 329a5431d7a..9d825b758af 100644
--- a/sonar-core/src/test/java/org/sonar/core/platform/PluginClassloaderFactoryTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/platform/PluginClassloaderFactoryTest.java
@@ -23,8 +23,10 @@ import com.sonarsource.plugins.license.api.FooBar;
import java.io.File;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
+import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.api.utils.internal.JUnitTempFolder;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -36,7 +38,10 @@ public class PluginClassloaderFactoryTest {
static final String BASE_PLUGIN_KEY = "base";
static final String DEPENDENT_PLUGIN_KEY = "dependent";
- PluginClassloaderFactory factory = new PluginClassloaderFactory();
+ @Rule
+ public JUnitTempFolder temp = new JUnitTempFolder();
+
+ PluginClassloaderFactory factory = new PluginClassloaderFactory(temp);
@Test
public void create_isolated_classloader() {
@@ -57,6 +62,20 @@ public class PluginClassloaderFactoryTest {
}
@Test
+ public void create_classloader_compatible_with_with_old_api_dependencies() {
+ PluginClassLoaderDef def = basePluginDef();
+ def.setCompatibilityMode(true);
+ ClassLoader classLoader = factory.create(asList(def)).get(def);
+
+ // Plugin can access to API and its transitive dependencies as defined in version 5.1.
+ // It can not access to core classes though, even if it was possible in previous versions.
+ assertThat(canLoadClass(classLoader, RulesDefinition.class.getCanonicalName())).isTrue();
+ assertThat(canLoadClass(classLoader, StringUtils.class.getCanonicalName())).isTrue();
+ assertThat(canLoadClass(classLoader, BASE_PLUGIN_CLASSNAME)).isTrue();
+ assertThat(canLoadClass(classLoader, PluginClassloaderFactory.class.getCanonicalName())).isFalse();
+ }
+
+ @Test
public void classloader_exports_resources_to_other_classloaders() {
PluginClassLoaderDef baseDef = basePluginDef();
PluginClassLoaderDef dependentDef = dependentPluginDef();
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 2a2ae2c3575..76a1268f933 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,15 +21,13 @@ 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;
import org.assertj.core.data.MapEntry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.updatecenter.common.Version;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,11 +39,8 @@ public class PluginLoaderTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public LogTester logTester = new LogTester();
-
private PluginClassloaderFactory classloaderFactory = mock(PluginClassloaderFactory.class);
- private PluginLoader underTest = new PluginLoader(new FakePluginExploder(), classloaderFactory);
+ private PluginLoader loader = new PluginLoader(new FakePluginExploder(), classloaderFactory);
@Test
public void define_classloader() throws Exception {
@@ -55,7 +50,7 @@ public class PluginLoaderTest {
.setMainClass("org.foo.FooPlugin")
.setMinimalSqVersion(Version.create("5.2"));
- Collection<PluginClassLoaderDef> defs = underTest.defineClassloaders(ImmutableMap.of("foo", info));
+ Collection<PluginClassLoaderDef> defs = loader.defineClassloaders(ImmutableMap.of("foo", info));
assertThat(defs).hasSize(1);
PluginClassLoaderDef def = defs.iterator().next();
@@ -64,6 +59,21 @@ public class PluginLoaderTest {
assertThat(def.getFiles()).containsOnly(jarFile);
assertThat(def.getMainClassesByPluginKey()).containsOnly(MapEntry.entry("foo", "org.foo.FooPlugin"));
// TODO test mask - require change in sonar-classloader
+
+ // built with SQ 5.2+ -> does not need API compatibility mode
+ assertThat(def.isCompatibilityMode()).isFalse();
+ }
+
+ @Test
+ public void enable_compatibility_mode_if_plugin_is_built_before_5_2() throws Exception {
+ File jarFile = temp.newFile();
+ PluginInfo info = new PluginInfo("foo")
+ .setJarFile(jarFile)
+ .setMainClass("org.foo.FooPlugin")
+ .setMinimalSqVersion(Version.create("4.5.2"));
+
+ Collection<PluginClassLoaderDef> defs = loader.defineClassloaders(ImmutableMap.of("foo", info));
+ assertThat(defs.iterator().next().isCompatibilityMode()).isTrue();
}
/**
@@ -94,7 +104,7 @@ public class PluginLoaderTest {
.setBasePlugin("foo")
.setUseChildFirstClassLoader(true);
- Collection<PluginClassLoaderDef> defs = underTest.defineClassloaders(ImmutableMap.of(
+ Collection<PluginClassLoaderDef> defs = loader.defineClassloaders(ImmutableMap.of(
base.getKey(), base, extension1.getKey(), extension1, extension2.getKey(), extension2));
assertThat(defs).hasSize(1);
@@ -110,18 +120,22 @@ public class PluginLoaderTest {
}
@Test
- public void log_warning_if_plugin_is_built_with_api_5_2_or_lower() throws Exception {
- File jarFile = temp.newFile();
- PluginInfo info = new PluginInfo("foo")
- .setJarFile(jarFile)
- .setMainClass("org.foo.FooPlugin")
- .setMinimalSqVersion(Version.create("4.5.2"));
+ public void plugin_is_not_recognised_as_system_extension_if_key_is_governance_and_extends_another_plugin() throws IOException {
+ PluginInfo foo = createPluginInfo("foo");
+ PluginInfo governance = createPluginInfo("governance")
+ .setBasePlugin("foo");
+
+ Collection<PluginClassLoaderDef> defs = loader.defineClassloaders(ImmutableMap.of("foo", foo, "governance", governance));
- Collection<PluginClassLoaderDef> defs = underTest.defineClassloaders(ImmutableMap.of("foo", info));
- assertThat(defs).extracting(PluginClassLoaderDef::getBasePluginKey).containsExactly("foo");
+ assertThat(defs).extracting("compatibilityMode").containsOnly(false, false);
+ }
- List<String> warnings = logTester.logs(LoggerLevel.WARN);
- assertThat(warnings).contains("API compatibility mode is no longer supported. In case of error, plugin foo [foo] should package its dependencies.");
+ private PluginInfo createPluginInfo(String pluginKey) throws IOException {
+ File jarFile = temp.newFile();
+ return new PluginInfo(pluginKey)
+ .setJarFile(jarFile)
+ .setMainClass("org.foo." + pluginKey + "Plugin")
+ .setMinimalSqVersion(Version.create("6.6"));
}
/**
diff --git a/sonar-duplications/build.gradle b/sonar-duplications/build.gradle
index 79e4b224640..a45feb6dca7 100644
--- a/sonar-duplications/build.gradle
+++ b/sonar-duplications/build.gradle
@@ -8,7 +8,6 @@ dependencies {
// please keep list ordered
compile 'org.codehaus.sonar:sonar-channel'
- compile project(':sonar-plugin-api')
compileOnly 'com.google.code.findbugs:jsr305'
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/CodeFragment.java b/sonar-duplications/src/main/java/org/sonar/duplications/CodeFragment.java
new file mode 100644
index 00000000000..450259c72e6
--- /dev/null
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/CodeFragment.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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.duplications;
+
+
+/**
+ * TODO Enforce contracts of this interface in concrete classes by using preconditions, currently this leads to failures of tests.
+ *
+ * <p>This interface is not intended to be implemented by clients.</p>
+ *
+ * @since 2.14
+ */
+public interface CodeFragment {
+
+ /**
+ * Number of line where fragment starts.
+ * Numbering starts from 1.
+ */
+ int getStartLine();
+
+ /**
+ * Number of line where fragment ends.
+ * Numbering starts from 1.
+ */
+ int getEndLine();
+
+}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/block/Block.java b/sonar-duplications/src/main/java/org/sonar/duplications/block/Block.java
index 76b8bbc4b36..7b2e5fe3dce 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/block/Block.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/block/Block.java
@@ -19,11 +19,13 @@
*/
package org.sonar.duplications.block;
+import org.sonar.duplications.CodeFragment;
+
/**
* Represents part of source code between two lines.
* If two blocks have the same {@link #getBlockHash() hash}, then we assume that there is a duplication in a code, which they represent.
*/
-public final class Block {
+public final class Block implements CodeFragment {
private final String resourceId;
private final ByteArray blockHash;
@@ -125,10 +127,12 @@ public final class Block {
return indexInFile;
}
+ @Override
public int getStartLine() {
return startLine;
}
+ @Override
public int getEndLine() {
return endLine;
}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/ClonePart.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/ClonePart.java
index 5b62937268a..da8cd64c543 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/index/ClonePart.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/ClonePart.java
@@ -19,7 +19,9 @@
*/
package org.sonar.duplications.index;
-public class ClonePart {
+import org.sonar.duplications.CodeFragment;
+
+public class ClonePart implements CodeFragment {
private final String resourceId;
private final int startUnit;
@@ -46,10 +48,12 @@ public class ClonePart {
return startUnit;
}
+ @Override
public int getStartLine() {
return startLine;
}
+ @Override
public int getEndLine() {
return endLine;
}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
index 1e11b40dc9c..50a32621f0a 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
@@ -21,8 +21,9 @@ package org.sonar.duplications.internal.pmd;
import java.util.ArrayList;
import java.util.List;
+
import javax.annotation.concurrent.Immutable;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
+
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
index e98f30e9e00..44f1576dc75 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
@@ -26,7 +26,6 @@ import net.sourceforge.pmd.cpd.SourceCode;
import net.sourceforge.pmd.cpd.TokenEntry;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.cpd.Tokens;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.cpd.FileCodeLoaderWithoutCache;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/TokensLine.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java
index 648fddbf37a..62f3059b01d 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/TokensLine.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java
@@ -17,12 +17,14 @@
* 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.api.batch.sensor.cpd.internal;
+package org.sonar.duplications.internal.pmd;
+
+import org.sonar.duplications.CodeFragment;
/**
* Immutable code fragment, which formed from tokens of one line.
*/
-public class TokensLine {
+public class TokensLine implements CodeFragment {
private final String value;
@@ -49,6 +51,7 @@ public class TokensLine {
return value;
}
+ @Override
public int getStartLine() {
return startLine;
}
@@ -56,6 +59,7 @@ public class TokensLine {
/**
* Same as {@link #getStartLine()}
*/
+ @Override
public int getEndLine() {
return startLine;
}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/statement/Statement.java b/sonar-duplications/src/main/java/org/sonar/duplications/statement/Statement.java
index c6a84d59a7b..a18c6635381 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/statement/Statement.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/statement/Statement.java
@@ -21,9 +21,10 @@ package org.sonar.duplications.statement;
import java.util.List;
import javax.annotation.Nullable;
+import org.sonar.duplications.CodeFragment;
import org.sonar.duplications.token.Token;
-public class Statement {
+public class Statement implements CodeFragment {
private final int startLine;
private final int endLine;
@@ -53,10 +54,12 @@ public class Statement {
this.endLine = tokens.get(tokens.size() - 1).getLine();
}
+ @Override
public int getStartLine() {
return startLine;
}
+ @Override
public int getEndLine() {
return endLine;
}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/PmdBlockChunkerTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/PmdBlockChunkerTest.java
index 99d895c0b9b..4335be00567 100644
--- a/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/PmdBlockChunkerTest.java
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/PmdBlockChunkerTest.java
@@ -19,13 +19,13 @@
*/
package org.sonar.duplications.internal.pmd;
-import java.util.Arrays;
-import java.util.List;
import org.junit.Test;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
+import java.util.Arrays;
+import java.util.List;
+
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/TokenizerBridgeTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/TokenizerBridgeTest.java
index 12f58bd8c9a..7ff5dbe30d4 100644
--- a/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/TokenizerBridgeTest.java
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/internal/pmd/TokenizerBridgeTest.java
@@ -30,7 +30,6 @@ import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.cpd.Tokens;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/sonar-plugin-api-deps/build.gradle b/sonar-plugin-api-deps/build.gradle
new file mode 100644
index 00000000000..e8620ce371a
--- /dev/null
+++ b/sonar-plugin-api-deps/build.gradle
@@ -0,0 +1,42 @@
+sonarqube {
+ properties {
+ property 'sonar.projectName', "${projectTitle} :: Plugin API Dependencies"
+ }
+}
+
+apply plugin: 'com.github.johnrengelman.shadow'
+
+dependencies {
+ // Versions must not be changed and overridden from root build.gradle (see dependencyManagement section).
+ // These are the versions defined in SQ 5.1
+ compile 'com.google.code.gson:gson:2.3.1'
+ compile 'com.google.guava:guava:10.0.1'
+ compile 'commons-beanutils:commons-beanutils:1.9.3'
+ compile 'commons-codec:commons-codec:1.8'
+ compile 'commons-collections:commons-collections:3.2.2'
+ compile 'org.apache.commons:commons-email:1.3.2'
+ compile 'commons-io:commons-io:2.4'
+ compile 'commons-lang:commons-lang:2.6'
+ compile('dom4j:dom4j:1.6.1') {
+ exclude group: 'xml-apis'
+ }
+ compile 'org.picocontainer:picocontainer:2.14.3'
+ compile 'org.slf4j:slf4j-api:1.7.10'
+ compile 'ch.qos.logback:logback-classic:1.1.2'
+ compile 'ch.qos.logback:logback-core:1.1.2'
+ compile('org.apache.maven:maven-core:3.0.5') {
+ exclude group: 'classworlds', module: 'classworlds'
+ exclude group: 'org.sonatype.sisu', module: 'sisu-guava'
+ }
+ compile 'org.apache.maven:maven-artifact:3.0.5'
+ compile('org.codehaus.sonar:sonar-squid:4.1') {
+ exclude group: 'org.codehaus.sonar', module: 'sonar-check-api'
+ }
+ compile('org.codehaus.sonar:sonar-java-api:5.1') {
+ exclude group: 'org.codehaus.sonar', module: 'sonar-deprecated'
+ exclude group: 'org.codehaus.sonar', module: 'sonar-plugin-api'
+ }
+}
+
+// This JAR is cached because used as a resource in sonar-core:
+shadowJar.outputs.cacheIf { true }
diff --git a/sonar-plugin-api/build.gradle b/sonar-plugin-api/build.gradle
index e9c781e7e47..047cf35b695 100644
--- a/sonar-plugin-api/build.gradle
+++ b/sonar-plugin-api/build.gradle
@@ -17,6 +17,9 @@ dependencies {
// shaded, but not relocated
compile project(':sonar-check-api')
+ compile(project(':sonar-duplications')) {
+ exclude group: 'org.slf4', module: 'slf4j-api'
+ }
shadow 'org.codehaus.staxmate:staxmate'
shadow 'org.codehaus.woodstox:stax2-api'
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
index 06c5e30c0dd..16ac7816508 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
@@ -30,6 +30,7 @@ import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
import org.sonar.api.batch.sensor.internal.DefaultStorable;
import org.sonar.api.batch.sensor.internal.SensorStorage;
import org.sonar.api.config.Configuration;
+import org.sonar.duplications.internal.pmd.TokensLine;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Collections.unmodifiableList;
@@ -38,7 +39,7 @@ import static java.util.Objects.requireNonNull;
public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
private final Configuration config;
- private final List<TokensLine> result = new ArrayList<>();
+ private final ArrayList<TokensLine> result = new ArrayList<>();
private InputFile inputFile;
private int startLine = Integer.MIN_VALUE;
private int startIndex = 0;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
index 3559ebdb258..76977f55bd7 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
@@ -53,7 +53,6 @@ import org.sonar.api.batch.sensor.coverage.NewCoverage;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
-import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
import org.sonar.api.batch.sensor.error.AnalysisError;
import org.sonar.api.batch.sensor.error.NewAnalysisError;
import org.sonar.api.batch.sensor.error.internal.DefaultAnalysisError;
@@ -81,6 +80,7 @@ import org.sonar.api.internal.SonarRuntimeImpl;
import org.sonar.api.measures.Metric;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.Version;
+import org.sonar.duplications.internal.pmd.TokensLine;
import static java.util.Collections.unmodifiableMap;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/authentication/IdentityProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/authentication/IdentityProvider.java
index fff63e272fd..3b0e01073e9 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/authentication/IdentityProvider.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/authentication/IdentityProvider.java
@@ -19,7 +19,6 @@
*/
package org.sonar.api.server.authentication;
-import org.sonar.api.ExtensionPoint;
import org.sonar.api.server.ServerSide;
/**
@@ -33,7 +32,6 @@ import org.sonar.api.server.ServerSide;
* @since 5.4
*/
@ServerSide
-@ExtensionPoint
public interface IdentityProvider {
/**
diff --git a/sonar-scanner-engine/build.gradle b/sonar-scanner-engine/build.gradle
index 9ad0ce807ba..2442d85d34d 100644
--- a/sonar-scanner-engine/build.gradle
+++ b/sonar-scanner-engine/build.gradle
@@ -30,7 +30,6 @@ dependencies {
compile project(':sonar-core')
compile project(':sonar-scanner-protocol')
compile project(':sonar-ws')
- compile project(':sonar-duplications')
runtime project(path: ':sonar-plugin-api', configuration: 'shadow')
compileOnly project(path: ':sonar-plugin-api')