Method intercepted twice even though it was called once - java

In the following code snippet I'm calling the method doStuff once on an instance of Subclass. However it is intercepted twice.
Note that doStuff was defined in the parent class SuperClass. If doStuff was defined in SubClass the interception logic would work as expected: only one interception.
Am I using Byte Buddy incorrectly?
package com.test;
import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import org.junit.Test;
public class ReproBugTest {
#Test
public void reproBug() {
new AgentBuilder.Default().type(nameStartsWith("com.test"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(
Builder<?> builder,
TypeDescription td) {
return builder.method(any())
.intercept(
MethodDelegation.to(MethodInterceptor.class));
}
})
.installOn(
ByteBuddyAgent.installOnOpenJDK());
SubClass subClass = new SubClass();
subClass.doStuff();
}
}
class SuperClass {
public void doStuff() {
System.out.println("Doing stuff...");
}
}
class SubClass extends SuperClass {
}
class MethodInterceptor {
#RuntimeType
public static Object intercept(#SuperCall Callable<?> zuper)
throws Exception {
// Intercepted twice, bug?
System.out.println("Intercepted");
Object returnValue = zuper.call();
return returnValue;
}
}

You are intercepting the method call for every type, i.e. for both Subclass and SuperClass. You need to further specify your interceptor for what methods to intercept. In you case, you only want to intercept methods if they are declared by a given type.
This is easy to implement. Instead of builder.method(any()), you should intercept builder.method(isDeclaredBy(td)). This way, a method is only intercepted if it is declared by the intercepted type.
Finally, I can see from, your source code that you are using an older version of Byte Buddy. Version 0.7-rc6 runs stable, has additional features and fixes several bugs. (However, some APIs still need to be changed.)

Related

How to get Method annotated with given annotation in springboot / java app

Is there a way to directly get hold of a method(non-static) annotated with given annotation present in a given object? I don't want to iterate over list of all methods and check if the given annotation present or not.
In below sample code, I have used dummy method(not exist) getMethodAnnotatedWith() . I need to replace this with actual method.
public class MyController {
#PostMapping("/sum/{platform}")
#ValidateAction
public Result sum(#RequestBody InputRequest input, #PathVariable("platform") String platform) {
log.info("input: {}, platform: {}", input, platform);
return new Result(input.getA() + input.getB());
}
}
class InputRequest {
#NotNull
private Integer a;
#NotNull
private Integer b;
#MyValidator
public boolean customMyValidator() {
log.info("From customMyValidator-----------");
return false;
}
}
#Aspect
#Component
#Slf4j
public class AspectClass {
#Before(" #annotation(com.example.ValidateAction)")
public void validateAspect(JoinPoint joinPoint) throws Throwable {
log.info(" MethodName : " + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
log.info("args[0]==>"+args[0] +", args[1]==>"+args[1]);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Method customMyValidator = parameters[0].getType().getMethodAnnotatedWith(MyValidator.class); // InputRequest class type which has a method annotated with #MyValidator
customMyValidator.invoke(args[0]);
}
}
Here is my stand-alone AspectJ MCVE. I just imported some Spring classes. The syntax would be the same in Spring AOP.
Helper classes:
package de.scrum_master.app;
public class Result {
public Result(int i) {}
}
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface ValidateAction {}
Custom validator interface (not annotation):
package de.scrum_master.app;
public interface MyValidator {
boolean validate();
}
Class implementing custom validator interface:
package de.scrum_master.app;
public class InputRequest implements MyValidator {
private Integer a;
private Integer b;
public InputRequest(Integer a, Integer b) {
this.a = a;
this.b = b;
}
#Override
public boolean validate() {
System.out.println("Performing custom validation");
return false;
}
public Integer getA() {
return a;
}
public Integer getB() {
return b;
}
#Override
public String toString() {
return "InputRequest(a=" + a + ", b=" + b + ")";
}
}
See? Instead of annotating the validator method, you just override the interface method. It is just as simple as before, but more type-safe and feels more "standard-ish". It will also be much easier to handle by AOP, as you are going to find out below.
Controller:
The controller looks just the same as before. You still have the method annotated with #ValidateAction and taking InputRequest as its first parameter.
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public class MyController {
#PostMapping("/sum/{platform}")
#ValidateAction
public Result sum(#RequestBody InputRequest input, #PathVariable("platform") String platform) {
System.out.println("input: " + input + ", platform: " + platform);
return new Result(input.getA() + input.getB());
}
}
Sample driver application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new MyController().sum(new InputRequest(11, 22), "foo");
}
}
Aspect:
The aspect is super simple now, like I said in my comment to your question. The pointcut checks for methods which
are annotated with #ValidateAction and
have a first parameter implementing MyValidator.
Then it binds the MyValidator parameter to an advice method argument by args().
Please note that you can omit the trailing && execution(* *(..)) in Spring AOP because it only supports method execution() joinpoints, while in AspectJ there are also call() joinpoints which here would lead to double validation and log output.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import de.scrum_master.app.MyValidator;
#Aspect
#Component
public class MyValidatorAspect {
#Before("#annotation(de.scrum_master.app.ValidateAction) && execution(* *(..)) && args(validator, ..)")
public void validateAspect(JoinPoint joinPoint, MyValidator validator) throws Throwable {
System.out.println(joinPoint);
validator.validate();
}
}
Console log:
execution(Result de.scrum_master.app.MyController.sum(InputRequest, String))
Performing custom validation
input: InputRequest(a=11, b=22), platform: foo
Update answering follow-up questions: Please read some documentation. The Spring manual is a good source.
what does it mean && args(validator, ..)?
It is called argument binding. This specific pointcut designator means: Match all target methods where the first argument matches the type of validator in the advice method arguments list and bind the value to that argument. As you see, the argument is declared as MyValidator validator. The , .. means that any subsequent target method arguments (if any) do not matter. For more information see this manual paragraph.
What would happen if more than one class implementing MyValidator interface . I mean how would FW figure out that which implementation has to passed while invoking current controller operation ?
FW meaning what? Maybe framework? Anyway, I do not see the problem. Why would the framework have to figure out which implementation there is? That is the beauty of OOP and virtual methods: You do not need to figure out anything because each implementation has a boolean validate() which then gets called. It is simple, type-safe, hassle-free. Your solution is neither of these. This approach just works. Instead of asking, why don't you just try?
MethodUtils (Apache Commons Lang) can be used to achieve the requirement.
API used in the example code : MethodUtils.getMethodsListWithAnnotation
Aspect Code
#Component
#Aspect
public class CallAnnotatedMethodAspect {
#Pointcut("within(rg.so.q64604586.service.*) && #annotation(rg.so.q64604586.annotation.ValidateAction)")
public void validateActionMethod() {
};
#Before("validateActionMethod() && args(request,..)")
public void adviceMethod(InputRequest request)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
List<Method> methodList = MethodUtils.getMethodsListWithAnnotation(request.getClass(), MyValidator.class);
for (Method m : methodList) {
m.invoke(request, new Object[] {});
}
}
}
Note : The Retention Policy of MyValidator annotation is RUNTIME for this code to work.
If the InputRequest instance obtained in the aspect is a proxy , the code will need to be modified to get the actual class of the same.

