Skip to content

Commit 1ea2132

Browse files
authored
Add new setting to disable the classloading cache (#76)
Add support for disabling the JAR file caches in URLClassLoader & Auto-detect if Gradle Daemon is running and set default for disableClassloadingCache setting. This closes #75
1 parent 28a6c88 commit 1ea2132

File tree

7 files changed

+73
-3
lines changed

7 files changed

+73
-3
lines changed

src/main/docs/ant-task.html

+7
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ <h2>Parameters</h2>
8787
<td>Reference to a <code>path</code> defined anywhere else. Should be identical to classpath used for compiling the class files.</td>
8888
</tr>
8989

90+
<tr>
91+
<td>disableClassloadingCache</td>
92+
<td><code>boolean</code></td>
93+
<td><code>false</code></td>
94+
<td>Disable the internal JVM classloading cache when getting bytecode from the classpath. This setting slows down checks, but <em>may</em> work around issues with other Mojos, that do not close their class loaders. If you get <code>FileNotFoundException</code>s related to non-existent JAR entries you can try to work around using this setting.</td>
95+
</tr>
96+
9097
<tr>
9198
<td>failOnUnsupportedJava</td>
9299
<td><code>boolean</code></td>

src/main/java/de/thetaphi/forbiddenapis/Checker.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public final class Checker implements RelatedClassLookup, Constants {
6060
public static enum Option {
6161
FAIL_ON_MISSING_CLASSES,
6262
FAIL_ON_VIOLATION,
63-
FAIL_ON_UNRESOLVABLE_SIGNATURES
63+
FAIL_ON_UNRESOLVABLE_SIGNATURES,
64+
DISABLE_CLASSLOADING_CACHE
6465
}
6566

6667
public final boolean isSupportedJDK;
@@ -285,6 +286,9 @@ private ClassSignature getClassFromClassLoader(final String clazz) throws ClassN
285286
if (url != null) {
286287
final URLConnection conn = url.openConnection();
287288
final boolean isRuntimeClass = isRuntimeClass(conn);
289+
if (!isRuntimeClass && options.contains(Option.DISABLE_CLASSLOADING_CACHE)) {
290+
conn.setUseCaches(false);
291+
}
288292
final InputStream in = conn.getInputStream();
289293
try {
290294
classpathClassCache.put(clazz, c = new ClassSignature(AsmUtils.readAndPatchClass(in), isRuntimeClass, false));

src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java

+14
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public class AntTask extends Task implements Constants {
7171
private boolean failOnViolation = true;
7272
private boolean ignoreEmptyFileset = false;
7373
private String targetVersion = null;
74+
private boolean disableClassloadingCache = false;
7475

7576
@Override
7677
public void execute() throws BuildException {
@@ -109,6 +110,7 @@ public void info(String msg) {
109110
if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES);
110111
if (failOnViolation) options.add(FAIL_ON_VIOLATION);
111112
if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES);
113+
if (disableClassloadingCache) options.add(DISABLE_CLASSLOADING_CACHE);
112114
final Checker checker = new Checker(log, loader, options);
113115

114116
if (!checker.isSupportedJDK) {
@@ -380,4 +382,16 @@ public void setFailOnViolation(boolean failOnViolation) {
380382
public void setTargetVersion(String targetVersion) {
381383
this.targetVersion = targetVersion;
382384
}
385+
386+
/**
387+
* Disable the internal JVM classloading cache when getting bytecode from
388+
* the classpath. This setting slows down checks, but <em>may</em> work around
389+
* issues with other tasks, that do not close their class loaders.
390+
* If you get {@code FileNotFoundException}s related to non-existent JAR entries
391+
* you can try to work around using this setting.
392+
* The default is {@code false}.
393+
*/
394+
public void setDisableClassloadingCache(boolean disableClassloadingCache) {
395+
this.disableClassloadingCache = disableClassloadingCache;
396+
}
383397
}

src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java

+22
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,27 @@ public void setIgnoreFailures(boolean ignoreFailures) {
303303
data.ignoreFailures = ignoreFailures;
304304
}
305305

306+
/**
307+
* Disable the internal JVM classloading cache when getting bytecode from
308+
* the classpath. This setting slows down checks, but <em>may</em> work around
309+
* issues with other plugin, that do not close their class loaders.
310+
* If you get {@code FileNotFoundException}s related to non-existent JAR entries
311+
* you can try to work around using this setting.
312+
* <p>
313+
* The default is {@code false}, unless the plugin detects that your build is
314+
* running in the <em>Gradle Daemon</em> (which has this problem), setting the
315+
* default to {@code true} as a consequence.
316+
*/
317+
@Input
318+
public boolean getDisableClassloadingCache() {
319+
return data.disableClassloadingCache;
320+
}
321+
322+
/** @see #getDisableClassloadingCache */
323+
public void setDisableClassloadingCache(boolean disableClassloadingCache) {
324+
data.disableClassloadingCache = disableClassloadingCache;
325+
}
326+
306327
/**
307328
* List of a custom Java annotations (full class names) that are used in the checked
308329
* code to suppress errors. Those annotations must have at least
@@ -483,6 +504,7 @@ public void info(String msg) {
483504
if (getFailOnMissingClasses()) options.add(FAIL_ON_MISSING_CLASSES);
484505
if (!getIgnoreFailures()) options.add(FAIL_ON_VIOLATION);
485506
if (getFailOnUnresolvableSignatures()) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES);
507+
if (getDisableClassloadingCache()) options.add(DISABLE_CLASSLOADING_CACHE);
486508
final Checker checker = new Checker(log, loader, options);
487509

488510
if (!checker.isSupportedJDK) {

src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public class CheckForbiddenApisExtension {
4444
"failOnUnsupportedJava",
4545
"failOnMissingClasses",
4646
"failOnUnresolvableSignatures",
47-
"ignoreFailures"
47+
"ignoreFailures",
48+
"disableClassloadingCache"
4849
);
4950

5051
public FileCollection signaturesFiles; // initialized by plugin-init.groovy
@@ -56,6 +57,7 @@ public class CheckForbiddenApisExtension {
5657
public boolean failOnUnsupportedJava = false,
5758
failOnMissingClasses = true,
5859
failOnUnresolvableSignatures = true,
59-
ignoreFailures = false;
60+
ignoreFailures = false,
61+
disableClassloadingCache = false;
6062

6163
}

src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java

+12
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ public abstract class AbstractCheckMojo extends AbstractMojo implements Constant
155155
@Parameter(required = false, property="forbiddenapis.failOnViolation", defaultValue = "true")
156156
private boolean failOnViolation;
157157

158+
/**
159+
* Disable the internal JVM classloading cache when getting bytecode from
160+
* the classpath. This setting slows down checks, but <em>may</em> work around
161+
* issues with other Mojos, that do not close their class loaders.
162+
* If you get {@code FileNotFoundException}s related to non-existent JAR entries
163+
* you can try to work around using this setting.
164+
* @since 2.0
165+
*/
166+
@Parameter(required = false, defaultValue = "false")
167+
private boolean disableClassloadingCache;
168+
158169
/**
159170
* The default compiler target version used to expand references to bundled JDK signatures.
160171
* E.g., if you use "jdk-deprecated", it will expand to this version.
@@ -312,6 +323,7 @@ public void info(String msg) {
312323
if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES);
313324
if (failOnViolation) options.add(FAIL_ON_VIOLATION);
314325
if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES);
326+
if (disableClassloadingCache) options.add(DISABLE_CLASSLOADING_CACHE);
315327
final Checker checker = new Checker(log, loader, options);
316328

317329
if (!checker.isSupportedJDK) {

src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy

+9
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@ if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) {
2323
throw new PluginInstantiationException('Forbidden-apis only works in projects using the java plugin.');
2424
}
2525

26+
// chck if running in Gradle Daemon?
27+
// see: http://stackoverflow.com/questions/23265217/how-to-know-whether-you-are-running-inside-a-gradle-daemon
28+
boolean isGradleDaemon = System.getProperty('sun.java.command', '').startsWith('org.gradle.launcher.daemon.') ||
29+
Thread.currentThread().stackTrace.any { it.className.startsWith 'org.gradle.launcher.daemon.' };
30+
if (isGradleDaemon) {
31+
project.logger.info('You are running forbidden-apis in the Gradle Daemon; disabling classloading cache to work around resource leak.');
32+
}
33+
2634
// create Extension for defaults:
2735
def extension = project.extensions.create(FORBIDDEN_APIS_EXTENSION_NAME, CheckForbiddenApisExtension.class);
2836
extension.with {
2937
signaturesFiles = project.files();
38+
disableClassloadingCache |= isGradleDaemon;
3039
}
3140

3241
// Define our tasks (one for each SourceSet):

0 commit comments

Comments
 (0)