[GRADLE-2695] java.lang.ClassFormatError: Duplicate method name&signature in custom task's class file after upgrading to Gradle 1.3 Created: 27/Feb/13  Updated: 18/Nov/13  Resolved: 18/Nov/13

Status: Resolved
Project: Gradle
Affects Version/s: None
Fix Version/s: 1.10-rc-1

Type: Bug
Reporter: Gradle Forums Assignee: Unassigned
Resolution: Fixed Votes: 2


 Description   

Hi there,

I'm a bit late to the party this time But I'm currently upgrading our project builds to Gradle 1.3.

I'm having issues with one of our tasks. I didn't change its code between 1.1 and 1.3 and it compiles fine under all three versions. However, when I reference it in a build file as simple as this one:

buildscript {
repositories {
mavenRepo name: "universe", url: "http://companyRepoServer/"
}

dependencies {
classpath (
[group: 'com.mycompany', name: 'my-tasks', version: 'SNAPSHOT']
)
}
}

import com.mycompany.MyTask

apply plugin: 'java'

task somTask (type: MyTask) {
// nothing
}

gradle aborts during configuration with the following error:

  • Where:
    Build file 'D:\tmp\gradle-test-proj\build.gradle' line: 18
  • What went wrong:
    A problem occurred evaluating root project 'gradle-test-proj'.
    > Could not generate a proxy class for class com.mycompany.MyTask.
  • Try:
    Run with --info or --debug option to get more log output.
  • Exception is:
    org.gradle.api.GradleScriptException: A problem occurred evaluating root project 'gradle-test-proj'.
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:54)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:127)
    at org.gradle.configuration.BuildScriptProcessor.evaluate(BuildScriptProcessor.java:38)
    at org.gradle.configuration.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:43)
    at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:463)
    at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:75)
    at org.gradle.configuration.ProjectEvaluationConfigurer.execute(ProjectEvaluationConfigurer.java:23)
    at org.gradle.configuration.ProjectEvaluationConfigurer.execute(ProjectEvaluationConfigurer.java:21)
    at org.gradle.api.internal.Actions$CompositeAction.execute(Actions.java:67)
    at org.gradle.api.internal.Actions$TransformingActionAdapter.execute(Actions.java:96)
    at org.gradle.api.internal.project.AbstractProject.configure(AbstractProject.java:439)
    at org.gradle.api.internal.project.AbstractProject.allprojects(AbstractProject.java:434)
    at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:32)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:142)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:113)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:81)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:38)
    at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:39)
    at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:25)
    at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:50)
    at org.gradle.api.internal.Actions$RunnableActionAdapter.execute(Actions.java:137)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:201)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:174)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:170)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:139)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
    at org.gradle.launcher.Main.doAction(Main.java:48)
    at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
    at org.gradle.launcher.Main.main(Main.java:39)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:50)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:32)
    at org.gradle.launcher.GradleMain.main(GradleMain.java:26)
    Caused by: org.gradle.api.GradleException: Could not generate a proxy class for class com.mycompany.MyTask.
    at org.gradle.api.internal.AbstractClassGenerator.generate(AbstractClassGenerator.java:201)
    at org.gradle.api.internal.project.taskfactory.TaskFactory.createTaskObject(TaskFactory.java:105)
    at org.gradle.api.internal.project.taskfactory.TaskFactory.createTask(TaskFactory.java:70)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory.createTask(AnnotationProcessingTaskFactory.java:93)
    at org.gradle.api.internal.project.taskfactory.DependencyAutoWireTaskFactory.createTask(DependencyAutoWireTaskFactory.java:39)
    at org.gradle.api.internal.tasks.DefaultTaskContainer.add(DefaultTaskContainer.java:50)
    at org.gradle.api.internal.project.AbstractProject.task(AbstractProject.java:916)
    at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:216)
    at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:122)
    at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:147)
    at org.gradle.groovy.scripts.BasicScript.methodMissing(BasicScript.java:83)
    at build_21nm0ea1t3edkqi880mlpe13sn.run(D:\tmp\gradle-test-proj\build.gradle:18)
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:52)
    ... 32 more
    Caused by: org.gradle.api.GradleException: Could not call ClassLoader.defineClass() on org.gradle.util.MutableURLClassLoader@35de4376
    at org.gradle.util.JavaMethod.invoke(JavaMethod.java:62)
    at org.gradle.api.internal.AsmBackedClassGenerator$ClassBuilderImpl.generate(AsmBackedClassGenerator.java:819)
    at org.gradle.api.internal.AbstractClassGenerator.generate(AbstractClassGenerator.java:199)
    ... 44 more
    Caused by: java.lang.ClassFormatError: Duplicate method name&signature in class file com/mycompany/MyTask_Decorated
    at org.gradle.util.JavaMethod.invoke(JavaMethod.java:56)
    ... 46 more

