I have a requirement where in I need to time various method calls into a time series db.
For the same, I have created 2 annotations one for the method call:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
String event();
String entity();
}
and another one for a field
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
String id();
}
The reason I need the @ID annotation is that, the id field to be pushed to influx db would be known only at run time.
So, in my method, something like this would happen:
@Id
String key;
@Auditable(event="xxx",entity="yyy")
public void methodToBeIntercepted(){
String key = <logic to generate key>;
}
The idea that I wanted to use was add an annotation advice along with a field set advice.
@After("@annotation(auditable) && (set(@<package>.ID java.lang.String sample..*.*) && args(id))")
public void pointcutMethod(Auditable auditable,String id){
}
But the flow is never entering into the pointCutMEthod. If I change the condition to || above, then it enters but it clearly suggests that only 1 condition would be true at any given point of time.
What is it that I am doing wrongly here?
Your analysis is correct: The advice will never trigger. It just cannot because the two pointcuts you combine are mutually exclusive: Where @Auditable
is (method call or execution) is a different joinpoint from set()
. What you intend to express is the following: "Intercept member variable assignment within the control flow of a method execution." I.e. you need cflow(@annotation(auditable))
.
Annotations and driver application:
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;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
String id();
}
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.METHOD)
public @interface Auditable {
String event();
String entity();
}
package de.scrum_master.app;
public class Application {
@Id(id = "my ID")
String key;
public static void main(String[] args) {
Application application = new Application();
application.methodToBeIntercepted();
}
@Auditable(event = "xxx", entity = "yyy")
public void methodToBeIntercepted() {
key = "I am the key";
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.Auditable;
@Aspect
public class MyAspect {
@After("cflow(@annotation(auditable)) && set(@de.scrum_master.app.Id String de.scrum_master..*.*) && args(id)")
public void pointcutMethod(JoinPoint thisJoinPoint, Auditable auditable, String id) {
System.out.println(thisJoinPoint);
System.out.println(" " + auditable);
System.out.println(" " + id);
}
}
Console log:
set(String de.scrum_master.app.Application.key)
@de.scrum_master.app.Auditable(event=xxx, entity=yyy)
I am the key