Annotation works only for methods from interface - java

I have annotation:
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
public #interface Loggable { }
and aspect:
#Aspect
public class AspectLogger {
#Around("#annotation(aspects.Loggable)")
public void aroundLogging(ProceedingJoinPoint joinPoint) {
System.out.println("aroundLogging()");
throw new AuthentificationFailException();
}
}
Also I have interface and class:
public interface IAuthInteractor {
public User authorization(String login, String password);
}
public class AuthInteractor implements IAuthInteractor {
private EntityDAO<User> userDAO;
private ITokenGenerator tokenGenerator;
public AuthInteractor(EntityDAO<User> userDAO,
ITokenGenerator tokenGenerator) {
this.userDAO = userDAO;
this.tokenGenerator = tokenGenerator;
}
#Loggable
public User authorization1(String login, String password) {
return null;
}
#Loggable
public User authorization(String login, String password) {
return null;
}
}
For first method (authorization1) annotation doesn't work. For method authorization (that was described in interafce) annotation works.
Why does it work this way? and how to work without interface?

First of all, the aspect's advice has a void return type, i.e. it will never kick in for methods returning other types such as User in your example. The aspect should not even compile. It does not for me in any case. The AspectJ compiler says:
applying to join point that doesn't return void: method-execution(de.scrum_master.app.User de.scrum_master.app.AuthInteractor.authorization(java.lang.String, java.lang.String))
So, assuming you change your advice to
#Around("#annotation(aspects.Loggable)")
public Object aroundLogging(ProceedingJoinPoint joinPoint) {
System.out.println("aroundLogging()");
throw new AuthentificationFailException();
}
it will compile and also kick in. I tested it locally.
Now let me just quickly change the advice to actually proceed to the original method instead of always throwing an exception so we can test a bit more without catching exceptions all the time. I also want to print the actual joinpoint signature, so we can see what is going on:
#Around("#annotation(aspects.Loggable)")
public Object aroundLogging(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint);
//throw new AuthentificationFailException();
return joinPoint.proceed();
}
If then you add this main method to your interface implementation class:
public static void main(String[] args) {
System.out.println("Interface object");
IAuthInteractor iAuthInteractor = new AuthInteractor(null, null);
iAuthInteractor.authorization("user", "pw");
System.out.println("\nImplementation object");
AuthInteractor authInteractor = new AuthInteractor(null, null);
authInteractor.authorization("user", "pw");
authInteractor.authorization1("user", "pw");
}
The console log should print something like this, assuming you use AspectJ and not just "AOP lite" via Spring AOP which does not support call() joinpoints:
Interface object
execution(User de.scrum_master.app.AuthInteractor.authorization(String, String))
Implementation object
call(User de.scrum_master.app.AuthInteractor.authorization(String, String))
execution(User de.scrum_master.app.AuthInteractor.authorization(String, String))
call(User de.scrum_master.app.AuthInteractor.authorization1(String, String))
execution(User de.scrum_master.app.AuthInteractor.authorization1(String, String))
As you can see, executions are always caught, but calls are not for interface type instances because the interface method is not annotated, only the implementation.
BTW, method annotations are not inherited anyway, so your #Inherited meta annotation for an annotation type with #Target({ElementType.METHOD}) is kinda useless.

Related

How to create an invocation counter annotation in Spring?

