ByteBuddy MethodDelegation not working in Java Agent - java

I have a premain() wherein all methods annotated with a certain annotation should be delegated to a certain class. In general, i looks like this:
public static void premain( final String agentArguments, final Instrumentation instrumentation ) {
CountingInterception ci = new CountingInterception();
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(com.codahale.metrics.annotation.Counted.class))
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(ci))
).installOn(instrumentation);
}
Using a debugger shows that this part is processed but if an annotated method is called, nothing happens.
The CountingInterception looks like this
public class CountingInterception {
#RuntimeType
public Object intercept(#DefaultCall final Callable<?> zuper, #Origin final Method method, #AllArguments final Object... args) throws Exception {
String name = method.getAnnotation(Counted.class).name();
if (name != null) {
// do something
}
return zuper.call();
}
}
Thanks for any hints!
Using ByteBuddy 1.6.9

To achieve what I wanted to do, the following changes were made:
In premain:
CountingInterception ci = new CountingInterception();
new AgentBuilder.Default()
.type(declaresMethod(isAnnotatedWith(Counted.class)))
.transform((builder, type, classLoader, module) -> builder
.method(isAnnotatedWith(Counted.class))
.intercept(MethodDelegation.to(ci).andThen(SuperMethodCall.INSTANCE))
).installOn(instrumentation);
and in CountingInterception:
public void interceptor(#Origin final Method method) throws Exception {
String name = method.getAnnotation(Counted.class).name();
if (name != null) {
// do something
}
}

I assume that you are trying to do something different than a Java 8 default method call. Did you mean to use #SuperCall which invokes a super method?
I would suggest you to:
1. Reduce your interceptor to do nothing. Create an interceptor that chains your MethodDelegation with a SuperMethodCall.
2. Register an AgentBuilder.Listener to write errors to the console.
I am sure Byte Buddy cannot bind your methods as your interceptor can only be applied to classes that offer a default method implementation.

Related

Mockito - verify a method with Callable<> as parameter [duplicate]

I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
bootStrapper.start();
}
}
#Component
public class AppBootStrapper {
private NetworkScanner networkScanner;
private PacketConsumer packetConsumer;
public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
this.networkScanner = networkScanner;
this.packetConsumer = packetConsumer;
}
public void start() {
networkScanner.addConsumer(packetConsumer::consumePacket);
networkScanner.startScan();
}
}
#Component
public class NetworkScanner {
private List<Consumer<String>> consumers = new ArrayList<>();
public void startScan(){
Executors.newSingleThreadExecutor().submit(() -> {
while(true) {
// do some scanning and get/parse packets
consumers.forEach(consumer -> consumer.accept("Package Data"));
}
});
}
public void addConsumer(Consumer<String> consumer) {
this.consumers.add(consumer);
}
}
#Component
public class PacketConsumer {
public void consumePacket(String packet) {
System.out.println("Packet received: " + packet);
}
}
#RunWith(JUnit4.class)
public class AppBootStrapperTest {
#Test
public void start() throws Exception {
NetworkScanner networkScanner = mock(NetworkScanner.class);
PacketConsumer packetConsumer = mock(PacketConsumer.class);
AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);
appBootStrapper.start();
verify(networkScanner).addConsumer(packetConsumer::consumePacket);
verify(networkScanner, times(1)).startScan();
}
}
I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:
Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546#282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapper$$Lambda$7/920446957#5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)
From the exception, clearly the function pointers aren't the same.
Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.
Any help, perspectives on this would be greatly appreciated.
Java doesn't have any concept of "function pointers"; when you see:
networkScanner.addConsumer(packetConsumer::consumePacket);
What Java actually compiles is (the equivalent of):
networkScanner.addConsumer(new Consumer<String>() {
#Override void accept(String packet) {
packetConsumer.consumePacket(packet);
}
});
This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.
If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.
A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.
See the comments and answer on this SO question.