BUILD FAILED

I read that you switched ASM versions in Gradle 1.2, but when I use 1.2 at runtime, everything works fine. The problems start when using Gradle 1.3 for building projects that rely on the custom task.

Any idea?

Best regards,
Mike



 Comments   
Comment by Gradle Forums [ 27/Feb/13 ]

Do you get the same error when compiling the plugin against 1.3, and then using that in a 1.3 build?

Comment by Gradle Forums [ 27/Feb/13 ]

Yes I do; it looks as if the runtime is relevant, not the compiled class file.

Comment by Gradle Forums [ 27/Feb/13 ]

Can you provide a reproducible example? Then we can have a closer look.

Comment by Gradle Forums [ 27/Feb/13 ]

Okay, so I compiled my task into a jar and created a minimal project reproducing the error. If you just run "gradle tasks" with gradle 1.2, it works. If you run it with gradle 1.3, it fails.

Here's the project archive:
[1]https://docs.google.com/file/d/0B6hxw...

Edit the task class was compiled with Gradle 1.3 btw.
----------------------------------------------------------------------------------------
[1] https://docs.google.com/file/d/0B6hxwnTm7cjybXhNcnFZR0VMcU0/edit

Comment by Gradle Forums [ 27/Feb/13 ]

Update: tested with Gradle 1.4: same result as with 1.3, even when compiling the task with 1.4

Comment by Gradle Forums [ 27/Feb/13 ]

Any update here? This is really a blocker for us...

Comment by Gradle Forums [ 27/Feb/13 ]

At the moment, it isn't clear to me that the problem is on Gradle's side. If you can provide a minimal reproducible example that allows to build the task from source, I might have another look.

Comment by Gradle Forums [ 27/Feb/13 ]

Hi,

Can you please provide the source for the task class. I can't proceed without it.

Comment by Gradle Forums [ 27/Feb/13 ]

Oh, sure, here's the gradle project that builds the "failingTask.jar" file used in the sample project above.

[1]https://docs.google.com/file/d/0B6hxw...
----------------------------------------------------------------------------------------
[1] https://docs.google.com/file/d/0B6hxwnTm7cjybGR3NFR6UXhORnM/edit?usp=sharing

Comment by Gradle Forums [ 27/Feb/13 ]

push

Comment by Gradle Forums [ 27/Feb/13 ]

Hi, I tried out the example and run into the same problem. I would be interested in the solution, too. Is there an example of a Java-Task class?

Comment by Gradle Forums [ 27/Feb/13 ]

I haven't worked out exactly why, but it's the `fileMapping(File, File)` and `fileMapping(File)` methods that trigger this. If you rename them it should work.

I'm still looking into exactly why this happens.

Comment by Gradle Forums [ 27/Feb/13 ]

Thanks, Luke. On my side it is working now if I rename the methods like you suggested.

Comment by Gradle Forums [ 27/Feb/13 ]

Thanks for pointing us at the right method and coming up with a workaround, Luke.