Is it possible to create a custom annotation that simply tracks invocations of some methods, without having to add a service method call in every method explicit?
#InvocationCounter(path = "/test1") //I'm looking for this
#GetMapping("/person/{id}")
public Person getPerson(Long id) {
//...
}
On every getPerson() call, I want an invocation counter to record the invocation, like:
#Service
public class InvocationCounterService {
Map<String, AtomicInteger> counter;
public void count(String path) {
if (counter.get(path) == null) counter.put(path, new AtomicInteger()));
counter.get(path).incrementAndGet();
}
#Scheduled(fixedRate = 60000)
public void persist() {
//optionally process or persist the invocations
}
}
Question: how could I possibly tell Spring to invoke the count() service method on each annotated controller method?
The annotation InvocationCounter:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface InvocationCounter {
String path();
}
The Aspect InvocationCounterAspect:
#Aspect
#Component
public class InvocationCounterAspect {
#Autowired
InvocationCounterService invocationCounterService;
#Around("#annotation(InvocationCounter)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
InvocationCounter invocationCounter = signature.getMethod().getAnnotation(InvocationCounter.class);
final String path = invocationCounter.path(); //retrieve path from the annotation
invocationCounterService.count(path); //call the counter service
return joinPoint.proceed(); //proceed executing the annotated method
}
}
You should be looking at micrometer.io, Spring boot application natively supports metrics collection, including simple counter.
An annotation is a form of syntactic metadata that can be added to Java source code.
So you can add informations to your source with an annotation but you can't add code to be executed.
You can do with aspects as mentioned by pleft in the comments.
Or you can create a countExecutions function that takes your Function as a lambda expression to count the number of executions as follow:
public R countExecutions(Function<R, T> fun, T data) {
// invoke counter
return fun.apply(data);
}
and rewriting your code as
#GetMapping("/person/{id}")
public Person getPerson(Long id) {
return countExecutions( /* code previously in the getPerson as a Function */ , id);
}

Incorporating Guice and AOP

I'm building a package that is trying to intercept a function's return value based on a flag. My design involves some AOP. The idea is that a class FirstIntercept intercepts a call firstCall and stores parameters in a Parameters object. Then later, a second class SecondIntercept intercepts another call secondCall and does some logic based on what is populated in Parameters:
// pseudoish code
public class FirstIntercept {
private Parameters param;
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// logic handling payload returned from firstCall()
// logic provides a Boolean flag
this.param = new Parameters(flag);
}
}
public class Parameters {
#Getter
private Boolean flag;
public Parameters(Boolean flag) {
this.flag = flag;
}
}
public class SecondIntercept {
private static Parameters params;
#Around("execution(* ...secondCall(..))")
public void handleSecondCallIntercept(ProceedingJoinPoint joinPoint) {
// want to do logic here based on what params contains
}
}
What I want to achieve is that the Parameters object is loaded once and for all when FirstIntercept.loadParam is invoked through AOP. I'm not too sure how I can go about with this persistence. I looked online and Google guice seems to be promising. I believe a first step would to use dependency injection on the Parameters, but I'm really not sure. Can someone help point me in the right direction?
edit:
So I tried this setup:
public class FirstIntercept implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("invoked!");
return invocation.proceed();
}
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// do stuff
}
public String firstCall() {
return "hello";
}
}
public class InterceptionModule extends AbstractModule {
protected void configure() {
FirstIntercept first = new FirstIntercept();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class), first);
}
}
public class FirstIterceptTest {
#Test
public void dummy() {
Injector injector = Guice.createInjector(new InterceptionModule());
FirstIntercept intercept = injector.getInstance(FirstIntercept.class);
intercept.firstCall();
}
}
When I do .firstCall(), I can see the #AfterReturning running but the invoke is not being called.
If you expand upon the documentation for AOP https://github.com/google/guice/wiki/AOP you should get something close to:
public class FirstInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
// your logic based on result to set parameters.setFlag()
return result;
}
}
Then the second:
public class SecondInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
boolean flag = parameters.getFlag();
// your logic here
return invocation.proceed(); // maybe maybe not?
}
}
Your parameters is the key, you'll need to ensure it's thread safe, which is another topic. But to inject these you need:
public class InterceptionModule extends AbstractModule {
protected void configure() {
// Ensure there is only ever one Parameter injected
bind(Parameter.class).in(Scopes.SINGLETON);
// Now inject and bind the first interceptor
FirstInterceptor firstInterceptor = new FirstInterceptor();
requestInjection(firstInterceptor );
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
firstInterceptor);
// Now inject and bind the second interceptor
SecondInterceptor SecondInterceptor = new SecondInterceptor ();
requestInjection(firstInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
SecondInterceptor);
}
}
Edit
Look at what you're doing.
You're telling Guice to wrap a method with #AfterReturn with the FirstInterceptor
Then you're calling interceptor.firstCall()
First call does not have #AfterReturn annotation, so why would it be matched against that configuration?
I'm guessing if you called:
intercept.loadParam();
you would see the invoke method. Also, this is great for a test, but in real life you want to have a Service level class have the #AfterReturn which is then Injected into another Api/Job/Etc that will call LoadParam.
edit
Oh no. Take a look at this line
bindInterceptor(Matchers.any(), // a class with this matcher
Matchers.annotatedWith(AfterReturning.class), // a method with this
firstInterceptor);
This means that the injector only fires on the loadParams. You need to annotate the method of the class youw ish to cause the interception with #AfterReturning. And you want the loadParams to be the invoke method.