Efficiently get caller in byte buddy's MethodDelegation

I am trying to build a call tree in my java agent with byte buddy library. To add elements to the tree I want to use method delegation. However, to make sure who is the parent of any leaf, I need to know who called the method.
I don't want to use:
sun.reflect.Reflection#getCallerClass(int)
Because it's deprecated and unavailable in Java 8+.
Also, I tried:
public class ThreadUtil {
public static StackTraceElement getCaller() {
Instant now = Instant.now();
StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
String callerClass = ste.getClassName();
String callerMethod = ste.getMethodName();
Instant now2= Instant.now();
System.out.println(Duration.between(now, now2));
return ste;
}
}
But, it's extremely slow(~1 ms - too much if I have thousands of calls).
Is there a way to get caller efficiently at this point (maybe some byte buddy's tricks)?
P.S.
My agent:
private static void instrument(String agentOps, Instrumentation inst) {
System.out.println("Agent");
new AgentBuilder.Default().with(new Eager())
.ignore(ElementMatchers.nameContains("com.dvelopp.agenttest"))
.type((ElementMatchers.any()))
.transform((builder, typeDescription, classLoader, module) -> builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(Interceptor.class))).installOn(inst);
}
public static class Interceptor {
#RuntimeType
public static Object intercept(#SuperCall Callable<?> zuper, #Origin Method method,
#AllArguments Object[] args, #This(optional = true) Object me) throws Exception {
System.out.println("CURRENT: " + method.getDeclaringClass().getName());
System.out.println("CALLER: " + ThreadUtil.getCaller().getClassName());
return zuper.call();
}
}
ENV: Java 8
Byte code instrumentation only allows you to generate code that you could also write yourself. For your case, you would need to create a fairly intrusive instrumentation that I would not recommend:
Instrument your target method to accept a new parameter of type Class.
Instrument every caller to supply their type as an additional argument.
The better solution is surely what Holger suggested in the comments. Use StackWalker and if not available, fall back to sun.reflect.Reflection (which is present in all JVMs I know of).

How to get arrgument of a method using bytebuddy

I am trying to use metrics with a java application to find out performance. I used java agent with bytebuddy of get metrics.In my testing program the method that i want to check is running several time. only i need to get metrics when it passing a parameter contain name 'connector'. So i want to get this using bytebuddy and i used #AllArguments Object[] args for this . But i try to used this my TimerAdvice class not running.
This is my code
class Agent {
public static void premain(String arguments, Instrumentation instrumentation) {
System.out.println("Premain");
new AgentBuilder.Default()
.with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
.type((ElementMatchers.nameContains("ConnectorCallback")))
.transform(
new AgentBuilder.Transformer.ForAdvice()
.include(MethodListner.class.getClassLoader())
.advice(ElementMatchers.any(), MethodListner.class.getName())
).installOn(instrumentation);}}
This my TimerAdvice class
public class TimerAdvice {
#Advice.OnMethodEnter
static void enter(#Advice.Origin String method , #AllArguments Object[] args)throws Exception {
if (changeMethodName(method).equals("BalConnectorCallback")) {
//Metrics works
}
}
#Advice.OnMethodExit
static void exit(#Advice.Origin String method, #AllArguments Object[] args) throws Exception {
if (changeMethodName(method).equals("done")) {
//Metrics works
}
}
public static String changeMethodName(String method) {
String newMethod = method.substring(0, method.lastIndexOf('('));
newMethod = newMethod.substring(newMethod.lastIndexOf('.') + 1);
//newMethod = newMethod.replace(".", " ");
return newMethod;
}}
When i am using #AllArguments Object[] args this only TimerAdvice not working without it its work perfectly.Is this problem in my code ?
Any Help..
You are probably importing the wrong annotation. The annotation that you are looking for is #Advice.AllArguments.
This naming collission is unfortunate but it is too late to change that. All advice-comatible annotations are prefixed. Tge others are meant to be used with method delegation.

Can I ignore aspect of a method while mocking it using Mockito?

I have a class with a few methods advised through an input validation aspect (validates whether all input parameters are not-null/non-empty strings).
I am facing an issue while writing test case for them and want to verify if this is indeed a bad design issue.
Here's a very simplified version of my class:
public class A {
public String one(String word) {
// Some actions
String val = two(word2);
// Some more actions
}
protected String two(String word) {
// Some actions
}
}
Now while writing test cases for one() I use Mockito and want to mock calls to two(). So I use:
#Spy
A a;
#Test
void test() {
doReturn("Bye").when(A).two(Mockito.anyString());
a.one("hello");
// Some validations
}
This test fails as the: doReturn() line fails with input being empty for two().
Should I not mock two() or can I make this work somehow?
Edit:
Adding a more specific example related to the two methods being present in two different classes as requested:
Create a page through a WebService. This builds a putRequest, executes it and returns a response.
public class AUtility implements BaseUtility {
public Response create(Params params) {
try {
PutMethod putRequest = buildPUTRequest(params.getAttr1(), params.getAttr2());
return Utils.buildResponse(client.executeMethod(putRequest),
params.getAttr3(),
params.getAttr4());
} catch (Exception e) {
throw new AppException(e);
}
}
}
The put request marshals the data into a file to write it through the HttpClient
private PutMethod buildPUTRequest(final String url, final Object obj) throws IOException, JAXBException {
// Create a temp file to store the stream
File tempFile = File.createTempFile(APPLICATION_LABEL, XML_LABEL);
decoder.marshal(obj, tempFile);
// Build the put method
return putMethod;
}
XMLMarshaller
public interface XMLDecoder implement Decoder {
public void marshal(Object obj, File tempFile) throws IOException, JAXBException {
// Perform marshalling operations
}
}
The test fails on line2 with the inputs being null.
#Test
public void createPageParamsHttpException() throws HttpException, IOException, JAXBException {
expectedException.expect(AppException.class);
doNothing().when(decoder).marshal(Mockito.anyString(), Mockito.any(File.class));
doThrow(HttpException.class).when(client).executeMethod(Mockito.any(HttpMethod.class));
Params params = new Params(new Application(),
APPLICATION_URL_LABEL,
SITE_NAME_LABEL,
URL_WITHOUT_HTTP_N_HTML);
utility.createPage(params);
}
Any idea how should I proceed for the same?
You don't want to do this.
You are inherently changing the behavior of the class. If you change what two() does, how do you know that one() will do what it's supposed to do in production?
If you truly want to do this, you should extract the behavior of two() into another top level class, and then inject the dependency into A. Then you can mock this dependency and you don't have to worry about going to the trouble of creating a partial mock for A.
In a similar vein, if you must keep two in the same class (because it's behavior is part of the same responsibility that is assigned to A - see the Single Responsibility Principle - why is it public?
The reason you are having trouble is because you are violating the SRP, see my note above. You said this:
This builds a putRequest, executes it and returns a response.
You should not be trying to test the behavior of all three of those things at the same time. Ultimately, this method does not really do anything. The buildPUTRequest method does, and shouldn't be in a class called AUtility, it should be in a class RequestFactory. Then, you would want to test the Utils.buildResponse method, except that shouldn't be in a class called Utils, it should be in a class called Responder or something... and this method ABSOLUTELY should not be static.
Work on naming your classes better things, and if you can't come up with a good name, that means the class probably does too much and should be refactored. And a method that wraps the work in two other methods doesn't need to be unit tested. Integration tested, perhaps, but that's another story.

CGLIB not able to intercept methods in a superclass/superinterface

May be I'm not thinking hard enough or the answer is really elusive. Quick scenario (Try the code out. It compiles).
Consider a legacy interface
public interface LegacyInterfaceNoCodeAvailable{
void logInfo(String message);
}
The consider a legacy implementation of the interface above
public abstract class LegacyClassNoCodeAvailable implements LegacyInterfaceNoCodeAvailable{
public abstract void executeSomething();
public void rockItOldSchool(){
logInfo("bustin' chops, old-school style");
}
#Override
public void logInfo(String message){
System.out.println(message);
}
}
Now I come in as this ambitious person and writes a class for a 'New' system but that runs inside the 'Legacy' framework, hence I have to extend the legacy base class.
public class lass SpankingShiny extends LegacyClassNoCodeAvailable{
public void executeSomething(){
rockItOldSchool();
logInfo("I'm the King around here now");
System.out.println("this new stuff rocks!!");
}
}
Everything works great, just like you would expect:
SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();
The above code yields (as expected):
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
Now as you can see, the 'System.out.println()' faithfully prints the desired output. But I wish to replace the 'System.out.println()' with a logger.
Problem:
I'm unable to have the CGLIB proxy intercept the method to 'logInfo(string)' and have it print out my desired message through a logger (I have done the logging configuration right by the way). That method invocation 'apparently' does not hit the proxy.
Code:
public class SpankingShinyProxy implements MethodInterceptor{
private SpankingShiny realShiny;
private final Logger logger = Logger.getLogger(SpankingShinyProxy.class);
public SpankingShinyProxy(SpankingShiny realShiny) {
super();
this.realShiny = realShiny;
}
#Override
public Object intercept(Object proxyObj, Method proxyMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
String methodName = proxyMethod.getName();
if("logInfo".equals(methodName)){
logger.info(methodParams[0]);
}
return proxyMethod.invoke(realShiny, methodParams);
}
public static SpankingShiny createProxy(SpankingShiny realObj){
Enhancer e = new Enhancer();
e.setSuperclass(realObj.getClass());
e.setCallback(new SpankingShinyProxy(realObj));
SpankingShiny proxifiedObj = (SpankingShiny) e.create();
return proxifiedObj;
}
}
Main method:
public static void main(String... args) {
SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();
SpankingShiny shinyO = SpankingShinyProxy.createProxy(shiny);
shinyO.executeSomething();
}
The above code yields (NOT as expected):
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
Where would I be going wrong?
Thanks!
I had the same problem. In my case, the realObj was a proxy itself (a Spring Bean - a #Component).
So what I had to do was change the .setSuperClass() part in:
Enhancer e = new Enhancer();
e.setSuperclass(realObj.getClass());
e.setCallback(new SpankingShinyProxy(realObj));
SpankingShiny proxifiedObj = (SpankingShiny) e.create();
I changed:
e.setSuperclass(realObj.getClass());
To:
e.setSuperclass(realObj.getClass().getSuperClass());
This worked because, as said, realObj.getClass() was a CGLIB proxy itself, and that method returned a crazy-name-CGLIB-generated class, such as a.b.c.MyClass$$EnhancerBySpringCGLIB$$1e18666c. When I added .getSuperClass() it returned the class it should have been returning in the first place.
Well, first of all, you are lucky that your proxy is not hit. If you were referencing the actual proxy within intercept, you would end up with an endless loop since your reflective method incocation would get dispatched by the same SpankingShinyProxy. Again and again.
The proxy is not working since you simply delegate the method call executeSomething on your proxy to some unproxied object. You must not use realObj. All method calls must be dispatched by your proxy, also those method calls that are invoked by the must hit the proxy itself!
Change the last line in your intercept method to methodProxy.invokeSuper(proxyObj, args). Then, construct your object by using the Enhancer. If your constructor for SpankingShiny does not need arguments, calling create without any arguments if fine. Otherwise, supply the objects you would normally supply to the constructor to the create method. Then, only use the object that you get from create and you are good.
If you want more information on cglib, you might want to read this blog article: http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

Categories