The following classes appear as argument class and as parameter class, but are defined by different class loader


(Rob Winch) #1

It appears I am experiencing a ClassLoader bug within Gradle. Any help resolving this would be greatly appreciated.

Original Project

The original project with the issue can be found at pivotalsoftware/pivotal-cla/tree/bug-staging. This project is quite a bit bigger than most would care to debug, so I created a minimal project to reproduce the problem.

Minimal Project

I have produces a minimal sample project that reproduces the issue. See rwinch/gradle-buildscript-question

My original project used the cloudfoundry-gradle-plugin. However, I wanted to simplify the problem for analysis. The sample I provided has a very simple plugin (still named cloudfoundry) that applies a task that doesn’t actually do anything. To run the sample you must first install into maven local using:

$ cd plugin
$ ./gradlew install

When using the plugin as shown below I get the ClassLoading issue.

build.gradle

buildscript {
	ext {
		tomcatPluginVersion = '2.2.4'
		cfPluginVersion = 'testing'
	}
	repositories {
		mavenCentral()
		mavenLocal()
	}
	dependencies {
		classpath("com.bmuschko:gradle-tomcat-plugin:$tomcatPluginVersion")
		classpath("org.cloudfoundry:cf-gradle-plugin:${cfPluginVersion}")
	}
}

apply from: 'gradle/staging.gradle'

gradle/staging.gradle

import org.cloudfoundry.gradle.tasks.*

buildscript {
	repositories {
		mavenCentral()
		mavenLocal()
	}
	dependencies {
		classpath("com.bmuschko:gradle-tomcat-plugin:$tomcatPluginVersion")
		classpath("org.cloudfoundry:cf-gradle-plugin:${cfPluginVersion}")
	}
}

apply plugin: 'cloudfoundry'

task(bug, type: CreateServiceCloudFoundryTask) {
	cloudfoundry {
		services {
			[
				"my-p-mysql" {
					label = "p-mysql"
					plan = "100mb"
				}
			]
		}
	}
}

Invoking the task produces:

$ cd useplugin
$ ./gradlew bug --stacktrace
:bug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bug'.
> No signature of method: org.cloudfoundry.gradle.tasks.ServiceCloudFoundryHelper$_createServices_closure1.doCall() is applicable for argument types: (org.cloudfoundry.gradle.CloudFoundryServiceExtension) values: [org.cloudfoundry.gradle.CloudFoundryServiceExtension@81c5da9]
  Possible solutions: doCall(org.cloudfoundry.gradle.CloudFoundryServiceExtension), findAll(), findAll(), isCase(java.lang.Object), isCase(java.lang.Object)
  The following classes appear as argument class and as parameter class, but are defined by different class loader:
  org.cloudfoundry.gradle.CloudFoundryServiceExtension (defined by 'org.gradle.api.internal.initialization.loadercache.DefaultClassLoaderCache$HashedMutableURLClassLoader@585427e' and 'org.gradle.api.internal.initialization.loadercache.DefaultClassLoaderCache$HashedMutableURLClassLoader@4b1c84a9')
  If one of the method suggestions matches the method you wanted to call, 
  then check your class loader setup.

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':bug'.
	...
Caused by: groovy.lang.MissingMethodException: No signature of method: org.cloudfoundry.gradle.tasks.ServiceCloudFoundryHelper$_createServices_closure1.doCall() is applicable for argument types: (org.cloudfoundry.gradle.CloudFoundryServiceExtension) values: [org.cloudfoundry.gradle.CloudFoundryServiceExtension@81c5da9]
Possible solutions: doCall(org.cloudfoundry.gradle.CloudFoundryServiceExtension), findAll(), findAll(), isCase(java.lang.Object), isCase(java.lang.Object)
The following classes appear as argument class and as parameter class, but are defined by different class loader:
org.cloudfoundry.gradle.CloudFoundryServiceExtension (defined by 'org.gradle.api.internal.initialization.loadercache.DefaultClassLoaderCache$HashedMutableURLClassLoader@585427e' and 'org.gradle.api.internal.initialization.loadercache.DefaultClassLoaderCache$HashedMutableURLClassLoader@4b1c84a9')
If one of the method suggestions matches the method you wanted to call, 
then check your class loader setup.
	at org.cloudfoundry.gradle.tasks.ServiceCloudFoundryHelper.createServices(ServiceCloudFoundryHelper.groovy:23)
	at org.cloudfoundry.gradle.tasks.CreateServiceCloudFoundryTask$_createService_closure1.doCall(CreateServiceCloudFoundryTask.groovy:46)
	at org.cloudfoundry.gradle.tasks.CreateServiceCloudFoundryTask.withCloudFoundryClient(CreateServiceCloudFoundryTask.groovy:36)
	at org.cloudfoundry.gradle.tasks.CreateServiceCloudFoundryTask.createService(CreateServiceCloudFoundryTask.groovy:45)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
	at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:228)
	at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:221)
	at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:210)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
	... 68 more

(Sometimes) working around the issue

I was able to find a few workarounds for the minimal sample I provided. Unfortuantely, I have not yet found a solution that works for the real project.

Consistent buildscript classpath

If the buildscript classpath in the imported gradle script matches the main build.gradle classpath the issue goes away. You can find an examplef of this in the fix-matching-classpath branch. Specifically, I can add the following to the staging.gradle file to work around the issue:

classpath("com.bmuschko:gradle-tomcat-plugin:$tomcatPluginVersion")

Apply Plugin Class (not string)

Alternatively, applying the plugin class seems to work for the minimal sample (but not my real sample). You can find a sample of this in the fix-plugin-class branch. Specifically I can use the following in staging.gradle file:

apply plugin: org.cloudfoundry.gradle.CloudFoundryPlugin

Help Please

Is this a known bug? I wasn’t able to find one reported.

Any suggestions on how to resolve my problem for the original project?

Cheers,
Rob


(Pepper Lebeck-Jobe) #2

Great bug report. I’m just beginning to take a look at the issue. I see from the minimal example, that you are experiencing this problem with Gradle 2.13.

Can I assume there was an earlier version of Gradle with which you did not experience the issue?


(Rob Winch) #3

Thanks for the reply @eljobe!

Unfortuantely, there is no version (that I’m aware of) that this was working. I didn’t try all versions, but I tried a few all the way back to Gradle 2.0.

I’m guessing you are aware of this, but the issue seems to be related to Gradle’s attempt at ClassLoader isolation of the imported Gradle script. If all the logic is place in a single file the problem will also disappear. This workaround is probably a dupicate of Consistent buildscript classpath so I did not list it again.

Thanks again for your time!
Rob