aspect is not invoked

I've got a simple aspect that supposed to set the value of class fied, that has annotation #GuiceInject.
Originally I have this
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository repository;
And I expect to get similar to this
private IRacesRepository repository = GuiceInject.getInstance(IRacesRepository.class);
And here is my aspect
public aspect InjectionAspect {
Object around(): get(#GuiceInject * *) {
System.out.println(thisJoinPointStaticPart);
// instantiate object as it supposed to be null originally
return GuiceInjector.getInstance(thisJoinPoint.getTarget().getClass());
}
}
As far as I understand - I am new to AOP - it supposed to replace get invokations of the field with the code in aspect.
It compiles fine, but when I run the application - nothing happens. I get NullPointerException for readRaces method as it stays null so aspect did not work.
My main class looks like this
public class Example {
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository racesRepository;
private void execute() {
System.out.println("List of races: " + racesRepository.readRaces());
}
public static void main(String[] args) {
new Example().execute();
}
}
What is the problem? Annotation has this definition
#Target(ElementType.FIELD)
// make annotation visible in runtime for AspectJ
#Retention(RetentionPolicy.RUNTIME)
public #interface GuiceInject {
Class<? extends AbstractModule> module();
}
Please try to redefine pointcut syntax as
Object around(): get(#package.subpackage.GuiceInject * *.*)
Correct field signature must specify the type of the field, the declaring type, and name. If your annotation is in different package, it should be fully qualified.

Spring AOP : Parameter passed as null for an interface with #Around aspect

I have a problem where I am using #Around in two interfaces that are configured as Spring beans. One of those interface is a parameter to another interface and is always getting passed as null value. Following is the code snippet
public interface Interface1 {
public void method1();
}
public interface Interface2 {
public void method2(Interface1 param1);
}
#Around("execution(* Interface1.method1(..))")
private void interceptor1(ProceedingJoinPoint pJoinPoint) throws Throwable{
//do something
}
#Around("execution(* Interface2.method2(..))")
private void interceptor2(ProceedingJoinPoint pJoinPoint) throws Throwable{
//do something
}
In the calling code to Interface2 I always get the parameter param1 to method2 as null.
If I remove the #Around("execution(* Interface1.method1(..))") above it works fine. The reason for adding the #Around for both of them is to catch the Exceptions for logging and audit purpose and to stop the rest of the exceptions to be propagated.
Can you please help me around this problem?
It looks like your Aspects are flawed. An around aspect should always have a return type of Object not void. Returning void basically destroys the proper passing of return values from the callstack, remember that the around aspect puts code around your method execution!
So change your aspect to return object and always return the result of the call to proceed()
public Object aroundAdvice(ProceedingJoinPoint pjp) {
// Your stuff to do before the method call here
Object returnValue = pjp.proceed();
// Your stuff to do after the method call here
return returnValue;
}

Spring AOP, pointcut expressions : annotation with specific param

I have Aspect class with method clear().
#Aspect
public class Clear
{
#After("#annotation(org.springframework.transaction.annotation.Transactional)")
public void clear()
{
// do smth
}
}
now I want call this aspect after each execution of method with annotation #Transactional with readOnly = true like
#Transactional(readOnly = true)
public void someMethod()
{
//...
}
is there way to do it without custom annotations?
I think you are quite close.
In your clear method, you should take in a parameter of type JoinPoint. This parameter will be auto populated by Spring at runtime, and with it, you can get details of your specific joinpoint, including the java.lang.reflect.Method, which will contain the annotation you are after.
I am thinking something like this:
#Aspect
public class Clear
{
#After("#annotation(org.springframework.transaction.annotation.Transactional)")
public void clear(final JoinPoint joinPoint)
{
final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
final Transactional txAnnotation = methood.getAnnotation(Transactional.class);
final boolean isReadOnly = txAnnotation.readOnly();
//do conditional work based on isReadOnly
}
}

Categories

Resources