Is the argument type based on the actual calling method, or based on the add()
signature?
In AspectJ for the call()
pointcut you need to specify method or constructor signatures. The add()
method in this case does not have any parameters annotated by @Entity
, thus what you are trying to do does not work. This is a workaround using reflection:
Sample annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {}
Sample entity:
package de.scrum_master.app;
@Entity
public class MyEntity {}
Driver application:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.myList.add("bar");
application.myList.add(new MyEntity());
}
}
Aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject);
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
Output:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@19dc6592
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@54906181
As for the control flow matching variant, I think from the naming perspective it makes sense to assume that getMyList()
does not add anything, but just return a list. Probably you rather do something like application.getMyList().add("foo")
, and in this case the add()
is really outside (after) the control flow of getMyList()
because it operates on its result.
If OTOH you have a hypothetical method addToList(Object element)
which really calls add()
you can use cflow()
. Let us modify the code sample:
Modified driver application:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public void addToMyList(Object element) { reallyAddToMyList(element); }
private void reallyAddToMyList(Object element) { myList.add(element); }
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.addToMyList("bar");
application.addToMyList(new MyEntity());
}
}
Modified aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject));
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
New output:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@323ba00
As you can see, only one call is logged. It is the one from reallyAddToMyList()
, not the one from main()
.
Update 2014-07-21 - better aspect modification:
Credits for this more elegant solution go to Andy Clement (AspectJ maintainer) who has mentioned it on the AspectJ mailing list. It shows both of my variants from above, but uses && @args(Entity)
instead of if (addedObject.getClass().isAnnotationPresent(Entity.class))
:
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject) && @args(Entity);
before(Object addedObject) : addEntity(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
pointcut addEntitySpecial(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject)) && @args(Entity);
before(Object addedObject) : addEntitySpecial(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject + " [special]");
}
}
The output with both variants active looks like this:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@229ff6d1
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e [special]