]> source.dussan.org Git - sonar-scanner-cli.git/commitdiff
SONARPLUGINS-2983, SONARPLUGINS-2941 Improve mask/unmask feature in EmbededRunner
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 18 Jun 2013 09:33:25 +0000 (11:33 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 19 Jun 2013 10:21:48 +0000 (12:21 +0200)
sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java
sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncher.java
sonar-runner-impl/src/main/java/org/sonar/runner/impl/BatchLauncherMain.java
sonar-runner-impl/src/main/java/org/sonar/runner/impl/InternalProperties.java
sonar-runner-impl/src/main/java/org/sonar/runner/impl/IsolatedClassloader.java
sonar-runner-impl/src/test/java/org/sonar/runner/impl/BatchLauncherTest.java
sonar-runner-impl/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java

index e5ce102a38c1195c876ddd2c6f11d8e1bfa17a29..f83218f5fdb8d0d9e908c109f07573db6da8850e 100644 (file)
@@ -36,6 +36,7 @@ public class EmbeddedRunner extends Runner<EmbeddedRunner> {
 
   private final BatchLauncher batchLauncher;
   private final List<Object> extensions = new ArrayList<Object>();
+  private final static String MASK_RULES_PROP = "sonarRunner.maskRules";
 
   EmbeddedRunner(BatchLauncher bl) {
     this.batchLauncher = bl;
@@ -49,12 +50,42 @@ public class EmbeddedRunner extends Runner<EmbeddedRunner> {
   }
 
   /**
-   * 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) {
index 4b06069bbf9fbbc98b072b1e04f351f15fdb5d53..ce41b572656edac524e1e4ca9d8e2c25fd6710e7 100644 (file)
@@ -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");
index 51b917a4c7d609610c2022f68877567229eba007..96c28db50768ebc7f1ef9672da89f129ea6c91f2 100644 (file)
@@ -57,8 +57,15 @@ public class BatchLauncher {
     Object launcher = AccessController.doPrivileged(new PrivilegedAction<Object>() {
       public Object run() {
         List<File> 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
+}
index 0c8591ae16b233b439ef880add6217bfd6aeb0cc..67fd0e80c2c40b3f928cf394d28d4655c2bab091 100644 (file)
@@ -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);
index 8f9956ee0386b0a249f22d2a19a9119b09c308e0..f0069a78c507e64f30fde589960071c4f4365ff3 100644 (file)
@@ -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";
 }
index 954b3413ab09919f38d6ea53557f39a9c3db1aa4..9054935b297cc7050b38aed9f8869cb754187507 100644 (file)
@@ -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<File> 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;
index b914a395c2810810f6bd4caeb10481c7f08e8319..eceb8c9c527ebf4fe6d1271a392d56277c611fb2 100644 (file)
@@ -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<Object> extensions = new ArrayList<Object>();
 
     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();
index fb0d7f96939103349c2b82e81952ca26d8689daa..266208c9277fb48a510bd5bbbd5ff2d791bd350f 100644 (file)
@@ -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();