diff --git a/src/main/docs/ant-task.html b/src/main/docs/ant-task.html index ed68ab9f..9395d358 100644 --- a/src/main/docs/ant-task.html +++ b/src/main/docs/ant-task.html @@ -87,6 +87,13 @@

Parameters

Reference to a path defined anywhere else. Should be identical to classpath used for compiling the class files. + + disableClassloadingCache + boolean + false + Disable the internal JVM classloading cache when getting bytecode from the classpath. This setting slows down checks, but may work around issues with other Mojos, that do not close their class loaders. If you get FileNotFoundExceptions related to non-existent JAR entries you can try to work around using this setting. + + failOnUnsupportedJava boolean diff --git a/src/main/java/de/thetaphi/forbiddenapis/Checker.java b/src/main/java/de/thetaphi/forbiddenapis/Checker.java index d0dee8f1..3cbd2c1a 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/Checker.java +++ b/src/main/java/de/thetaphi/forbiddenapis/Checker.java @@ -60,7 +60,8 @@ public final class Checker implements RelatedClassLookup, Constants { public static enum Option { FAIL_ON_MISSING_CLASSES, FAIL_ON_VIOLATION, - FAIL_ON_UNRESOLVABLE_SIGNATURES + FAIL_ON_UNRESOLVABLE_SIGNATURES, + DISABLE_CLASSLOADING_CACHE } public final boolean isSupportedJDK; @@ -285,6 +286,9 @@ private ClassSignature getClassFromClassLoader(final String clazz) throws ClassN if (url != null) { final URLConnection conn = url.openConnection(); final boolean isRuntimeClass = isRuntimeClass(conn); + if (!isRuntimeClass && options.contains(Option.DISABLE_CLASSLOADING_CACHE)) { + conn.setUseCaches(false); + } final InputStream in = conn.getInputStream(); try { classpathClassCache.put(clazz, c = new ClassSignature(AsmUtils.readAndPatchClass(in), isRuntimeClass, false)); diff --git a/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java b/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java index 48bf2c65..bab11181 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java @@ -71,6 +71,7 @@ public class AntTask extends Task implements Constants { private boolean failOnViolation = true; private boolean ignoreEmptyFileset = false; private String targetVersion = null; + private boolean disableClassloadingCache = false; @Override public void execute() throws BuildException { @@ -109,6 +110,7 @@ public void info(String msg) { if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES); if (failOnViolation) options.add(FAIL_ON_VIOLATION); if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); + if (disableClassloadingCache) options.add(DISABLE_CLASSLOADING_CACHE); final Checker checker = new Checker(log, loader, options); if (!checker.isSupportedJDK) { @@ -380,4 +382,16 @@ public void setFailOnViolation(boolean failOnViolation) { public void setTargetVersion(String targetVersion) { this.targetVersion = targetVersion; } + + /** + * Disable the internal JVM classloading cache when getting bytecode from + * the classpath. This setting slows down checks, but may work around + * issues with other tasks, that do not close their class loaders. + * If you get {@code FileNotFoundException}s related to non-existent JAR entries + * you can try to work around using this setting. + * The default is {@code false}. + */ + public void setDisableClassloadingCache(boolean disableClassloadingCache) { + this.disableClassloadingCache = disableClassloadingCache; + } } diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index 6e4db9ea..8dc34ad5 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -303,6 +303,27 @@ public void setIgnoreFailures(boolean ignoreFailures) { data.ignoreFailures = ignoreFailures; } + /** + * Disable the internal JVM classloading cache when getting bytecode from + * the classpath. This setting slows down checks, but may work around + * issues with other plugin, that do not close their class loaders. + * If you get {@code FileNotFoundException}s related to non-existent JAR entries + * you can try to work around using this setting. + *

+ * The default is {@code false}, unless the plugin detects that your build is + * running in the Gradle Daemon (which has this problem), setting the + * default to {@code true} as a consequence. + */ + @Input + public boolean getDisableClassloadingCache() { + return data.disableClassloadingCache; + } + + /** @see #getDisableClassloadingCache */ + public void setDisableClassloadingCache(boolean disableClassloadingCache) { + data.disableClassloadingCache = disableClassloadingCache; + } + /** * List of a custom Java annotations (full class names) that are used in the checked * code to suppress errors. Those annotations must have at least @@ -483,6 +504,7 @@ public void info(String msg) { if (getFailOnMissingClasses()) options.add(FAIL_ON_MISSING_CLASSES); if (!getIgnoreFailures()) options.add(FAIL_ON_VIOLATION); if (getFailOnUnresolvableSignatures()) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); + if (getDisableClassloadingCache()) options.add(DISABLE_CLASSLOADING_CACHE); final Checker checker = new Checker(log, loader, options); if (!checker.isSupportedJDK) { diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java index f6c6d346..d02e2641 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -44,7 +44,8 @@ public class CheckForbiddenApisExtension { "failOnUnsupportedJava", "failOnMissingClasses", "failOnUnresolvableSignatures", - "ignoreFailures" + "ignoreFailures", + "disableClassloadingCache" ); public FileCollection signaturesFiles; // initialized by plugin-init.groovy @@ -56,6 +57,7 @@ public class CheckForbiddenApisExtension { public boolean failOnUnsupportedJava = false, failOnMissingClasses = true, failOnUnresolvableSignatures = true, - ignoreFailures = false; + ignoreFailures = false, + disableClassloadingCache = false; } diff --git a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java index 641b3578..7180a1f5 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java +++ b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java @@ -155,6 +155,17 @@ public abstract class AbstractCheckMojo extends AbstractMojo implements Constant @Parameter(required = false, property="forbiddenapis.failOnViolation", defaultValue = "true") private boolean failOnViolation; + /** + * Disable the internal JVM classloading cache when getting bytecode from + * the classpath. This setting slows down checks, but may work around + * issues with other Mojos, that do not close their class loaders. + * If you get {@code FileNotFoundException}s related to non-existent JAR entries + * you can try to work around using this setting. + * @since 2.0 + */ + @Parameter(required = false, defaultValue = "false") + private boolean disableClassloadingCache; + /** * The default compiler target version used to expand references to bundled JDK signatures. * E.g., if you use "jdk-deprecated", it will expand to this version. @@ -312,6 +323,7 @@ public void info(String msg) { if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES); if (failOnViolation) options.add(FAIL_ON_VIOLATION); if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); + if (disableClassloadingCache) options.add(DISABLE_CLASSLOADING_CACHE); final Checker checker = new Checker(log, loader, options); if (!checker.isSupportedJDK) { diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index dc574d52..cce9648e 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -23,10 +23,19 @@ if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) { throw new PluginInstantiationException('Forbidden-apis only works in projects using the java plugin.'); } +// chck if running in Gradle Daemon? +// see: http://stackoverflow.com/questions/23265217/how-to-know-whether-you-are-running-inside-a-gradle-daemon +boolean isGradleDaemon = System.getProperty('sun.java.command', '').startsWith('org.gradle.launcher.daemon.') || + Thread.currentThread().stackTrace.any { it.className.startsWith 'org.gradle.launcher.daemon.' }; +if (isGradleDaemon) { + project.logger.info('You are running forbidden-apis in the Gradle Daemon; disabling classloading cache to work around resource leak.'); +} + // create Extension for defaults: def extension = project.extensions.create(FORBIDDEN_APIS_EXTENSION_NAME, CheckForbiddenApisExtension.class); extension.with { signaturesFiles = project.files(); + disableClassloadingCache |= isGradleDaemon; } // Define our tasks (one for each SourceSet):