Reputation: 3564
I wrote a javaagent by myself to dynamically instrumenting the java classes with ASM (I am not using the COMPUTE_MAXS
or COMPUTE_FRAMES
of ASM, I do that manually by myself). Actually, I am just trying to use a big try-catch block for non-constructor methods to capture the uncaught Exceptions or Errors and record such events (my code is actually the revised version of the code in this question).
However, when I tried to use my javaagent in the testing process of an open-source project joda-time, the following error occured:
org.apache.maven.surefire.testset.TestSetFailedException: org.joda.time.TestAllPackages
at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:116)
at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:140)
at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:113)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "org/joda/time/DateTimeZone"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.joda.time.TestChronology.<clinit>(TestChronology.java:47)
at org.joda.time.TestAll.suite(TestAll.java:37)
at org.joda.time.TestAllPackages.suite(TestAllPackages.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.common.junit3.JUnit3Reflector.createInstanceFromSuiteMethod(JUnit3Reflector.java:157)
at org.apache.maven.surefire.common.junit3.JUnit3Reflector.constructTestObject(JUnit3Reflector.java:124)
at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:75)
... 6 more
To my understanding, each time when sun/misc/Launcher$AppClassLoader
is trying to load a class, the transform
method of ClassFileTransformer
is entered. The class is finally loaded after I modified the class and return the modified byte array. Therefore, every class should be loaded only once.
I further tried to print out the class name and the loader which loads it in the begining of the transform
method, I found that org/joda/time/DateTimeZone
appears only once, which is consistent with my understanding.
So now the only thing inconsistent with my understanding is the error. Why sun/misc/Launcher$AppClassLoader attempted duplicate class definition
with my agent? Every went so soomthly when I removed my -javaagent
option.
Upvotes: 0
Views: 1519
Reputation: 1651
I had this issue when trying to run configuration from IntelliJ IDEA - reloading the project with maven helped.
Upvotes: 0
Reputation: 44042
To compute frames, ASM needs to find a common super class of several classes at targets of jump instructions. To do so, ASM loads the classes to browse their hierarchy. If you load a class this way while it is also instrumented during its first load, the class will slready be loaded post instrumentation and you will end up with this error.
To avoid this, you can override the getCommonSuperClass method of ASM's ClassWriter. You'd need to parse the class files of these classes passed to the method rather then loading them. If you wanted and out-of-box implementation of this, you could use Byte Buddy which exposes ASM and resolves its ClassWriter that way.
Upvotes: 2