I have a somewhat intricate setup which ends up with a class cast exception between instances of the same class. Essentially I have custom build logic which is externalized into a plugin and which is used from:
- settings.gradle to construct a multi-project structure * all projects in the structure which do an ‘apply plugin’ on the plugin
I have a github repo for replicating the issue at:
https://github.com/mbjarland/gradle-settings-classloader-issue
and the repo readme contains instructions (two steps) for reproducing the issue.
Here is essentially what happens:
- I include the plugin in the buildscript.dependencies.classpath in settings.gradle so that I can use a MultiProjectConfigurator class from the plugin. I then instantiate the MultiProjectConfigurator in the settings.gradle file and call a method on it. The MultiProjectConfigurator looks as follows:
class MultiProjectConfigurator {
def setup(settings) {
SomeClass instance = new SomeClass()
settings.gradle.allprojects {
project.ext.someClass = instance
}
}
}
where SomeClass also lives in the plugin jar. This code is naturally a bare bones, gutted out example and my actual plugin has some complex logic in it for setting up the multi-project structure. The essential piece for this issue is the instantiation of SomeClass and the setting of a project property on all projects.
- I then do a allprojects { apply plugin: ‘greeting’ } in build.gradle. The plugin in question looks as follows:
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// The below line breaks with ClassCastException...even though
// we are casting between two instances of the same class. I.e.
// we have class loader issues of some sort...
SomeClass instance = project.ext.someClass as SomeClass
println "Successfully cast ${project.ext.someClass} to SomeClass"
}
}
executing anything on the root project will apply the plugin and cause a class cast exception :
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'SomeClass@6c69df61' with class 'SomeClass' to class 'SomeClass'
Note that if I uncomment the following line in settings.gradle, the class cast exception goes away and everything works as expected:
buildscript {
...
//configurations.classpath.each { file -> settings.classLoader.addURL(file.toURI().toURL()) }
}
The driver for this use case is that my actual plugin code does an extensive and time consuming (sub-second…but this is for every single build…so we would like to not repeat that work) file system scan in the MultiProjectConfigurator class. This scan in turn results in a complex data structure which I need to make available for all projects in the multi-project structure.
So what is going on here? And perhaps more importantly, how would I go about transporting an instance between these two code snippets and execution phases?
Thank you in advance and cudos for making such a stellar build system.