Using Javassist
I'm trying to insert a line of code into a method's body. It's a simple modification for a label's text color inside the intellij-IDE
. I get the following error when trying to do so:
Caused by: java.lang.LinkageError: loader com.intellij.util.lang.UrlClassLoader @d2cc05a attempted duplicate class definition for com.intellij.ui.components.labels.LinkLabel. (com.intellij.ui.components.labels.LinkLabel is in unnamed module of loader com.intellij.util.lang.UrlClassLoader @d2cc05a, parent loader 'platform')
Which implies that the class I am trying to modify is already loaded by the Java class loader (at least I like to think so..). An example of the code is given below:
private static void test() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.intellij.ui.components.labels.LinkLabel");
CtMethod cm = cc.getDeclaredMethod("getTextColor");
cm.insertBefore("System.out.println(\"helloworld\");");
cc.toClass(); // <--- the problem
} catch (final Throwable e) {
e.printStackTrace();
}
}
And for reference, here's the accessed class: intellij link
Rafael Winterhalter commented on a similar issue a couple of years ago (link). Unfortunately my knowledge with Javassist
is limiting me in finding a proper solution.
Any ideas on how to resolve this issue?
UPDATE: A minimal, repoducible example can be found here: github link
I know that this is a old question, but it is the top result on google when you search "duplicate class definition javassist" so I might as well share the possible solution.
That problem that you are experiencing is because the class is already loaded into the class pool. You will have to modify/redefine the class before the class is loaded into the class pool.
In your particular problem, you are trying to redefine
com.intellij.ui.components.labels.LinkLabel
. This class has a static field calledourVisitedLinks
. This static field will force theLinkLabel
class to be loaded on the first mentioning ofLinkLabel
class.When you call
cc.toClass()
then you are essentially making a reference the toLinkLabel
class and thereby loading the class into the class pool because the static fieldourVisitedLinks
is initialized. TheLinkLabel
class is thereby implicit loaded into the class pool just before you load the modified version of the class.I admittedly do not understand everything that is going on inside javassist. I just know that
cc.toClass()
will load the class into the class pool preemptively due to the static field being initialized.You will have to instead call
cc.toClass(SomeNeighbor.class)
. TheSomeNeighbor.class
is a fictive class that I made up for the example. It just have to be a class that is within the same package as the class that you are trying to redefine.The
LinkLabel
class has the following neighbors that share the same packagecom.intellij.ui.components.labels
:You can pick any of those 5 classes as a neighbor as long as the class does not have any direct nor transitive static dependency of
LinkLabel
.This is the corrected code of the example in the question. The github link that was shared in the question is unfortunately not longer accessible, so I can not provide a perfect executable example.