I have a project that reads the contents of a jar file, modifies selected static methods in specific classes in that jar file so that they point to different static methods, then rewrites the jar file. This part is working.
However, I have seen that instead of only calling the different static methods, these intercepted calls first call the original method. I need to prevent this from happening.
My code currently only uses a method with the @Advice.OnMethodExit
annotation, because I needed to ensure I am returning the correct value (from the new method). Unfortunately, the some of the original methods have side effects that can cause problems, or even cause runtime failures in the environment they would be executed in (part of the reason for modifying them in the first place).
My current 'Advice' class (for a simple test) looks like this:
class StringRedirect extends RedirectAdvice {
@Advice.OnMethodExit
public static String intercept(@Advice.Origin("#t.#m") String methodSignature,
@Advice.AllArguments Object[] args,
@Advice.Return(readOnly = false) String returnValue) throws Exception {
MethodRedirectDescriptor descriptor = REDIRECT_MAP.get(methodSignature);
if (descriptor != null) {
ClassLoader jarClassLoader = Thread.currentThread().getContextClassLoader();
// Get the method to use instead from the descriptor
Method targetMethod = descriptor.getTargetMethod(jarClassLoader);
returnValue = (String) targetMethod.invoke(null, args);
}
return returnValue;
}
}
The descriptor
is a data structure that is used to define specific pieces of the methods to be intercepted and the methods that should be called instead. And, of course, REDIRECT_MAP
is a map of these descriptors.
As I said, this is working - in so far as the new target methods are being called. However, I need to prevent the original methods from being invoked in the first place. I looked at @Advice.OnMethodEnter
but couldn't find a way to tell it to not actually invoke the original method.
The relevant code from the driver application (that reads the jar file and uses byte-buddy to modify it) looks like this:
// get class name to modify and put in className variable
// get a descriptor for that class
//
...
byte[] modifiedClass = new ByteBuddy()
.redefine(typePool.describe(className).resolve(), locator)
.visit(Advice.to(descriptor.getAdviceClass())
.on(ElementMatchers.named(descriptor.getOriginalMethodName())))
.make()
.getBytes();
Is there a way to do this?
You have multiple options here:
The easiest is to add OnMethodEnter with (skipOn = OnDefaultValue.class). Then return false from the enter advice, nothing else.
Instead of using visit, use method(...).intercept(Advice.to(...).wrap(StubMethod.INSTANCE)). This would be more efficient.
Instead of Advice in the latter construct, use MethodInterception what allows you even further manipulations, if needed, at some additional cost.