From e51cb49945d4f3a1d39f74234de49be8ca925fff Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Thu, 19 Jul 2018 11:42:54 +0200 Subject: [PATCH] Revert "SONAR-10541, SONAR-10331 Drop compatibility mode and clean plugin classloader" This reverts commit 4dcb5245f1147048d122ff2d335a6b5b7364c565. --- .../build.gradle | 3 +- .../ComputeEngineContainerImplTest.java | 2 - .../server/platform/ServerFileSystemImpl.java | 16 ++---- server/sonar-server/build.gradle | 6 ++- settings.gradle | 1 + sonar-application/build.gradle | 4 +- sonar-core/build.gradle | 14 +++++ .../core/platform/PluginClassLoaderDef.java | 13 +++++ .../platform/PluginClassloaderFactory.java | 47 ++++++++++++++--- .../org/sonar/core/platform/PluginLoader.java | 9 ++-- .../java/org/sonar/core/util/FileUtils.java | 2 +- .../PluginClassloaderFactoryTest.java | 21 +++++++- .../sonar/core/platform/PluginLoaderTest.java | 52 ++++++++++++------- sonar-duplications/build.gradle | 1 - .../org/sonar/duplications/CodeFragment.java | 44 ++++++++++++++++ .../org/sonar/duplications/block/Block.java | 6 ++- .../sonar/duplications/index/ClonePart.java | 6 ++- .../internal/pmd/PmdBlockChunker.java | 3 +- .../internal/pmd/TokenizerBridge.java | 1 - .../internal/pmd}/TokensLine.java | 8 ++- .../duplications/statement/Statement.java | 5 +- .../internal/pmd/PmdBlockChunkerTest.java | 6 +-- .../internal/pmd/TokenizerBridgeTest.java | 1 - sonar-plugin-api-deps/build.gradle | 42 +++++++++++++++ sonar-plugin-api/build.gradle | 3 ++ .../sensor/cpd/internal/DefaultCpdTokens.java | 3 +- .../sensor/internal/SensorContextTester.java | 2 +- .../authentication/IdentityProvider.java | 2 - sonar-scanner-engine/build.gradle | 1 - 29 files changed, 254 insertions(+), 70 deletions(-) create mode 100644 sonar-duplications/src/main/java/org/sonar/duplications/CodeFragment.java rename {sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal => sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd}/TokensLine.java (91%) create mode 100644 sonar-plugin-api-deps/build.gradle 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: * @@ -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 { *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • * * - * @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() { @@ -56,6 +61,20 @@ public class PluginClassloaderFactoryTest { assertThat(canLoadClass(classLoader, StringUtils.class.getCanonicalName())).isFalse(); } + @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(); 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 defs = underTest.defineClassloaders(ImmutableMap.of("foo", info)); + Collection 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 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 defs = underTest.defineClassloaders(ImmutableMap.of( + Collection 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 defs = loader.defineClassloaders(ImmutableMap.of("foo", foo, "governance", governance)); - Collection defs = underTest.defineClassloaders(ImmutableMap.of("foo", info)); - assertThat(defs).extracting(PluginClassLoaderDef::getBasePluginKey).containsExactly("foo"); + assertThat(defs).extracting("compatibilityMode").containsOnly(false, false); + } - List 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. + * + *

    This interface is not intended to be implemented by clients.

    + * + * @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 similarity index 91% rename from sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/TokensLine.java rename to 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 result = new ArrayList<>(); + private final ArrayList 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') -- 2.39.5