Chain/transform method calls with ByteBuddy - java

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.

Related

Define a method, call and interceptor and delegate to target with ByteBuddy?

I have an object service that has several methods, one of those methods is foo(arg1, arg2).
Want to create a new wrapper class that:
has single method _foo with one additional argument
delegate _foo execution to an interceptor, return is ignored
finally, delegate call to target foo on service.
Somehow, I am failing to do so:
final List<Class<?>> parameters =
Arrays.stream(fooMethod.getParameters())
.map(Parameter::getType)
.collect(Collectors.toList());
parameters.add(AdditionalParameter.class);
final DynamicType.Unloaded unloadedType = new ByteBuddy()
.subclass(Object.class)
.implement(interfaceType)
.name(service.getClass().getSimpleName() + "Dynamic")
.defineMethod(
"_" + methodName,
resolveReturnType(fooMethod),
Modifier.PUBLIC)
.withParameters(parameters)
.intercept(to(fooInterceptor).andThen(
MethodCall.invoke(fooMethod).on(service)
))
.make();
fooInterceptor is an InvocatiomHandler instance:
public class FooInterceptor implements InvocationHandler {
public Object invoke(
#This final Object proxy,
#Origin final Method method.
#AllArguments final Object[] args) {
...
}
}
The exception says that my fooService "does not accept 0 arguments".
May I call service.foo() from the interceptor - but not using reflection? I am not being able to do so (but didn't play with that part yet).
Help?🙏
EDIT: I have no control in what methods are in service so I can't simply use to(service) with intercept call; there might be a case where ByteBuddy would not be able to find the matching method.
EDIT2: If I can just 'tell' ByteBuddy the name of target method to bind, that would be awesome. Then I could use to(service) with given hint.
You can supply a matcher to MethodDelegation to narrow down the methods to consider:
MethodDelegation.withDefaultConfiguration().filter(...).to(...)
As for your MethodCall, you need to specify what arguments to include, foo takes two arguments. Since your original arguments seems to be equivalent, you can set:
MethodCall.invoke(fooMethod).on(service).withAllArguments();

Check if method is not called with Mockito

I have a void method that creates objects of type Foo and updates each entry of a list those objects.
When the list passed by parameter is empty it shouldn't create any object, thus not calling any setter method of class Foo.
public static void analyzeList(ArrayList<Something> list) {
for (Something s : list) {
Foo f = new Foo();
f.setSomething(someMethod1(f));
f.setSomething2(someMethod2(f));
s.setFoo(f);
}
}
So, I am trying to use Mockito to check that method analyzeList(ArrayList<Something> list) doesn't call any setter method of class Foo, but am having a hard time doing so, since I never worked with Mockito before.
I wrote this test:
#Test
public void shouldNotCallSettersWhenListIsEmpty() {
Foo fooMocked = mock(Foo.class);
FooLogic.analyzeList(emptyList);
Mockito.verify(fooMocked, times(0)).setSomething(anyInt());
}
This test passes, and it should. But when I change times(0) to times(1), the test keeps passing and it shouldn't, since the list is empty.
What am I doing wrong here?
The Foo object you have in your test and the Foo objects you create in your analyzeList are different. You are creating a new Foo object each time in your code.
Even if you pass a non-empty list Mockito.verify(fooMocked, times(0)).setSomething(anyInt()); will pass because you are not calling any methods on the mocked Foo instance.
But you say that by changing times(0) to times(1), the test still passes. But the test should fail (can you re-check this)
With the current setup, you cannot verify anything on the created Foo f instances1. You can still verify the setFoo calls you make on Something if the list you pass has mock Something objects.
1unless you use Powermockito or something that allows you to mock new object creations in the code.
In your situation,
I like to use the
InOrder
functionality;
specifically the
InOrder.verifyNoMoreInteractions()
method.
Here is an example:
import static org.mockito.Mockito.inOrder;
import org.mockito.InOrder;
#Test
public void shouldNotCallSettersWhenListIsEmpty() {
Foo fooMocked = mock(Foo.class);
InOrder inOrderVariable = inOrder(fooMocked);
FooLogic.analyzeList(emptyList);
//Mockito.verify(fooMocked, times(0)).setSomething(anyInt());
// Note: added example verify statement.
inOrderVariable.verify(fooMocked).someMethod(paramaters)
inOrderVariable.verifyNoMoreInteractions();
}

No parentheses in java

I am learning Java and I have learned that methods use parentheses for passing parameters. However, I have also noticed that sometimes I see code which to me looks like a method but it does not have parentheses.
MyObject.something()
MyObject.somethingElse
Where somethingElse does not have parentheses. I assume this is similar to how an arrayList has the size method for getting its size:
myList.size()
whereas an array has length to get its size, which does not have parentheses:
myArray.length
Is my assumption correct? If not, what is the difference?
This is probably an elementary question, but because of the amount of words I need to explain this problem, I have had trouble searching for it.
somethingElse is a property (data member), not a method. No code in the class is run when you access that member, unlike with a method where code in the class is run.
Here's an example:
public class Foo {
public int bar;
public Foo() {
this.bar = 42;
}
public int getBlarg() {
// code here runs when you call this method
return 67;
}
}
If you create a Foo object:
Foo f = new Foo();
...you can access the property bar without parens:
System.out.println(f.bar); // "42"
...and you can call the method getBlarg using parens:
System.out.println(f.getBlarg()); // "67"
When you call getBlarg, the code in the getBlarg method runs. This is fundamentally different from accessing the data member foo.
it is a class field which isn't a private field (usually it can be protected,package or public), so you can take it straight from your class. Usually fields are private, so you cannot take it like this outside your class definition.
myList.size() call a method defined in list class (public defined)
myArray.length call a property in array class not method
public class MyClass{
public int length;
public int size(){
....
}
}
MyClass mc =new MyClass();
mc.length;
mc.size();
This is triggering method called something of the instantiated object called someObject.
someObject.something();
This is accessing a property of the object called someObject (property which is public, most probably).
someObject.name

call get methods for different classes behind one another

I have a class with name "ConstituentSet". it has one method namely "getNucleusInConstSet()" which the output will be from "Proposition" class . The new Class "Proposition" have another method namely "getProperty()". I want to know what is the Propertry of my "Proposition Nucleus" in class "ConstituentSet". but i do not know how can i do that.
I wrote as follow but It does not work. (ConstituentSet.getNucleusInConstSet()).getProperty())
public class ConstituentSet{
// Constructor
private Proposition nucleusInConstSet;
public Proposition getNucleusInConstSet() {
return nucleusInConstSet;
}
}
public class Proposition{
//Constructor
private Property property;
public Property getProperty() {
return this.type;
}
}
You have:
(ConstituentSet.getNucleusInConstSet()).getProperty()
But you need to call an instance of ConstituentSet
e.g.
ConstituentSet cs = new ConstituentSet();
cs.getNucleusInConstSet().getProperty();
Note that this idiom (chained method calls) can be a pain. If one of your methods returns null, it's difficult to understand which one it is (without using a debugger). Note also that invocations of the form a().b().c().d() are a subtle form of broken encapsulation (a reveals that it has a b, that reveals it has a c etc.)
if you type ((ConstituentSet.getNucleusInConstSet()).getProperty()) you are attempting to call a static method of ConstituentSet.
You need to instantiate it and then call on that object.
ConstituentSet anInstanceOf = new ConstituentSet();
anInstanceOf.getNucleusInConstSet()).getProperty());
This won't work:
ConstituentSet.getNucleusInConstSet().getProperty();
Because the getNucleusInConstSet() method is not static. You have to use an instance of ConstituentSet, something like this:
ConstituentSet cs = new ConstituentSet();
cs.getNucleusInConstSet().getProperty();
Of course, you have to make sure that nucleusInConstSet is not null, or you'll get a NullPointerException. Initialize its value in ConstituentSet's constructor or set it using setNucleusInConstSet().
Alternatively, you could make getNucleusInConstSet() static, but I don't think that's the right thing to do in this case (but we don't have enough information about the problem to say so).

How to get a handle to a new Object while unit testing?

How would I test this ValueObject created on the fly in SomeClass without stubbing it out using PowerMock etc?
class ValueOBject {
private String x, y;
getters
setters
}
class SomeClass {
public void foo () {
ValueObject vo = new ValueObject();
vo.setX("some string 1");
vo.setY("some string 2");
faa(vo);
}
public void faa(ValueObject vo) {
// do some more logic on vo here
}
}
class SomeClassTest extends TestCase{
public void testFoo() {
SomeClass s = new SomeClass();
s.foo();
// HOW TO DO THIS?
verify(faa(VAlueObject that is created during foo's run));
}
}
I dont want to use PowerMockito or similar libraries to return a MOCKED object!
Rather a REAL object thats created during execution of the method foo() :
// DONT WANT A MOCKED OBJECT
// #Mock
// private ValueOBject vo;
// henNew(GetEmailInformation.class).withNoArguments().thenReturn(getEmailInformation);
The ValueObject is created internally, so it can only depend on object state. You don't need to test internals, you only need to test the side effects of the calls you do.
In this case you don't need to test foo because you know what foo does, it's only a wrapper for faa. Thus, you need to test faa for actions and side effects caused by the global state and the ValueObject input.
If you want to test the foo() does what you expect, you have to test that the side effects that foo() causes are the same that faa(expectedObject) has.
In other words: if foo and faa are public, and the public interface says that foo is a wrapper for faa with a fixed parameter, you need to test that foo does exactly what faa does with the fixed parameter you expect.
So it looks like you want to assert against the ValueObject created in foo() and passed to faa(vo)?
Create a unit test instantiates the expected ValueObject and passes it to faa(vo). Then compare the results of this to the results that occur when you just call foo().
For example:
// in your unit test:
ValueObject testVo = new ValueObject();
// set values as needed
// testVo should represent what you expect the ValueObject instantiated by foo to
// hold *before* it gets passed to faa
faa(testVo);
// assertions against results of faa method
foo();
// assertions *comparing* the results of foo with the results of your manual faa
So for example, if I expect foo to create a ValueObject with X=1 and Y=1, and then for faa to do magic on them such that the ValueObject's end result is X=-1 and Y=2, your unit test will look something like this:
ValueObject testVo = new ValueObject();
testVo.setX(1);
testVo.setY(1);
faa(testVo);
// assertions using your testing library of choice to verify getX() returns -1 and
// getY() returns 2
foo();
// assertions that the end values after foo()'s faa call are -1 and 2 respectively
Now in your dummy examples above, both foo and faa are void methods, so there's no returned value. I am assuming that faa actually does something that is testable -- eg. sets fields, changes values, and so on.
In some comments you left on other answers, you made a note that you have some private and/or protected fields and methods involved. If this is the case, you'll want to look into Java reflection for how to retrieve those values.
But for specifically ripping out the ValueObject instantiated within foo() before it is passed to faa(vo), this is impossible. As I described above, your best bet is to test your expectations of behavior for the ValueObject you expect to be created in foo() and comparing the results of it being passed to faa(vo) with what actually does happen.
This is telling you that the design has tight coupling so maybe inject the object into the method instead.
You might want to consider testing faa() with in foo(). From an OOP standpoint, I would rather the caller of foo() never need to know faa() even existed. Does faa() need to be public?

Categories

Resources