ClassCastException from org.gradle.process.internal.child.BootstrapSecurityManager

I’m a member of Java Platform Development Team. We are planning to change the application class loader not to be a URLClassLoader. org.gradle.process.internal.child.BootstrapSecurityManager depends on the internal implementation of the application class loader and will be broken in JDK 9. It looks like it uses the security manager to intercept the system initialization to add paths to “java.class.path” for Gradle’s own use. This is not going to work. We can assist you to determine the right fix. The system property value of “java.class.path” is read only once during the system initialization and anything appending to the classpath may not get the effect it wishes. It is generally a bad idea to depend on the internal implementation that is subject to change drastically in any release.

line 58 of org.gradle.process.internal.child.BootstrapSecurityManager

URLClassLoader systemClassLoader = target != null ? target : (URLClassLoader) getClass().getClassLoader();

Successfully started process ‘Gradle Test Executor 1’ Error occurred during initialization of VM java.lang.ClassCastException: sun.misc.ClassLoaders$AppClassLoader cannot be cast to java.net.URLClassLoader

at jarjar.org.gradle.process.internal.child.BootstrapSecurityManager.checkPermission(BootstrapSecurityManager.java:58)

at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1285)

at java.lang.System.getProperty(System.java:709)

at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1447)

at java.lang.System.initPhase3(System.java:1254)

1 Like

Thanks. I’ve sent a note to the gradle-dev list.

To be found here: https://groups.google.com/forum/#!topic/gradle-dev/vwMlR_yQ7Fw

We can assist you to determine the right fix.

Hi Mandy,

How would you prefer to go about this process? What information do you need from us to start this determination?

Can you explain what Gradle attempts to do with this custom security manager (BootstrapSecurityManager)?

Is this set via -Djava.security.manager?

Can you also explain why you can’t set -classpath rather than appending “java.class.path” at runtime (is it reading the paths from System.in)? Also it also sets the security manager if set from the input. We’re reworking the system initialization to make it more robust and is going to restrict only system classes to be loaded during bootstrapping. What class loader do you load the custom security manager?

Mandy,

We are nearing the end of a release cycle and time is tight. What are your time pressures for progressing this?

Would, say, a response in about 2 weeks be acceptable?

That’s fine. It will be good to target the fix for the next gradle release.

This is all to work around the command-line length limitations on windows, where the classpath is longer than the limit. It’s all a total hack, but the most transparent we’ve found so far. Here’s how it works:

  1. We launch java with a classpath pointing at a single jar containing the security manager and a minimal main class. 2. the security manager injects the full classpath into the system ClassLoader using reflection. 3. the security manager updates ‘java.class.path’, for those tests that might happen to read this (not to update the classpath). 4. the security manager removes itself.

The obvious solution to me would be to allow the ‘java’ launcher to accept an options file or similar so we can just use the long classpath without the hacks.

@Adam,

Would the -classpath wildcard help your situation?

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html#A1100762

Is this hack for the long classpath for your testing only? #3 seems to imply that - the security manager updates java.class.path, for those tests that might happen to read this (not to update the classpath).

The issue with using the wildcard option is we then can’t enforce ordering. It also only possibly solves the problem if we are pointing to a single directory with a bunch of jars. For example, when running against a resolved configuration this won’t work because of the folder structure used in the artifact cache. We are having this problem (too long a classpath on Windows) in other places as well GRADLE-3260.

1 Like

The hack is used when Gradle runs user tests, so it’s invoked by pretty much every Gradle build out there.

The user test classpath can be any arbitrary sequence of files, but is almost always a bunch of jars from various spots in the Gradle artifact cache, and bunch of jars and directories from various locations from the build output. In this case, a wildcard option would not work, as these things are all spread across many directories, and we want precise control over the ordering.

We have a similar problem in a few other spots, but test execution is the most problematic for us and the most widely used.

The command-line limit on Windows is 8k. Is the 8k limit still an issue for Gradle in practice?

https://bugs.openjdk.java.net/browse/JDK-4326573 is the issue about java @file support. The complication there is the i18n and locale issue for the launcher to deal with before the system is initialized.

If the command-line limit is still an issue, one alternative is to create an empty JAR file listing the dependences in Class-Path attribute.