Can't compile sub class that implements abstract method from base class

Having problems compiling sub classes of a base class that I've defined that has a single method and each sub class implements the abstract base method, but javac is saying that they don't even though it is quite clearly defined in the sub class.
DbModel.java (the base class)
package com.manodestra.db;
import java.sql.ResultSet;
import java.sql.SQLException;
public abstract class DbModel<T extends DbModel> extends Model {
abstract T newInstance(ResultSet rs) throws SQLException;
}
DbModel extends Model, which only has a generic toString method.
MenuPermissions.java (the sub class)
package com.manodestra.csa.db.model.configNew;
import com.manodestra.db.DbModel;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
public class MenuPermissions extends DbModel<MenuPermissions> {
private final String menuId;
private final String userLevel;
public MenuPermissions(
String menuId,
String userLevel
) {
this.menuId = menuId;
this.userLevel = userLevel;
}
public String getMenuId() {
return this.menuId;
}
public String getUserLevel() {
return this.userLevel;
}
public MenuPermissions newInstance(ResultSet rs) throws SQLException {
return new MenuPermissions(
rs.getString("menu_id"),
rs.getString("user_level")
);
}
}
Compilation Error
[javac] Compiling 487 source files to C:\Media\Code\manodestra_java\bin
[javac] C:\Media\Code\manodestra_java\src\com\manodestra\csa\db\model\configNew\MenuPermissions.java:10:
error: MenuPermissions is not abstract
and does not override abstract method newInstance(ResultSet) in DbModel
[javac] public class MenuPermissions extends DbModel<MenuPermissions> {
[javac] ^
Anyone see what the problem is here? I'm guessing that I'm overlooking something really simple.
Further info on requirements:
I'm building an entity framework that generates model objects from a given database. MenuPermissions above is one such model object (auto-generated by a class that I've written called GenerateModel). I want each model to have a method that will allow me to get a new instance of each objecct type based on a resultset, which will populate the object accordingly and return it. Ideally, it should be a static method, but I've tried it as a concrete method for the moment as I need to enforce its existence in each sub class of DbModel. Hope that makes sense.
Your abstract method newInstance has package access. I don't know if that was intended but if it is in a different package then you would get an error.
Edit:
So the abstract method in the parent class can not be resolved since it is not declared a public. A possible remedy is to add public to the method definition or move the child class into the same package as the parent class :-D

Powermock/EasyMock: Set expectation on final method that would throw exception

How do I set an expectation on a final method if I can't safely invoke that method at all? PowerMock is supposed to ensure the invocation is mocked, but I can't even get to that stage:
WithFinal.java:
public class WithFinal {
public final void finalMethod() {
throw new RuntimeException();
}
}
CallsFinal.java:
public class CallsFinal {
private WithFinal withFinal;
public CallsFinal(WithFinal withFinal) {
this.withFinal = withFinal;
}
public void callFinal() {
withFinal.finalMethod();
}
}
PowerMockTest.java:
import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.core.classloader.annotations.PrepareForTest;
import static org.powermock.api.easymock.PowerMock.*;
#RunWith(PowerMockRunner.class)
#PrepareForTest(CallsFinal.class)
public class PowerMockTest {
#Test public void testFinal() {
WithFinal mock = createMock(WithFinal.class);
CallsFinal callsFinal = new CallsFinal(mock);
mock.finalMethod();
EasyMock.expectLastCall().atLeastOnce();
replay(mock);
callsFinal.callFinal();
verify(mock);
}
}
I get a RuntimeException on the very first call to mock.finalMethod(), which makes sense, but I thought the whole point of PowerMock was to make this possible?
There was a simple mistake in the test class: instead of #PrepareForTest(CallsFinal.class), it should have been #PrepareForTest(WithFinal.class).
PowerMock only requires that the calling class be prepared for test when mocking a system class from the JRE; otherwise, it's the class to be mocked itself that needs to get prepared.
Finally, I will mention there is another mocking library that can be used here, which I happen to develop: JMockit. With it, the test can be written as:
import org.junit.*;
import mockit.*;
public class JMockitTest {
#Tested CallsFinal callsFinal;
#Injectable WithFinal mock;
#Test public void testFinal() {
new Expectations() {{ mock.finalMethod(); }};
callsFinal.callFinal();
}
}
Using PowerMock, you can mock skip a internal method call instead of direct method call.
For example you want to test callFinal method of CallsFinal class which internally calling finalMethod of WithFinal class. So in this case if you don't want to instantiate WithFinal class then you need to mock WithFinal object to skip internal call for finalMethod.

AspectJ type pattern for all types with a method with an attribute?

