From c519cdb9986efc54f849fc7b93e3488135b7cd24 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 20 Sep 2015 00:25:33 +0200 Subject: [PATCH 1/4] Add support for disabling the JAR file caches in URLClassLoader --- src/main/java/de/thetaphi/forbiddenapis/Checker.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/Checker.java b/src/main/java/de/thetaphi/forbiddenapis/Checker.java index 5cfcbab3..eaf0bc67 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/Checker.java +++ b/src/main/java/de/thetaphi/forbiddenapis/Checker.java @@ -63,7 +63,8 @@ public static enum Option { INTERNAL_RUNTIME_FORBIDDEN, FAIL_ON_MISSING_CLASSES, FAIL_ON_VIOLATION, - FAIL_ON_UNRESOLVABLE_SIGNATURES + FAIL_ON_UNRESOLVABLE_SIGNATURES, + DISABLE_CLASSLOADING_CACHE } public final boolean isSupportedJDK; @@ -242,6 +243,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(new ClassReader(in), isRuntimeClass, false)); From 57d07c0a4431305f1ce49d0d549d6d7bec59a2d0 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 20 Sep 2015 18:18:02 +0200 Subject: [PATCH 2/4] Fix issue #38: Add a new setting to disable class loading cache (JarUrlConnection's internal cache) --- src/main/docs/ant-task.html | 7 ++++++ .../thetaphi/forbiddenapis/ant/AntTask.java | 14 +++++++++++ .../gradle/CheckForbiddenApis.java | 24 +++++++++++++++---- .../gradle/CheckForbiddenApisExtension.java | 6 +++-- .../maven/AbstractCheckMojo.java | 12 ++++++++++ 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main/docs/ant-task.html b/src/main/docs/ant-task.html index 19900147..b7cd17fc 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/ant/AntTask.java b/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java index 48b0463b..11b60e1d 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java @@ -69,6 +69,7 @@ public class AntTask extends Task { private boolean failOnUnresolvableSignatures = true; private boolean failOnViolation = true; private boolean ignoreEmptyFileset = false; + private boolean disableClassloadingCache = false; @Override public void execute() throws BuildException { @@ -90,6 +91,7 @@ public void execute() throws BuildException { 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(new Logger() { @Override public void error(String msg) { @@ -347,4 +349,16 @@ public void setIgnoreEmptyFileSet(boolean ignoreEmptyFileset) { public void setFailOnViolation(boolean failOnViolation) { this.failOnViolation = 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 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 b5b8b364..7e872d3a 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -16,10 +16,7 @@ * limitations under the License. */ -import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_MISSING_CLASSES; -import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_UNRESOLVABLE_SIGNATURES; -import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_VIOLATION; -import static de.thetaphi.forbiddenapis.Checker.Option.INTERNAL_RUNTIME_FORBIDDEN; +import static de.thetaphi.forbiddenapis.Checker.Option.*; import groovy.lang.Closure; import java.io.Closeable; @@ -220,6 +217,24 @@ 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}. + */ + @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 @@ -409,6 +424,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 1049f5ad..f28fa243 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -40,7 +40,8 @@ public class CheckForbiddenApisExtension { "failOnUnsupportedJava", "failOnMissingClasses", "failOnUnresolvableSignatures", - "ignoreFailures" + "ignoreFailures", + "disableClassloadingCache" ); public FileCollection signaturesFiles; @@ -51,6 +52,7 @@ public class CheckForbiddenApisExtension { 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 85a6b582..a293ec81 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java +++ b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java @@ -111,6 +111,17 @@ public abstract class AbstractCheckMojo extends AbstractMojo { @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. @@ -217,6 +228,7 @@ public void execute() throws MojoExecutionException { 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(new Logger() { @Override public void error(String msg) { From 4c303ad8e2679e06e036280d54993016831ae9a8 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 12 Jun 2016 14:07:49 +0200 Subject: [PATCH 3/4] Auto-detect if Gradle Daemon is running and set default for disableClassloadingCache setting --- .../forbiddenapis/gradle/CheckForbiddenApis.java | 5 ++++- .../de/thetaphi/forbiddenapis/gradle/plugin-init.groovy | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index 12b5d20d..8dc34ad5 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -309,7 +309,10 @@ public void setIgnoreFailures(boolean ignoreFailures) { * 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}. + *

+ * 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() { 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..a2b3e65a 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 forbiddenapis 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): From 2240ab70d08eddb9bb3702ae64657c356dfa9c5b Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 12 Jun 2016 14:25:55 +0200 Subject: [PATCH 4/4] fix typo (make it consistent) --- .../de/thetaphi/forbiddenapis/gradle/plugin-init.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a2b3e65a..cce9648e 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -28,7 +28,7 @@ if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) { 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 forbiddenapis in the Gradle Daemon; disabling classloading cache to work around resource leak.'); + project.logger.info('You are running forbidden-apis in the Gradle Daemon; disabling classloading cache to work around resource leak.'); } // create Extension for defaults: