I've been searching for the answer to "how to add an annotation to the method at runtime" for several days already and found this awesome tool called Byte Buddy, played with it, but still cannot make it work as I need to. I'm sure it must be able to do that from this question Can Byte Buddy create fields and method annotations at runtime?
Having this class:
public class ClassThatNeedsToBeAnnotated {
public void method(int arg1, String arg2) {
// code that we don't want to touch at all, leave it as is
System.out.println("Called method with arguments " + arg1 + " " + arg2);
}
public void method() {
System.out.println("Called method without arguments");
}
}
and this code:
public class MainClass {
public static void main(String[] args) {
ByteBuddyAgent.install();
AnnotationDescription description = AnnotationDescription.Builder.ofType(MyClassAnnotation.class)
.define("value", "new")
.build();
new ByteBuddy()
.redefine(ClassThatNeedsToBeAnnotated.class)
.annotateType(description)
.make()
.load(ClassThatNeedsToBeAnnotated.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
}
}
It is easy to add an annotation to the class.
But for the method, it seems to be not possible without changing the method implementation.
Method existingMethod = ClassThatNeedsToBeAnnotated.class.getDeclaredMethods()[0];
AnnotationDescription annotationDescription = AnnotationDescription.Builder.ofType(MyMethodAnnotation.class)
.build();
new ByteBuddy()
.redefine(ClassThatNeedsToBeAnnotated.class)
.annotateType(description)
.method(ElementMatchers.anyOf(existingMethod))
// here I don't want to intercept method, I want to leave the method code untouched. How to do that?
.annotateMethod(annotationDescription)
.make()
.load(ClassThatNeedsToBeAnnotated.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
I'm sure that I just don't do it right but unfortunately cannot find an example when there is no code change for the method and only annotation change.
You found a blindspot in Byte Buddy that I thought of fixing for a while. Early versions of Byte Buddy did not allow for defining annotations but when it did, the API was already so widely used that I could not change it and it would require some bits in the implementation, too.
If you are willing to pay the minimal price of adding a synthetic method, you can rebase the class instead:
new ByteBuddy().rebase(ClassThatNeedsToBeAnnotated.class)
Doing so, you can just use the current API and add an implementation of SuperMethodCall. This will invoke the very same method in a rebasement.
This enhancement of Byte Buddy is tracked here: https://github.com/raphw/byte-buddy/issues/627
UPDATE: In the upcoming Byte Buddy 1.10.0 this is possible by:
new ByteBuddy()
.redefine(ClassThatNeedsToBeAnnotated.class)
.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(someAnnotation)
.on(matcher))
.make();
Annotation instance can be get by:
AnnotationDescription.Latent.Builder.ofType(AnnotationClass.class).build()
Related
Given:
public<?> void methodName(? input){
var something = ?.GetItNow();
}
We have a ton of classes that were auto-generated from WSDL. Almost all of them have the same methods, but there is no way to have them implement an interface for those same methods. Assume all these different classes have a method named GetItNow(). My question is how do I set up a Generic method to allow the code above to work?
When using the "?" do I have to always use the "extends" key-word? If so how do I extend any object so I don't see compile errors that "method doesn't exist.
Note the code above is for illustration purposes only and does not do anything (yet).
You could use reflection to access the method, provided the name is known beforehand.
public void methodName(Object input){
try{
Method method = input.getClass().getMethod("GetItNow");
RETURN_TYPE returnValue = (RETURN_TYPE)method.invoke(input);
}catch(Exception e){
throw new RuntimeException("Error while invoking method",e);
}
}
Using ByteBuddy, can I implement one instance method by calling another and transforming the result?
For instance (toy example):
public abstract class Foo {
public String bar() {
return "bar";
}
public abstract int baz();
}
Given the above, can I implement baz such that it calls bar() and returns the length of the returned string? I.e., as if it were:
public int baz() {
return bar().length();
}
Naively, I tried the following:
Method bar = Foo.class.getDeclaredMethod("bar");
Method baz = Foo.class.getDeclaredMethod("baz");
Method length = String.class.getDeclaredMethod("length");
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(
MethodCall.invoke(bar) // call bar()...
.andThen(MethodCall.invoke(length)) // ... .length()?
).make()
.load(Foo.class.getClassLoader())
.getLoaded()
.newInstance();
System.out.println(foo.baz());
However, it looks like I was wrong in thinking andThen() is invoked on the return value of the first invocation; it looks like it's invoked on the generated instance.
Exception in thread "main" java.lang.IllegalStateException:
Cannot invoke public int java.lang.String.length() on class Foo$ByteBuddy$sVgjXXp9
at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation
.invoke(MethodCall.java:1667)
I also tried an interceptor:
class BazInterceptor {
public static int barLength(#This Foo foo) {
String bar = foo.bar();
return bar.length();
}
}
with:
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(MethodDelegation.to(new BazInterceptor()))
// ...etc.
This ran, but produced the nonsensical result 870698190, and setting breakpoints and/or adding print statements in barLength() suggested it's never getting called; so clearly I'm not understanding interceptors or #This properly, either.
How can I get ByteBuddy to invoke one method and then invoke another on its return value?
Per k5_'s answer: BazInterceptor works if either:
we delegate to new BazInterceptor(), as above, but make barLength() an instance method, or:
we leave barLength() a class method, but delegate to BazInterceptor.class instead of to an instance.
I suspect the 870698190 was delegating to hashCode() of the BazInterceptor instance, though I didn't actually check.
There is not currently a good way in Byte Buddy but this would be an easy feature to add. You can track the progress on GitHub. I will add it once I find some time.
If you want to implement such chained calls today, you can implement them in Java code and inline this code using the Advice component. Alternatively, you can write the byte code more explicitly by creating your own ByteCodeAppender based on MethodInvocation instances where you have to load the arguments manually however.
You use an instance as interceptor, that means instance methods are prefered (maybe static method are not accepted at all). There is an instance method that matches the signature of your int baz() method, it is int hashCode(). The number you are getting is the hashcode of the new BazInterceptor() instance.
Options i am aware of:
Remove static from barLength that way it will actually be used for interception.
Add the class as interceptor .intercept(MethodDelegation.to(BazInterceptor.class))
I would prefer the second option as you are not using any fields/state of the BazInterceptor instance.
Suppose I have a method m:
public void m() {
String foo = "foo";
int bar = 0;
doSomething(foo, bar);
}
I want to use ByteBuddy to instrument the code so that when calling doSomething in m, it will automatically put the value of foo and bar into a HashMap, pretty much something looks like:
public void m() {
String foo = "foo";
int bar = 0;
context.put("foo", foo); // new code injected
context.put("bar", bar); // new code injected
doSomething(foo, bar);
}
Is there anyway to do this instrumentation via ByteBuddy?
There is built-in way in Byte Buddy to do redefine method m in this way. Byte Buddy is however voluntarily exposing the ASM API on top of which Byte Buddy is implemented. ASM offers quite extensive documentation which would show you how to do this. I can however tell you that it will be quite a lot of code. Note that you require to compile any method with debug symbols enabled, otherwise these internal variables are not available at run time.
Are you however sure you want to do this? Without knowing your exact use case, it feels like it is a bad idea. By implementing this solution, you make the names of local variables a part of your application instead of letting them be an implementation detail.
I would therefore suggest you to rather instrument the doSomething method. Would this suffice yourn what is easily done in Byte Buddy using an interceptor like the following:
class Interceptor {
void intercept(#Origin Method method, #AllArguments Object[] args) {
int index = 0;
for(Parameter p : method.getParameters()) {
context.add(p.getName(), args[index++]);
}
}
}
This interceptor could then be used as follows:
MethodDelegation.to(new Interceptor()).andThen(SuperMethodCall.INSTANCE);
Given the following code:
LinkedList list = mock(LinkedList.class);
doCallRealMethod().when(list).clear();
list.clear();
by executing this test, a NullPointerException is thrown from first line in LinkedList#clear:
public void clear() {
Entry<E> e = header.next;
while (e != header) {
Entry<E> next = e.next;
//Code omitted.
but header has been instantiated before:
private transient Entry<E> header = new Entry<E>(null, null, null);
Could someone please explain what's happening during mock creation?
####### UPDATE. ######
Having read all answers especially Ajay's one, I looked into Objenesis source code and find out that it's using Reflection API to create the proxy instance (through CGLIB) and therefore bypassing all constructors in the hierarchy until java.lang.Object.
Here is the sample code to simulate the issue:
public class ReflectionConstructorTest {
#Test
public void testAgain() {
try {
//java.lang.Object default constructor
Constructor javaLangObjectConstructor = Object.class
.getConstructor((Class[]) null);
Constructor mungedConstructor = ReflectionFactory
.getReflectionFactory()
.newConstructorForSerialization(CustomClient.class, javaLangObjectConstructor);
mungedConstructor.setAccessible(true);
//Creates new client instance without calling its constructor
//Thus "name" is not initialized.
Object client = mungedConstructor.newInstance((Object[]) null);
//this will print "CustomClient"
System.out.println(client.getClass());
//this will print "CustomClient: null". name is null.
System.out.println(client.toString());
} catch(Exception e) {
e.printStackTrace();
}
}
}
class CustomClient {
private String name;
CustomClient() {
System.out.println(this.getClass().getSimpleName() + " - Constructor");
this.name = "My Name";
}
#Override
public String toString() {
return this.getClass().getSimpleName() + ": " + name;
}
}
You are only asking Mockito to call the real thing on clear, the underlying object is still a fake created by Mockito for you. If you need a real LinkedList then just use the LinkedList - only the most heated purist of BDD would tell you to mock everything around you. I mean, you are not mocking Strings are you?
Mockito author himself has said that calling the real thing should be used scarcely, usually only for testing a legacy code.
If you need to spy on the real object (track the invocations) then Mockito has a feature for this too:
List list = new LinkedList();
List spy = spy(list);
With spy, you can still stub a method if you need. It basically works like a mock, but isn't ;)
Your reasoning is flawless.
The key issue is that you are not operating on the actual LinkedList object. Here is what is happening behind the scenes:
The object that you are given by Mockito's mock() is an Enhancer object from the CGLIB library.
For me it is something like java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28
which kind of acts like a Proxy, albeit with the fields set to default values. (null,0 etc)
When you mock a class the object you are using is a fake, therefore the variables are not instantiated and the methods don't work as expected. You could use reflection to set a value for the header but I really wouldn't recommend this. As theadam said, the best thing to do would be to just use a list.
Say if I have a class named Car I can use the following line of code in certain situations.
Car.class
My question is there a way I can make the same type of call if a user supplies a class name at run time. Have tried something similar to the below but no joy, is there a way i can do it.
String className = "Car";
Class.forName(className ).class;
Also I need to be able to cast dynamically, if the user specifies a list of objects I need to be able to dynamically cast.
e.g. instead of Car myCar = (Car) object
I need to be able to have to the user specify the name/type of class at run time so that I need to be able to do something along the lines of ClassName myObj = (ClassName) object.
Class.forName("Car") already returns the same as Car.class.
For casting, you can then use Class.forName("Car").cast(object), which would return a Car object. Take a look at the API, mostly the java.lang.Class part of it.
Also, since you're casting # runtime, there's no type safety, and you should check whether object extends or implements Car before doing it, otherwise you'll get an exception. A question I asked ~ a year ago and the answers there may be relevant to you as well.
Though, as others already said, this smells & you could probably redesign it in a better way, also note that this type of casting will typically be pretty slow because Java needs to examine the type hierarchy (it needs to throw a ClassCastException if it can't cast to Car).
Given the nature of the question, most of the answers to this are straight from the Reflection API documentation. I would suggest you take a look at this: http://docs.oracle.com/javase/tutorial/reflect/class/index.html. If this does not help and you need help with something specific, we can look at that.
What you are looking for is a feature called Reflection in the Java programming language.
It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
A Simple Example from http://java.sun.com
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[])
{
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
For an invocation of:
java DumpMethods java.util.Stack
the output is:
public java.lang.Object java.util.Stack.push(
java.lang.Object)
public synchronized
java.lang.Object java.util.Stack.pop()
public synchronized
java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized
int java.util.Stack.search(java.lang.Object)
Here is an example of creating objects at runtime:
import java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
You can read more about it here and here - for indepth view
Also look here:
What is reflection and why is it useful?
You want to interact with myObj, so rather than going through these gymnastics, think about adding an interface that models the interactions you want to have with the objects, then use that interface in the code. The classes supplied by the user can then be validated to implement the necessary interface and errors raised appropriately.
The expression Car.class returns the java.lang.Class object for class Car.
A statement Class.forName("Car") will also return the java.lang.Class object for class Car (assuming that class Car is in the default package). Note: No need to append .class; that would give you the Class object of class Class itself, which is not what you want.
Class Class has methods to check if an object is an instance of the class that the Class instance represents (hope this is not too confusing...). Since you don't know the name of class Car at compile time, you're not going to have any kind of compile time type safety.
Lookup the API documentation of java.lang.Class.