Reputation: 9059
I'm trying to apply, remove and re-apply
the bytebuddy transformation via the javaagent as the following code. The remove is worked properly via the ResettableClassFileTransformer::reset
, but the re-apply is no worked.
// The target classes
public @interface ToString {
}
public interface Greetable {
String say(final String name);
}
@ToString
public class Greeter implements Greetable {
public String say(final String name) {
return "Hello " + name;
}
}
//note the some transform is from the example
new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader,
JavaModule module) {
return builder.method(named("say"))
.intercept(FixedValue.value("transformed");
}
})
// The transformation
public class MyProgram {
public static void main(final String[] args) {
ByteBuddyAgent.install();
AgentBuilder bldr = new AgentBuilder.Default().
with(AgentBuilder.Listener.
StreamWriting.
toSystemOut()).
with(RedefinitionStrategy.
RETRANSFORMATION).
disableClassFormatChanges().
type(isSubTypeOf(
Greetable.class)).
transform(<some transform>);
ResettableClassFileTransformer resetter =
bldr.installOnByteBuddyAgent();
Greetable g1 = new Greeter();
System.out.println(g1.say("001"));
//remove the transformation
resetter.reset(ByteBuddyAgent.getInstrumentation(),
RedefinitionStrategy.RETRANSFORMATION);
Greetable g2 = new Greeter();
System.out.println(g2.say("002"));
//re-apply the transformation
bldr.installOn(ByteBuddyAgent.getInstrumentation());
Greetable g3 = new Greeter();
System.out.println(g3.say("003"));
}
}
The output is
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
transformed //note: the g1.say() is transformed.
Hello 002 //note: the g2 is an orginal
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
Hello 003 //note the re-apply has not worked.
Could you please help to advise how to achieve the re-apply?
I've updated the byte-buddy
to version 1.6.11
and changed the AgentBuilder.Transformer
. The result still same. The g3.say()
is not transformed.
My environment is
Windows 10 64Bits
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
I've changed the type matcher from interface
type(isSubTypeOf(Greetable.class))
to concrete class as
type(isSubTypeOf(Greeter.class))
The g3.say()
is transformed propery. Is there any limitation on interface matching and transforming?
Upvotes: 2
Views: 1843
Reputation: 44032
You are running an old version of Byte Buddy as I can tell from the AgentBuilder.Transformer
API. I just tried with a recent version and it works.
In practice, do you rely on the @ToString
annotation for triggering the transformation? When you transform a class that is not yet loaded, Byte Buddy reads annotations from the class file directly. For a loaded class, it uses the loaded representation. Your annotation is not annotated with a @RetentionPolicy(Retention.RUNTIME)
to retain the annotations visibility which might be the problem.
UPDATE: You need to refine your method matcher by excluding the say
method from the interface by not(isAbstract()).and(named("say"))
. Otherwise, you include the interface in your retransformation attempt. The JVM seems to silently fail the second retransformation because of this as you cannot change an abstract method into a default method after the class was loaded. Normally, this triggers a exception but in your case, the VM seems to silently suppress the error.
Upvotes: 3