Changing that method's name would break the task's "api" though, and force all our projects to not only update to a new version of the task but also change actual project build files when migrating to the newer gradle versions... I still hope you can find the cause for this and maybe fix it for 1.5 ... I think we might migrate directly from 1.2 to 1.5 if it fixes the issue without having to adapt all project build files.

Looking forward to hearing from you!

Comment by Gradle Forums [ 27/Feb/13 ]

Hi,

as a newbie to gradle I found this topic very interesting. So I re-arranged your example to have a buildSrc directory:

[1]www.gradle.org/docs/current/userguide...
[2]http://stackoverflow.com/questions/13...

With this I only have to rename one method:
public void fileMappingX(File input) {

Perhaps you can replace all fileMapping(file) calls with fileMapping(file, null) ? No pretty solution either.
----------------------------------------------------------------------------------------
[1] http://www.gradle.org/docs/current/userguide/organizing_build_logic.html#sec:build_sources
[2] http://stackoverflow.com/questions/13874680/what-is-the-purpose-of-gradles-buildsrc-folder/13875350#13875350

Comment by Gradle Forums [ 27/Feb/13 ]

Unfortunately this won't make 1.5, which is close to RC stage.

Comment by Daz DeBoer [ 06/May/13 ]

From forum user 'netmikey':
I found an explanation for this issue: it looks like somewhere between ASM, Groovy and Code Generation, there's a naming issue between the class member variable fileMapping and its accessor methods and the actual fileMapping(...) methods. I guess it might be the mechanism that allows to access fields via their accessors in groovy like task.fileMapping.

I found that renaming the member variable and its accessor methods also solves the problem, (almost) without affecting its public interface

Comment by Andrew Oberstar [ 23/Oct/13 ]

This workaround does end up affecting the public API of a task, so it's not feasible if you have a heavily used task. We really need an actual fix.

Comment by Andrew Oberstar [ 23/Oct/13 ]

Using a git bisect, I narrowed the issue down to this commit. Looking at the diff, the significant break comes in AbstractClassGenerator lines 151. Instead of only dealing with single-arg methods, it was changed to apply to any non-zero arg methods, in order to provide decoration of methods whose last argument is an Action to also provide a method that takes a Closure. The important thing to note up there is that any methods with the name of a property are added to the Multimap "methods". So in the case of a class with an "environment" property and two methods "environment(String)" and "environment(String, String, String)" both will be added to the list.

Looking farther down in the generate method you see a for loop over settable properties. Since in this case the environment property has two methods in the Multimap, it will call "builder.overrideSetMethod()" twice.

Now looking at the AsmBackedClassGenerator's implementation of overrideSetMethod, note the comment at line 748 and the surrounding lines. Regardless of how many arguments the metaMethod argument has, only the first one will be grabbed and a new implementation that takes one arg will be added. So now in our example, we have passed in both "environment(String)" and "environment(String, String, String)" both of which will try to add a "environment(String)" implementation to the generated class.

My belief (as a complete layman when it comes to ASM) is that adding these two implementations is what causes the failure.

The fix would probably involve updating overrideSetMethod to return if the metaMethod doesn't have 1 argument.

Comment by Andrew Oberstar [ 23/Oct/13 ]

That does seem to work:

diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedC
index 06ec955..3a9c6e5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGen
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGen
@@ -784,7 +784,7 @@ public class AsmBackedClassGenerator extends AbstractClassGe
         }

         public void overrideSetMethod(MetaBeanProperty property, MetaMethod met
-            if (!extensible) {
+            if (!extensible || metaMethod.getParameterTypes().length != 1) {
                 return;
             }
             Type paramType = Type.getType(metaMethod.getParameterTypes()[0].get
Comment by Andrew Oberstar [ 23/Oct/13 ]

Any chance that this could get into 1.9? It is just a bug fix.

Generated at Wed Jun 30 12:28:58 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.