How to diagnose unexpected build failures caused by plugin application?

Hi,

we’re using a couple of plugins in a Gradle multi project build. There is one central settings.gradle.kts file that uses include to include all modules of the build.

The project compiles fine through Gradle up to that point. I am now running into a problem where Gradle fails with a very unexpected failure when I include a new custom plugin for the build:

* What went wrong:
An exception occurred applying plugin request [id: 'company.gradle.support', version: '6.0.0-alpha']
> Failed to apply plugin 'company.gradle.support'.
   > Could not create task ':some-project:cmpPublishExt'.
      > Could not create task of type 'CompanyPublishExtension'.
         > Failed to apply plugin class 'org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin'.
            > Cannot cast object 'org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention@18f19b33' with class 'org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention' to class 'org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention'

I am not quoting the full stack trace here for the sake of brevity but it appears that the problem is really caused by the instantiation of the plugin:

. . . there are more consequences up the stack trace in regard to task creation

Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin'.
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:173)
        at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:151)
        at org.gradle.api.plugins.PluginManager$apply.call(Unknown Source)
        at org.jfrog.gradle.plugin.artifactory.ArtifactoryPluginBase.apply(ArtifactoryPluginBase.groovy:61)
        at org.jfrog.gradle.plugin.artifactory.ArtifactoryPluginBase.apply(ArtifactoryPluginBase.groovy)
        at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
        at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
        at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:187)
        at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:52)
        at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:282)
        . . . 
        at org.gradle.api.internal.project.AbstractPluginAware.apply(AbstractPluginAware.java:49)
        at org.gradle.api.plugins.PluginAware$apply$0.call(Unknown Source)
        at de.company.build.support.extensions.CompanyPublishExtension.applyArtifactoryPlugin(CompanyPublishExtension.groovy:239)
        at de.company.build.support.extensions.CompanyPublishExtension.<init>(CompanyPublishExtension.groovy:50)
        at de.company.build.support.extensions.CompanyPublishExtension.<init>(Unknown Source)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$InvokeConstructorStrategy.newInstance(AsmBackedClassGenerator.java:2070)
        at org.gradle.internal.instantiation.generator.AbstractClassGenerator$GeneratedClassImpl$GeneratedConstructorImpl.newInstance(AbstractClassGenerator.java:486)
        at org.gradle.internal.instantiation.generator.DependencyInjectingInstantiator.doCreate(DependencyInjectingInstantiator.java:64)
        at org.gradle.internal.instantiation.generator.DependencyInjectingInstantiator.newInstanceWithDisplayName(DependencyInjectingInstantiator.java:50)
        at org.gradle.api.internal.project.taskfactory.TaskFactory$1.call(TaskFactory.java:88)
        ... 202 more
Caused by: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention@61f2bf3a' with class 'org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention' to class 'org.
jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention'
        at org.jfrog.gradle.plugin.artifactory.ArtifactoryPluginBase.getArtifactoryPluginConvention(ArtifactoryPluginBase.groovy:92)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.jfrog.gradle.plugin.artifactory.ArtifactoryPluginBase.apply(ArtifactoryPluginBase.groovy:47)
        at org.jfrog.gradle.plugin.artifactory.ArtifactoryPluginBase.apply(ArtifactoryPluginBase.groovy)
        at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
        at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
        . . . 
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:74)
        at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:74)
        at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:167)
        at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:166)
        ... 242 more

The application of the plugin is rather straight forward (albeit old Groovy code):

if (!project.plugins.hasPlugin("com.jfrog.artifactory")) {
    project.apply(["plugin": "com.jfrog.artifactory"])
}

This code is executed when the task is being created upon plugin application.

The weird thing is that there should be no reason that the new plugin that I just added can have any effect on whatever the Artifactory plugin is doing when it is being applied.

If I remove the new plugin, that error message goes away. I have played with variations of declaring the plugin in all modules with apply false to no avail.

I am wondering which scenarios could lead to that type of error. It is my understanding that this kind of problem (i.e. a class cannot be cast to its type) could be caused by class loader issues. I just don’t see how any of the code involved is getting anywhere near any problematic areas that would be able to cause this.

Any help I can get is very much appreciated.
Thank you!

The only reason I know for cannot cast to itself indeed is, if the class files come from different class loaders.
Class loading in Gradle is not trivial and without seeing your build it is hard to guess what exactly causes the problem.

For example if you have two script plugins (not precompiled ones, regular ones), both add plugin X to their buildscript class path, one of the plugins applies X, the other tries to react to the application using pluginManager.withPlugin("X") { ... }, then the block is executed as the plugin is matched by string id, but the classes are different as they come from different class loaders.

A solution to that problem would for example be to add the plugin X to a parent class loader both inherit from.

Unfortunately I can’t share the code here because it is under NDA. But I checked:

  • All modules apply plugin A which then applies the Artifactory Plugin if it is not present already.
  • All modules reference plugin B (some with apply true, some with apply false). Plugin B has no relationship to plugin A, or the Artifactory plugin.
  • The only relationship that is based on pluginManager.withPlugin("...") is in plugin B which reacts to the java plugin (because it then declares a deferred task dependency)
  • Both plugins do not use any threads but only regular Gradle functionality. There are some misused afterEvaluate blocks for different things but nothing that should have any effect here.

The project compiles fine when I remove plugin B. I am absolutely clueless what happens here and why.

It has probably not so much to do with who applies what where or whether threads are involved.
As I said, it is most probably more a question of how the classes in question are added to class path.
You could for example set a breakpoint on the loading of the artifactory class to see where it is loaded from, you simply add it to some higher order class path like when referencing it in buildSrc or adding it to the root project build script class path or similar.

I guess, I will have to break out the debugger. Which is tedious because I cannot simply debug a project that IntelliJ cannot open due to build failure…

The problem seems to be caused by the fact that one of the projects of the multiproject build did not have the plugin B mentioned in the plugins block. I have now added that plugin with apply false and that seems to resolve the issue.

This is the most confusing Gradle behavior ever. There is absolutely no relationship from plugin B to the Artifactory plugin that subsequently failed.

Can anyone point me to further information about this topic? The section about binary plugins doesn’t really give a proper explanation…

Not seen a similar behavior yet, so without some MCVE hard to say where to direct you to or how to explain what happened.