Community
Participate
Working Groups
I have a problem with matching annotations declared on method parameters, when method uses generics. Here's extracted 'testcase' (i will also attach it): ----------------------------------------------- import java.lang.annotation.*; import java.util.*; @Target(ElementType.TYPE) @interface Ann {} @Ann class AClass {} public class Test2 { void abc(AClass y) {} <T> void par(T y) {} public static void main(String[] args) { Test2 test = new Test2(); test.abc(new AClass()); test.par(new AClass()); Set set = new HashSet(); set.add(new AClass()); } } aspect Annotations { before() : call(* *(@Ann *)) { System.out.println("Before: " + thisJoinPoint); } } ---------------------------------------------- The result of the test on Java5 (JRockit) and AspectJ 1.5.0M2 is folowing: ---------------------------------------------- Before: call(void Test2.abc(AClass)) ---------------------------------------------- Initially problem appeared when I tried matching on java.util collections' methods (see call to Set.add()), but the problem is the same for simple parametrized method (call to Test2.par()).
Created attachment 20113 [details] extracted test case
generics has known problems in M2, will fix for M3.
First thing, if you are going to match on annotations then you must specify they have runtime retention - you need to change the definition of Ann to this: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Ann {} we should be putting out an error message if the annotation is missing runtime retention but currently we aren't (this a bug I am aware of). call() matches on the declared signature of a method - in the face of generics on the bounds of any type parameters. The declaration of par does not say it takes an AClass as a parameter, it takes a T and with no bounds specified the default is Object so it effectively reads 'void par(Object y)' which doesn't match the pointcut. This, however, will match: call(* *(*)) && @args(Ann) since @args uses runtime type information rather than the static signature and at runtime the object passed to the call is annotated by @Ann so we get a match. An interesting case is this method that we could add: <T extends AClass> void xyz(T y) {} which again specifys a parameterized method but upper bound is AClass - this reads like 'void xyz(AClass y)' and does match the variant of the pointcut I showed above. All this works in the currently available AspectJ.
fixed in May as per Andy's last comment.... Runtime retention is needed when matching with @args (which also allows binding), but not needed when you simple use an annotation as part of the signature matching in eg. call or execution.