I have following classes.
#Inherited
#InterceptorBinding
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
public #interface MyLogger {
public boolean skipParams() default true;
}
#MyLogger
#Interceptor
public class MyInterceptor {
#AroundInvoke
public Object logMethod(InvocationContext joinPoint) throws Exception {
// Some log statements here
}
}
public class MyClass {
#MyLogger()
void test1(){
// some code
}
#MyLogger(skipParams = true)
void test2(){
// some code
}
#MyLogger(skipParams = false)
void test3(){
// some code
}
}
#AroundInvoke does not work when attribute supplied.
logMethod() gets called for test1(), but not for test2() and test3().
Related
I am struggling to write unit test cases for aspect code. Please find the all the respective code.
Custom Annotation -
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface WriteAccessAuthorization{
boolean isAdmin() default false;
}
Aspect Code -
#Aspect
class AccessAspect {
...
...
boolean isAdminForWriteAccess(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
WriteAccessAuthorization writeAccessAuthorization =
method.getAnnotation(WriteAccessAuthorization.class);
return writeAccessAuthorization.isAdminPortal();
}
...
}
Here I am getting NPE in last line of the method.
Here method.getAnnotation() is returning null even we are mocking it in Junit test method.
Please find the junit test case code -
class AccessAspectTest {
#Mock private ProceedingJoinPoint joinPoint;
#Mock private MethodSignature methodSignature;
#Mock private Method method;
#Mock private WriteAccessAuthorization writeAccessAuthorization;
#InjectMocks private AccessAspect accessAspect;
#BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
}
#Test
void test_isAdmin()
throws Throwable {
//Given
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(methodSignature.getMethod()).thenReturn(getDeclaredMethod(WriteAccessAuthorization.class));
when(method.getAnnotation(WriteAccessAuthorization.class)).thenReturn(writeAccessAuthorization);
//When
accessAspect.isAdminForWriteAccess(joinPoint);
//Then
verify(joinPoint, times(1)).proceed();
}
#NotNull
private <T> Method getDeclaredMethod(Class<T> clazz) throws NoSuchMethodException {
return clazz.getDeclaredMethod("isAdmin");
}
}
In many blogs or stackoverflow answers it was mention to have RUNTIME policy in you annotation but in my case it was already placed.
Please let me know if there is anything else required.
You actually need to apply the annotation to a method. What your code is testing is whether the isAdmin method defined in annotation interface is annotated with WriteAccessAuthorization which it is not. That's why method.getAnnotation returns null.
Here is an example of how to access the annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface WriteAccessAuthorization {
boolean isAdmin() default false;
}
public static final class SomeClass {
#WriteAccessAuthorization
public void doSth() {}
}
public static void main(String[] args) throws Exception {
Method method = SomeClass.class.getDeclaredMethod("doSth");
WriteAccessAuthorization annotation =
method.getDeclaredAnnotation(WriteAccessAuthorization.class);
System.out.println(annotation);
}
Output:
#com.example.test$WriteAccessAuthorization(isAdmin=false)
I created annotation to check permissions on the APIs. Annotation worked when placed in class or method. But when I use it for both class and method at the same time, the annotation placed on the method is ignored
Here is an example:
#RestController
#RequestMapping("/user")
#CustomAnnotation(permission="manageUser")
public class UserController {
#CustomAnnotation(permission="updateUser")
#PutMapping("/{id}")
public UserDto getUser(HttpServletRequest request) {
...
}
}
Now #CustomAnnotation(permission="updateUser") in the getUser() method will be ignored and only execute annation in class UserController
My custom code
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface CustomAuthorize {
public String permission() default "";
public String attribute() default "";
}
#Aspect
#Component
public class CustomAnnotationAspect {
#Autowired
CustomAuthorizationImpl authBean;
#Pointcut("#annotation(customAuthorize) || #within(customAuthorize)")
public void pointcutForCustomAnnotation(CustomAuthorize customAuthorize) {
// Do nothing.
}
#Around("pointcutForCustomAnnotation(customAuthorize)")
public Object customAspect(ProceedingJoinPoint pjp, CustomAuthorize customAuthorize) throws Throwable {
if (Objects.isNull(customAuthorize)) {
System.out.println("Call from method");
// Check permission
return pjp.proceed();
}
System.out.println("Call from class");
ExpressionParser elParser = new SpelExpressionParser();
if (!customAuthorize.permission().isBlank()) {
// check permission
}
return pjp.proceed();
}
}
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Multipart {
Class acceptClass();
}
Annotated method:
#Multipart (acceptClass = SomeClass.class)
public void someMethod(SomeClass a){
//do stuff..
}
MultipartAspect:
#Aspect
public class MultipartAspect {
#Autowired(required=true)
private HttpServletRequest request;
#Pointcut(value = "#annotation(Multipart)", argNames = "multipart")
public void before(JoinPoint jp, Multipart multipart) {}
#Before("before()")
public SomeClass doStuffBeforeThing() {
SomeClass sc = new SomeClass(); //object of passed class
//do something..
return sc; //return this to annotated method(somemethod)
}
}
I want before method works execute annotation, create object of passed class(SomeClass) and the pass object of this class to annotated method. Could I do this?
You should use #Around advice instead of #Before.
I am trying to implement an interceptor with #Aspect. I need to get class level annotation
Here is my interceptor
#Aspect
public class MyInterceptor {
#Around("execution(* com.test.example..*(..))")
public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
Object result;
try {
result = pjp.proceed();
} catch (Throwable e) {
throw e;
}
return result;
}
}
and here is my annotation
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String reason();
}
and here is the class
#MyAnnotation(reason="yes")
public class SomeClassImpl implements SomeClass {
}
In interceptor I need to get the annotation and the value assigned to reason attribute.
Interceptor class to get value of the annotation marked at class level
#Aspect
#Component
public class MyInterceptor {
#Around("#target(annotation)")
public Object intercept(ProceedingJoinPoint joinPoint, MyAnnotation annotation) throws Throwable {
System.out.println(" called with '" + annotation.reason() + "'");
return joinPoint.proceed();
}
}
I have a interface
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.TYPE })
public #interface LoggingInterceptorBinding {
}
and a class:
#LoggingInterceptorBinding
#Interceptor
public class LoggingInterceptor implements Serializable {
#AroundInvoke
public Object onMethodCall(InvocationContext context) throws Exception {
try {
System.out.println("Log before Method");
return context.proceed();
} finally {
System.out.println("Log after Method");
}
}
and a annotated method:
#LoggingInterceptorBinding
public void sayHello(String name)
Is it possible to get the parameter "name" from sayHello in the interceptors "onMethodCalls"-method?
The InvocationContext interface has a getParameters() method that
Returns the parameter values that will be passed to the method of the
target class. If setParameters() has been called, getParameters()
returns the values to which the parameters have been set.