I'm working on a java agent for a personal project of mine and encountered a really peculiar error. When I run the method "register" in my IDE it works fine. No circularity error. But when I export the agent and use it with launch args javaagent:"agent.jar"
it crashes giving ClassCircularityError (Looping superclass hierarchy).
public class Refactorer implements ClassFileTransformer {
private static final Set<AbstractMatcher<String>> matchers = new HashSet<AbstractMatcher<String>>();
public static void register(AbstractMatcher<String> matcher) {
matchers.add(matcher);
}
public byte[] transform(ClassLoader loader, String name, Class<?> clazz, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {}
}
Stacktrace
java.lang.reflect.InvocationTargetException
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:497)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
Caused by: java.lang.ClassCircularityError: java/util/Set
at me.vader.Refactorer.register(Refactorer.java:42)
at me.vader.Setup.registerModders(Setup.java:30)
at me.vader.Agent.setAndAddTransformer(Agent.java:37)
at me.vader.Agent.premain(Agent.java:18)
What's really confusing me is that when I checked the superclass hierarchy in the compiled agent it printed out fine.
Tested with:
Class c = matchers.getClass();
int i = 0;
while (c != null){
System.out.println("Class (Sup x" + i+ "): " + c.getName());
c = c.getSuperclass();
i++;
}
i = 0;
c = matcher.getClass();
while (c != null){
System.out.println("Class (Sup x" + i+ "): " + c.getName());
c = c.getSuperclass();
i++;
}
Which outputs:
// The "matchers"
Class (Sup x0): java.util.HashSet
Class (Sup x1): java.util.AbstractSet
Class (Sup x2): java.util.AbstractCollection
Class (Sup x3): java.lang.Object
// The matcher being added to "matchers"
Class (Sup x0): me.vader.match.ClassMatcher
Class (Sup x1): me.vader.match.AbstractMatcher
Class (Sup x2): java.lang.Object
It seems that classes weren't being loaded in the right order. I'm not sure why, but that seems to be it.
My fix is slapping the folllowing in the Refactorer class:
static {
Serializable.class.getName();
Cloneable.class.getName();
Iterable .class.getName();
Collection.class.getName();
AbstractCollection.class.getName();
Set.class.getName();
AbstractSet.class.getName();
HashSet.class.getName();
}
Forcing them to be loaded in the correct order. It's ugly but it works.