Gradle 2.6-rc-1 Classloader issue

issue-resolved

(Yannick Welsch) #1

Finally the Play functionality has arrived in Gradle :smile:

We have an issue with the classloader hierarchy in Play when using runPlayBinary. The Gradle Classloader contains libraries in an older version that shadow some of our own libraries. The library in case is guava. Here, Gradle uses com.google.guava:guava-jdk5:17.0 whereas we are using the newer guava version 18. Due to classloading order, our application code wrongly uses the older Gradle version and breaks.

// Somewhere in Play Application: System.out.println(com.google.common.util.concurrent.MoreExecutors.class.getResource("MoreExecutors.class")); System.out.println("=========================================="); System.out.println(Arrays.toString(((URLClassLoader) this.getClass().getClassLoader().getParent().getParent().getParent()).getURLs()));

which returns

`
jar:file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/guava-jdk5-17.0.jar!/com/google/common/util/concurrent/MoreExecutors.class

[file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/gradle-base-services-2.6-rc-1.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/gradle-core-2.6-rc-1.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/gradle-cli-2.6-rc-1.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/gradle-native-2.6-rc-1.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/gradle-messaging-2.6-rc-1.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/slf4j-api-1.7.10.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/jul-to-slf4j-1.7.10.jar, file:/home/dev/.gradle/wrapper/dists/gradle-2.6-rc-1-bin/ca4lrp6t1xbzsi1l3ix7k0fab/gradle-2.6-rc-1/lib/guava-jdk5-17.0.jar]
`

The classloading order either needs to be changed, or the libraries (slf4j, guava etc.) need to be repackaged.

Not sure what workaround can be done for now…


(Yannick Welsch) #2

Another issue related to classloading when using runPlayBinary:

We are using a third-party library for serialization/deserialization.

In third-party library code:
Class.forName(nameOfOurClass);

This results in a ClassNotFoundException.

The class nameOfOurClass can however directly be loaded by our application classes.

The classloader hierarchy could take cases like this into account. The same issue exists when using SBT. A quickfix would be to add the deserialization library to the list of application jars that are loaded by the reloadable classloader. Is there an easy way to add to this classpath?


(Gary Hale) #3

Thanks for the feedback, Yannick. We’re looking into it.


(Gary Hale) #4

The fix for the classloader isolation issue is in 2.6-rc-2.

We’ll look into what we can do for the second issue regarding classes that are referenced across classloaders, but any feature supporting this would be in a future release.


(Yannick Welsch) #5

@Gary_Hale thanks.

Regarding the quick fix I mentioned (adding the deserialization library to the list of application jars), here is the code snippet that does it.

The configuration serializationLib contains the library that does the serialization.

tasks.withType(PlayRun) { 
  setChangingClasspath(playConfigurations.playRun.changingArtifacts + configurations.serializationLib)
  setRuntimeClasspath(playConfigurations.playRun.nonChangingArtifacts - configurations.serializationLib)
}

(Chris L) #6

The other work around that we have for this is to put ourClass in another project which generate a jar and have the play project depends on this jar instead of project dependency.
nevermind, I think Gary already list that as alternative, after I read his post carefully
-cl