If I have the following Class named Dog
public class Dog {
private String name = "Spike";
private int age = 3;
public Dog() {}
public String getName() {
return name;
}
}
How would I declare ByteBuddy for use in a javaagent to redefine the Dog class to provide the method getAge which returns an int?
The desired (effective) class should look like this below and be able to be invoked by doing dog.getAge()
public class Dog {
private String name = "Spike";
private int age = 3;
public Dog() {}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Attempt 1: I've tried changing the syntax to what I understand to be correct (PitBullInterceptor.class found below)
new AgentBuilder.Default()
.redefine(Dog.class)
.defineMethod("getAge", int.class, Method.PUBLIC)
.intercept(MethodDelegation.to(PitBullInterceptor.class))
.installOn(instrumentation);
I have the following issue in my attempts
The method redefine(Class) is undefined for the type AgentBuilder.Default
Which is basically a syntax error. I'm not sure which DynamicTypes or MethodDelegations/ElementMaters to use to configure the Agentbuilder to perform to this type of redefine/rebase as Rafael Winterhalter describes it
Attempt 2: I've tried using it with ByteBuddyAgent.install
public static void main(String[] args) throws Exception {
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(Dog.class)
.defineMethod("getAge", int.class, Method.PUBLIC)
.intercept(MethodDelegation.to(PitBullInterceptor.class))
.make()
.load(Dog.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
.getLoaded()
.newInstance();
}
public static class PitBullInterceptor {
private static int age = 1;
public static int getAge() {
return age;
}
}
Produces the error:
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
Attempt 3: Lastly, I've tried doing so with the agent built
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
.disableClassFormatChanges().with(RetransformationStrategy.RETRANSFORM)
.with(new ByteBuddy()
.redefine(PitBull.class)
.method(ElementMatchers.named("getAge"))
.intercept(MethodDelegation.to(PitBullInterceptor.class))
.make()
.load(Dog.class.getClassLoader()))
.installOn(instrumentation);
}
public static class PitBull {
private String name = "Kujo";
public String getName() {
return name;
}
}
public static class PitBullInterceptor {
private static int age = 3;
public static int getAge() {
return age;
}
}
Produces:
java -javaagent:agent.jar -jar app.jar
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:503)
Caused by: java.lang.IllegalStateException: Class already loaded: class io.xxx.agent.boss.Agent$PitBull
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.load(ByteArrayClassLoader.java:363)
at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$WrappingDispatcher.load(ClassLoadingStrategy.java:367)
at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:148)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6317)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6305)
at io.xxx.agent.boss.Agent.premain(Agent.java:21)
... 6 more
*** java.lang.instrument ASSERTION FAILED ***: "result" with message agent load/premain call failed at t:\workspace\open\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 422
FATAL ERROR in native method: processing of -javaagent failed, processJavaStart failed
Due to limitations of the JVM, you can't add, remove or modify members of existing classes. You have to either
Alternatively, you could use third party JVMs that do support that kind of modification (see: JetBrains runtime with the Enhanced class redefinition flag), but assuming this needs to run universally, it'd be a bit of a burden.