Reputation: 4295
I am trying to create an agent that proxies the methods in order to do some logic before and after the method. In order to do this I use Byte Buddy with method delegation.
Agent:
//Agent code before...
private static void instrument(String agentOps, Instrumentation inst) {
new AgentBuilder.Default().with(new Eager())
.ignore(ElementMatchers.nameContains("com.dvelopp.agenttest"))
.or(ElementMatchers.hasAnnotation(ElementMatchers.annotationType(ElementMatchers.nameContains("SpringBootApplication"))))
.type((ElementMatchers.any()))
.transform((builder, typeDescription, classLoader, module) -> builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(Interceptor.class)))
.installOn(inst);
}
//Agent code after...
Interceptor:
public static class Interceptor {
@RuntimeType
public static Object intercept(@SuperCall Callable<?> superCall, @SuperMethod Method superMethod, @Origin Method currentMethod,
@AllArguments Object[] args, @This(optional = true) Object me) throws Exception {
//... logic
Object call = superCall.call();
//... logic
return call;
}
}
It works just perfect on a simple console app. But, when I have some common libraries in my classpath(e.g. simple Spring Boot project), it doesn't work as expected. It produces different errors, but most of them are connected to unexpected reflection like this:
Exception in thread "main" java.lang.NoClassDefFoundError: sun/reflect/GeneratedMethodAccessor24
at sun.reflect.GeneratedMethodAccessor24.<clinit>(Unknown Source)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:403)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:394)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:393)
at sun.reflect.MethodAccessorGenerator.generateMethod(MethodAccessorGenerator.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:53)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:85)
at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:60)
at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:135)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog$original$snrPR67N(LogAdapter.java:102)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog$original$snrPR67N$accessor$5P1QZaof(LogAdapter.java)
at org.apache.commons.logging.LogAdapter$Log4jAdapter$auxiliary$pVmPc64S.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java)
at org.apache.commons.logging.LogAdapter.createLog$original$4Ty3vM8s(LogAdapter.java:79)
at org.apache.commons.logging.LogAdapter.createLog$original$4Ty3vM8s$accessor$z3gv7aJK(LogAdapter.java)
at org.apache.commons.logging.LogAdapter$auxiliary$6cBoraQE.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7(LogFactory.java:67)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7$accessor$VdtqA6Wx(LogFactory.java)
at org.apache.commons.logging.LogFactory$auxiliary$ak9XEBl0.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7(LogFactory.java:59)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7$accessor$VdtqA6Wx(LogFactory.java)
at org.apache.commons.logging.LogFactory$auxiliary$8JhBdK8k.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java)
at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:194)
at hello.Application.main(Application.java:15)
Caused by: java.lang.ClassNotFoundException: sun.reflect.GeneratedMethodAccessor24
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 39 more
As I can see, these errors are happening with objects that are proxies:
e.g. org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7
I tried to debug that deeper and I found out that super method and origin have different classes. The "super" one has a Proxy class.
@SuperMethod Method superMethod, @Origin Method currentMethod
Is there a way to make delegation work with such objects?
Added:
On this Spring Boot 2 example(https://spring.io/guides/gs/spring-boot/) there is a clear StackOverflowError:
java.lang.StackOverflowError: null
at com.dvelopp.agenttest.MethodCallContext.getCaller(MethodCallContext.java:18)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:108)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)
Upvotes: 2
Views: 626
Reputation: 7543
It seems you are using wrong annotations, or functionality.
I'd recommend check more simplest case. For example replace your implementation:
@RuntimeType
public static Object intercept(@SuperCall Callable<?> superCall, @SuperMethod Method superMethod, @Origin Method currentMethod,
@AllArguments Object[] args, @This(optional = true) Object me) throws Exception {
return superCall.call();
}
with next one:
@RuntimeType
public static Object intercept(@RuntimeType Object value) throws Exception {
return value;
}
In this case you avoid of recursion call, but possible will get a chance to detect and analyze different issue, that is clear and has relation on your situation.
Possible you should pay attention on conceptual issue also. You know java
developers using Callable
object should never use call()
directly.
Following API description for @RuntimeType
it uses for mapping from one object to different one. New question appears: why do you need convert callable
object? In which type it is going to be converted (are you sure that without Future wrapper you'll have an object there. There could be null
that possible leads to your exception, isn't it?)?
Upvotes: 1