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;
}
/**
- * 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) {
@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
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");
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();
return launcher;
}
-
-}
\ No newline at end of file
+}
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);
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";
}
*/
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) {
* @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;
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);
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();
@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();
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();
@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();