From: Julien HENRY Date: Tue, 18 Jun 2013 09:33:25 +0000 (+0200) Subject: SONARPLUGINS-2983, SONARPLUGINS-2941 Improve mask/unmask feature in EmbededRunner X-Git-Tag: 2.5-rc1~135 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a0a269e0240b18e6fdbd385366df8f75cbc27b0e;p=sonar-scanner-cli.git SONARPLUGINS-2983, SONARPLUGINS-2941 Improve mask/unmask feature in EmbededRunner --- diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java index e5ce102..f83218f 100644 --- a/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java @@ -36,6 +36,7 @@ public class EmbeddedRunner extends Runner { private final BatchLauncher batchLauncher; private final List extensions = new ArrayList(); + private final static String MASK_RULES_PROP = "sonarRunner.maskRules"; EmbeddedRunner(BatchLauncher bl) { this.batchLauncher = bl; @@ -49,12 +50,42 @@ public class EmbeddedRunner extends Runner { } /** - * Sonar is executed in an almost fully isolated classloader. The unmasked packages - * define the classes of the client application that are visible from Sonar classloader. They - * relate to the extensions provided by {@link #setUnmaskedPackages(String...)}. + * Sonar is executed in an almost fully isolated classloader (mask everything by default). his method allow to unmask some classes based on + * a prefix of their fully qualified name. They relate to the extensions provided by {@link #addExtensions(Object...)}. + * Complex mask/unmask rules can be defined by chaining several ordered calls to {@link #unmask(String)} and {@link #mask(String)}. + * Registered mask/unmask rules are considered in their registration order and as soon as a matching prefix is found + * the class is masked/unmasked accordingly. + * If no matching prefix can be found then by default class is masked. */ + public EmbeddedRunner unmask(String fqcnPrefix) { + return addMaskRule("UNMASK", fqcnPrefix); + } + + /** + * @see EmbeddedRunner#unmask(String) + */ + public EmbeddedRunner mask(String fqcnPrefix) { + return addMaskRule("MASK", fqcnPrefix); + } + + private EmbeddedRunner addMaskRule(String type, String fqcnPrefix) { + String existingRules = property(MASK_RULES_PROP, ""); + if (!"".equals(existingRules)) { + existingRules += ","; + } + existingRules += type + "|" + fqcnPrefix; + return setProperty(MASK_RULES_PROP, existingRules); + } + + /** + * @deprecated since 2.3 use {@link #setUnmaskedClassRegexp(String)} + */ + @Deprecated public EmbeddedRunner setUnmaskedPackages(String... packages) { - return setProperty("sonarRunner.unmaskedPackages", Utils.join(packages, ",")); + for (String packagePrefix : packages) { + unmask(packagePrefix + "."); + } + return this; } public EmbeddedRunner addExtensions(Object... objects) { diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java index 4b06069..ce41b57 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java @@ -48,10 +48,23 @@ public class EmbeddedRunnerTest { @Test public void should_set_unmasked_packages() { EmbeddedRunner runner = EmbeddedRunner.create(); - assertThat(runner.property(InternalProperties.RUNNER_UNMASKED_PACKAGES, null)).isNull(); + assertThat(runner.property(InternalProperties.RUNNER_MASK_RULES, null)).isNull(); runner = EmbeddedRunner.create().setUnmaskedPackages("org.apache.ant", "org.ant"); - assertThat(runner.property(InternalProperties.RUNNER_UNMASKED_PACKAGES, null)).isEqualTo("org.apache.ant,org.ant"); + assertThat(runner.property(InternalProperties.RUNNER_MASK_RULES, null)).isEqualTo("UNMASK|org.apache.ant.,UNMASK|org.ant."); + } + + @Test + public void should_set_mask_rules() { + EmbeddedRunner runner = EmbeddedRunner.create(); + assertThat(runner.property(InternalProperties.RUNNER_MASK_RULES, null)).isNull(); + + runner = EmbeddedRunner.create() + .unmask("org.slf4j.Logger") + .mask("org.slf4j.") + .mask("ch.qos.logback.") + .unmask(""); + assertThat(runner.property(InternalProperties.RUNNER_MASK_RULES, null)).isEqualTo("UNMASK|org.slf4j.Logger,MASK|org.slf4j.,MASK|ch.qos.logback.,UNMASK|"); } @Test @@ -68,10 +81,12 @@ public class EmbeddedRunnerTest { public void should_set_properties() { EmbeddedRunner runner = EmbeddedRunner.create(); runner.setProperty("sonar.projectKey", "foo"); - runner.addProperties(new Properties() {{ - setProperty("sonar.login", "admin"); - setProperty("sonar.password", "gniark"); - }}); + runner.addProperties(new Properties() { + { + setProperty("sonar.login", "admin"); + setProperty("sonar.password", "gniark"); + } + }); assertThat(runner.property("sonar.projectKey", null)).isEqualTo("foo"); assertThat(runner.property("sonar.login", null)).isEqualTo("admin"); diff --git a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncher.java b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncher.java index 51b917a..96c28db 100644 --- a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncher.java +++ b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncher.java @@ -57,8 +57,15 @@ public class BatchLauncher { Object launcher = AccessController.doPrivileged(new PrivilegedAction() { public Object run() { List jarFiles = jarDownloader.download(); - String unmaskedPackages = props.getProperty(InternalProperties.RUNNER_UNMASKED_PACKAGES, ""); - IsolatedClassloader classloader = new IsolatedClassloader(getClass().getClassLoader(), unmaskedPackages.split(":")); + String maskRulesProp = props.getProperty(InternalProperties.RUNNER_MASK_RULES, null); + String[] maskRulesConcat = maskRulesProp != null ? maskRulesProp.split(",") : new String[0]; + String[][] maskRules = new String[maskRulesConcat.length][2]; + for (int i = 0; i < maskRulesConcat.length; i++) { + String[] splitted = maskRulesConcat[i].split("\\|"); + maskRules[i][0] = splitted[0]; + maskRules[i][1] = splitted.length > 1 ? splitted[1] : ""; + } + IsolatedClassloader classloader = new IsolatedClassloader(getClass().getClassLoader(), maskRules); classloader.addFiles(jarFiles); Object launcher = delegateExecution(classloader, props, extensions); tempCleaning.clean(); @@ -88,5 +95,4 @@ public class BatchLauncher { return launcher; } - -} \ No newline at end of file +} diff --git a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncherMain.java b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncherMain.java index 0c8591a..67fd0e8 100644 --- a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncherMain.java +++ b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncherMain.java @@ -47,7 +47,7 @@ public class BatchLauncherMain { try { props.load(input); // just to be clean, do not forward properties that do not make sense in fork mode - props.remove(InternalProperties.RUNNER_UNMASKED_PACKAGES); + props.remove(InternalProperties.RUNNER_MASK_RULES); } finally { IOUtils.closeQuietly(input); diff --git a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/InternalProperties.java b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/InternalProperties.java index 8f9956e..f0069a7 100644 --- a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/InternalProperties.java +++ b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/InternalProperties.java @@ -22,5 +22,5 @@ package org.sonar.runner.impl; public interface InternalProperties { String RUNNER_APP = "sonarRunner.app"; String RUNNER_APP_VERSION = "sonarRunner.appVersion"; - String RUNNER_UNMASKED_PACKAGES = "sonarRunner.unmaskedPackages"; + String RUNNER_MASK_RULES = "sonarRunner.maskRules"; } diff --git a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/IsolatedClassloader.java b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/IsolatedClassloader.java index 954b341..9054935 100644 --- a/sonar-runner-impl/src/main/java/org/sonar/runner/impl/IsolatedClassloader.java +++ b/sonar-runner-impl/src/main/java/org/sonar/runner/impl/IsolatedClassloader.java @@ -33,14 +33,14 @@ import java.util.List; */ class IsolatedClassloader extends URLClassLoader { - private final String[] unmaskedPackages; + private final String[][] maskRules; /** * The parent classloader is used only for loading classes and resources in unmasked packages */ - IsolatedClassloader(ClassLoader parent, String... unmaskedPackages) { + IsolatedClassloader(ClassLoader parent, String[][] maskRules) { super(new URL[0], parent); - this.unmaskedPackages = unmaskedPackages; + this.maskRules = maskRules; } void addFiles(List files) { @@ -57,9 +57,9 @@ class IsolatedClassloader extends URLClassLoader { * @return true, if class can be loaded from parent ClassLoader */ boolean canLoadFromParent(String name) { - for (String pkg : unmaskedPackages) { - if (name.startsWith(pkg + ".")) { - return true; + for (String[] maskRule : maskRules) { + if (name.startsWith(maskRule[1])) { + return "UNMASK".equals(maskRule[0]); } } return false; diff --git a/sonar-runner-impl/src/test/java/org/sonar/runner/impl/BatchLauncherTest.java b/sonar-runner-impl/src/test/java/org/sonar/runner/impl/BatchLauncherTest.java index b914a39..eceb8c9 100644 --- a/sonar-runner-impl/src/test/java/org/sonar/runner/impl/BatchLauncherTest.java +++ b/sonar-runner-impl/src/test/java/org/sonar/runner/impl/BatchLauncherTest.java @@ -48,7 +48,7 @@ public class BatchLauncherTest { props.put("foo", "bar"); // Unmask the current classloader in order to access FakeIsolatedLauncher - props.put(InternalProperties.RUNNER_UNMASKED_PACKAGES, "org.sonar.runner.impl"); + props.put(InternalProperties.RUNNER_MASK_RULES, "UNMASK|org.sonar.runner.impl."); List extensions = new ArrayList(); FakeIsolatedLauncher isolatedLauncher = (FakeIsolatedLauncher) launcher.doExecute(jarDownloader, props, extensions); @@ -64,7 +64,7 @@ public class BatchLauncherTest { Properties props = new Properties(); // The current classloader in not available -> fail to load FakeIsolatedLauncher - props.put(InternalProperties.RUNNER_UNMASKED_PACKAGES, ""); + props.put(InternalProperties.RUNNER_MASK_RULES, ""); try { launcher.doExecute(jarDownloader, props, Collections.emptyList()); fail(); diff --git a/sonar-runner-impl/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java b/sonar-runner-impl/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java index fb0d7f9..266208c 100644 --- a/sonar-runner-impl/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java +++ b/sonar-runner-impl/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java @@ -32,7 +32,7 @@ public class IsolatedClassloaderTest { @Test public void should_restrict_loading_from_parent() throws Exception { ClassLoader parentClassloader = getClass().getClassLoader(); - IsolatedClassloader classLoader = new IsolatedClassloader(parentClassloader, "org.apache.ant"); + IsolatedClassloader classLoader = new IsolatedClassloader(parentClassloader, new String[][] {new String[] {"UNMASK", "org.apache.ant."}}); assertThat(classLoader.canLoadFromParent("org.sonar.runner.Foo")).isFalse(); assertThat(classLoader.canLoadFromParent("org.objectweb.asm.ClassVisitor")).isFalse(); @@ -46,7 +46,7 @@ public class IsolatedClassloaderTest { thrown.expect(ClassNotFoundException.class); thrown.expectMessage("org.junit.Test"); ClassLoader parent = getClass().getClassLoader(); - IsolatedClassloader classLoader = new IsolatedClassloader(parent); + IsolatedClassloader classLoader = new IsolatedClassloader(parent, new String[0][]); // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM assertThat(classLoader.loadClass("java.lang.String", false)).isNotNull(); @@ -56,7 +56,7 @@ public class IsolatedClassloaderTest { @Test public void should_find_in_parent_when_matches_unmasked_packages() throws ClassNotFoundException { ClassLoader parent = getClass().getClassLoader(); - IsolatedClassloader classLoader = new IsolatedClassloader(parent, "org.junit"); + IsolatedClassloader classLoader = new IsolatedClassloader(parent, new String[][] {new String[] {"UNMASK", "org.junit."}}); // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM assertThat(classLoader.loadClass("org.junit.Test", false)).isNotNull();