Currently I have the standard one:
#DeclareParents(value = "(#moody.MyAttribute *)", defaultImpl = MoodyImpl.class)
This will add my interface+implementation to any class with #MyAttribute
I would like to do this for all classes that have this attribute AND/OR have a method with that attribute.
So this class should also get my interface+implementation:
class MyClass {
#MyAttribute
public void test()
{
}
}
Is that possible?
No, because both #DeclareParents and the newer #DeclareMixin need class name specifications in their value parameter. If I were you I would refactor my annotation so as to only be applicable to classes, not methods, and then my code to move all annotations to classes as well.
One more option if you absolutely want to stay on your path: Since AspectJ 1.8.2 there is a new annotation processing feature. You might want to explore that one and create an annotation processor creating an ITD aspect for each affected class with annotated methods.
Update: I have just remembered a non-standard compiler option -XhasMember which you can use:
ajc -X
AspectJ Compiler 1.8.2 non-standard options:
(...)
-XhasMember allow hasmethod() and hasfield type patterns in
declare parents and declare #type
(...)
Caveat: It does not seem to work with #AspectJ syntax, i.e. you cannot use annotation-style #DeclareParents but have to use native AspectJ syntax declare parents. The default interface implementation is also done differently, i.e. via ITD in the aspect, there is no need for a specific implementation class.
Here is a compileable, fully self-consistent code sample:
Marker annotation:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface MyAttribute {}
Interface to be implemented via ITD:
package de.scrum_master.app;
public interface Moody {
public void sayHelloTo(String name);
}
Sample classes with(out) marker annotation:
Class Foo has the annotation at class level, Bar at method level and Zot has no annotation at all.
package de.scrum_master.app;
#MyAttribute
public class Foo {
public static void foo() {}
}
package de.scrum_master.app;
public class Bar {
#MyAttribute
public static void bar() {}
}
package de.scrum_master.app;
public class Zot {
public static void zot() {}
}
Driver application:
For demonstration purposes, the application checks for the existence of a method sayHelloTo(String) via reflection.
package de.scrum_master.app;
import java.lang.reflect.Method;
public class Application {
public static void main(String[] args) throws Exception {
Method method;
try {
method = Foo.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Foo: " + method);
try {
method = Bar.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Bar: " + method);
try {
method = Zot.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Zot: " + method);
}
}
Console output (without aspect):
Foo: null
Bar: null
Zot: null
Aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Moody;
import de.scrum_master.app.MyAttribute;
public aspect ITDAspect {
declare parents : #MyAttribute * implements Moody;
declare parents : hasmethod(#MyAttribute * *(..)) implements Moody;
public void Moody.sayHelloTo(String name) {
System.out.println("Hello " + name);
}
}
Console output (with aspect):
Foo: public void de.scrum_master.app.Foo.sayHelloTo(java.lang.String)
Bar: public void de.scrum_master.app.Bar.sayHelloTo(java.lang.String)
Zot: null
Voilà! We have successfully added the interface including its default implementation to Bar which does not have class-level annotation, but a method-level one.
Enjoy!

Assert an object is a specific type

Is it possible in JUnit to assert an object is an instance of a class? For various reasons I have an object in my test that I want to check the type of. Is it a type of Object1 or a type of Object2?
Currently I have:
assertTrue(myObject instanceof Object1);
assertTrue(myObject instanceof Object2);
This works but I was wondering if there is a more expressive way of doing this.
For example something like:
assertObjectIsClass(myObject, Object1);
I could do this:
assertEquals(myObject.class, Object1.getClass());
Is there a specific assert method that allows me to test a type of an object in a more elegant, fluid manner?
You can use the assertThat method and the Matchers that comes with JUnit.
Take a look at this link that describes a little bit about the JUnit Matchers.
Example:
public class BaseClass {
}
public class SubClass extends BaseClass {
}
Test:
import org.junit.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;
/**
* #author maba, 2012-09-13
*/
public class InstanceOfTest {
#Test
public void testInstanceOf() {
SubClass subClass = new SubClass();
assertThat(subClass, instanceOf(BaseClass.class));
}
}
Since assertThat which was the old answer is now deprecated, I am posting the correct solution:
assertTrue(objectUnderTest instanceof TargetObject);
Solution for JUnit 5
The documentation says:
However, JUnit Jupiter’s org.junit.jupiter.Assertions class does not provide an assertThat() method like the one found in JUnit 4’s org.junit.Assert class which accepts a Hamcrest Matcher. Instead, developers are encouraged to use the built-in support for matchers provided by third-party assertion libraries.
Example for Hamcrest:
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
class HamcrestAssertionDemo {
#Test
void assertWithHamcrestMatcher() {
SubClass subClass = new SubClass();
assertThat(subClass, instanceOf(BaseClass.class));
}
}
Example for AssertJ:
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
class AssertJDemo {
#Test
void assertWithAssertJ() {
SubClass subClass = new SubClass();
assertThat(subClass).isInstanceOf(BaseClass.class);
}
}
Note that this assumes you want to test behaviors similar to instanceof (which accepts subclasses). If you want exact equal type, I don’t see a better way than asserting the two class to be equal like you mentioned in the question.
Experimental Solution for JUnit 5.8
In Junit 5.8, the experimental assertInstanceOf() method was added, so you don't need Hamcrest or AssertJ anymore. The solution is now as simple as:
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import org.junit.Test;
public class InstanceOfTest {
#Test
public void testInstanceOf() {
SubClass subClass = new SubClass();
assertInstanceOf(BaseClass.class, subClass);
}
}
Solution for JUnit 5 for Kotlin!
Example for Hamcrest:
import org.hamcrest.CoreMatchers
import org.hamcrest.MatcherAssert
import org.junit.jupiter.api.Test
class HamcrestAssertionDemo {
#Test
fun assertWithHamcrestMatcher() {
val subClass = SubClass()
MatcherAssert.assertThat(subClass, CoreMatchers.instanceOf<Any>(BaseClass::class.java))
}
}
Example for AssertJ:
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
class AssertJDemo {
#Test
fun assertWithAssertJ() {
val subClass = SubClass()
assertThat(subClass).isInstanceOf(BaseClass::class.java)
}
}
Solution for JUnit for Kotlin
What worked for me:
assert(obj is ClassName)
for exmaple
assert(obj is User)
NOTE: assert is coming from AssertionsJVM.kt file

Categories

Resources