Skip to content

Commit

Permalink
Add new setting to disable the classloading cache (#76)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
uschindler authored Jun 13, 2016
1 parent 28a6c88 commit 1ea2132
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/main/docs/ant-task.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ <h2>Parameters</h2>
<td>Reference to a <code>path</code> defined anywhere else. Should be identical to classpath used for compiling the class files.</td>
</tr>

<tr>
<td>disableClassloadingCache</td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<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>
</tr>

<tr>
<td>failOnUnsupportedJava</td>
<td><code>boolean</code></td>
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/de/thetaphi/forbiddenapis/Checker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/de/thetaphi/forbiddenapis/ant/AntTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 <em>may</em> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 <em>may</em> 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.
* <p>
* The default is {@code false}, unless the plugin detects that your build is
* running in the <em>Gradle Daemon</em> (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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public class CheckForbiddenApisExtension {
"failOnUnsupportedJava",
"failOnMissingClasses",
"failOnUnresolvableSignatures",
"ignoreFailures"
"ignoreFailures",
"disableClassloadingCache"
);

public FileCollection signaturesFiles; // initialized by plugin-init.groovy
Expand All @@ -56,6 +57,7 @@ public class CheckForbiddenApisExtension {
public boolean failOnUnsupportedJava = false,
failOnMissingClasses = true,
failOnUnresolvableSignatures = true,
ignoreFailures = false;
ignoreFailures = false,
disableClassloadingCache = false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 <em>may</em> 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.
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 1ea2132

Please sign in to comment.