The AspectJTM 5 Development Kit Developer's Notebook

the AspectJ Team

Copyright (c) 2004 Contributors, All rights reserved.

Abstract

This guide describes the changes to the AspectJ language and tools in AspectJ 5. These include support for Java 5 (Tiger) features, enhancements to load-time weaving, an support for an annotation-based development style for aspects. If you are new to AspectJ, we recommend you start by reading the programming guide.

This is a draft document and is subject to change before the design and implementation is complete. There is also no guarantee that all of the features in this document will be implemented in a 1.5.0 release - some may be deferred until 1.5.1 or even later. In general, features in which we have more confidence in the design will be implemented earlier, providing a framework for user feedback and direction setting on features for which the use cases are less obvious at time of writing.


Table of Contents

1. Join Point Signatures
Join Point Matching
Join Point Signatures
Method call join point signatures
Method execution join point signatures
Field get and set join point signatures
Join Point Modifiers
Summary of Join Point Matching
2. Annotations
Annotations in Java 5
Using Annotations
Retention Policies
Accessing Annotations at Runtime
Annotation Inheritance
Annotating Aspects
Join Point Matching based on Annotations
Annotation Patterns
Type Patterns
Signature Patterns
Example Pointcuts
Runtime type matching and context exposure
Package and Parameter Annotations
Annotation Inheritance and pointcut matching
Limitations
Using Annotations with declare statements
Declare error and declare warning
declare parents
declare precedence
Declare Annotation
Inter-type Declarations
3. Generics
Generics in Java 5
Declaring Generic Types
Using Generic and Parameterized Types
Subtypes, Supertypes, and Assignability
Generic Methods and Constructors
Erasure
Generics in AspectJ 5
Matching generic and parameterized types in type patterns
Signature patterns
Pointcuts
Inter-type Declarations
Declare Parents
Declare Soft
Parameterized Aspects
4. Autoboxing and Unboxing
Autoboxing and Unboxing in Java 5
Autoboxing and Join Point matching in AspectJ 5
Inter-type method declarations and method dispatch
5. Covariance
Covariance in Java 5
Covariant methods and Join Point matching
6. Varargs
Variable-length Argument Lists in Java 5
Calling Methods and Constructors with variable-length arguments
Using Variable-length arguments in advice and pointcut expressions
Matching signatures based on variable length argument types
Exposing variable-length arguments as context in pointcuts and advice
7. Enumerated Types
Enumerated Types in Java 5
Enumerated Types in AspectJ 5
8. The pertypewithin Aspect Instantiation Model
9. An Annotation Based Development Style
Introduction
Aspect Declarations
Pointcuts and Advice
Pointcuts
Advice
Inter-type Declarations
Declare statements
aspectOf() and hasAspect() methods
10. New Reflection Interfaces
The Aspect Class
11. Other Changes in AspectJ 5
Pointcuts
Binding of formals
Additional lint warnings
Declare Soft
Tools
Aspectpath
12. Load-Time Weaving
Introduction
Weaving class files more than once
Load-time Weaving Requirements
Configuration
Enabling Load-time Weaving
Configuring Load-time Weaving with aop.xml files
Configuring Load-time Weaving with Properties Files
Weaver Options
Runtime Requirements for Load-time Weaving
Supported Agents
JVMTI
JRockit
A. A Grammar for the AspectJ 5 Language

Chapter 1. Join Point Signatures

Many of the extensions to the AspectJ language to address the new features of Java 5 are derived from a simple set of principles for join point matching. In this section, we outline these principles as a foundation for understanding the matching rules in the presence of annotations, generics, covariance, varargs, and autoboxing.

Join Point Matching

AspectJ supports 11 different kinds of join points. These are the method call, method execution, constructor call, constructor execution, field get, field set, pre-initialization, initialization, static initialization, handler, and advice execution join points.

The kinded pointcut designators match based on the kind of a join point. These are the call, execution, get, set, preinitialization, initialization, staticinitialization, handler, and adviceexecution designators.

A kinded pointcut is written using patterns, some of which match based on signature, and some of which match based on modifiers. For example, in the call pointcut designator:

        call(ModifierPattern TypePattern TypePattern.IdPattern(TypePatternList) ThrowsPattern)
		

the modifiers matching patterns are ModifierPattern and ThrowsPattern, and the signature matching patterns are TypePattern TypePattern.IdPattern(TypePatternList).

A join point has potentially multiple signatures, but only one set of modifiers. A kinded primitive pointcut matches a particular join point if and only if:

  1. They are of the same kind
  2. The signature pattern (exactly) matches at least one signature of the join point
  3. The modifiers pattern matches the modifiers of the subject of the join point

These rules make it very easily to quickly determine whether a given pointcut matches a given join point. In the next two sections, we describe what the signature(s) of a join point are, and what the subjects of join points are.

Join Point Signatures

Call, execution, get, and set join points may potentially have multiple signatures. All other join points have exactly one signature. The following table summarizes the constituent parts of a join point signature for the different kinds of join point.

Join Point KindReturn TypeDeclaring TypeIdParameter TypesField TypeException Type
Method call++++  
Method execution++++  
Constructor call + +  
Constructor execution + +  
Field get ++ + 
Field set ++ + 
Pre-initialization + +  
Initialization + +  
Static initialization +    
Handler     +
Advice execution + +  

Note that whilst an advice excetution join point has a signature comprising the declaring type of the advice and the advice parameter types, the adviceexecution pointcut designator does not support matching based on this signature.

The signatures for most of the join point kinds should be self-explanatory, except for field get and set, and method call and execution join points, which can have multiple signatures. Each signature of a method call or execution join point has the same id and parameter types, but the declaring type and return type (with covariance) may vary. Each signature of a field get or set join point has the same id and field type, but the declaring type may vary.

The following sections examine signatures for these join points in more detail.

Method call join point signatures

For a call join point where a call is made to a method m(parameter_types) on a target type T (where T is the static type of the target):

		T t = new T(); 
		t.m("hello");  <= call join point occurs when this line is executed
		

Then the signature R(T) T.m(parameter_types) is a signature of the call join point, where R(T) is the return type of id in T, and parameter_types are the parameter types of m. If T itself does not declare a definition of m(parameter_types), then R(T) is the return type in the definition of m that T inherits. Given the call above, and the definition of T.m:

        interface Q {
          R m(String s);
        }
        
        class P implements Q {
          R m(String s) {...}        
        }
        
        class S extends P {
          R' m(String s) {...}
        }
        
        class T extends S {} 
		
		

Then R' T.m(String) is a signature of the call join point for t.m("hello").

For each ancestor (super-type) A of T, if m(parameter_types) is defined for that super-type, then R(A) A.m(parameter_types) is a signature of the call join point, where R(A) is the return type of m(parameter_types) as defined in A, or as inherited by A if A itself does not provide a definition of m(parameter_types).

Continuing the example from above,we can deduce that

        R' S.m(String)
        R  P.m(String)
        R  Q.m(String)
		

are all additional signatures for the call join point arising from the call t.m("hello"). Thus this call join point has four signatures in total. Every signature has the same id and parameter types, and a different declaring type.

Method execution join point signatures

Join point signatures for execution join points are defined in a similar manner to signatures for call join points. Given the hierarchy:

        interface Q {
          R m(String s);
        }
        
        class P implements Q {
          R m(String s) {...}        
        }
        
        class S extends P {
          R' m(String s) {...}
        }
        
        class T extends S { }
        
        class U extends T {
          R' m(String s) {...}
        }
		
		

Then the execution join point signatures arising as a result of the call to u.m("hello") are:

        R' U.m(String)
        R' S.m(String)
        R  P.m(String)
        R  Q.m(String)
		

Each signature has the same id and parameter types, and a different declaring type. There is one signature for each type that provides its own declaration of the method. Hence in this example there is no signature R' T.m(String) as T does not provide its own declaration of the method.

Field get and set join point signatures

For a field get join point where an access is made to a field f of type F on a object with declared type T, then F T.f is a signature of the get join point.

If T does not directly declare a member f, then for each super type S of T, up to and including the most specific super type of T that does declare the member f, F S.f is a signature of the join point. For example, given the hierarchy:

        
        class P  {
          F f;        
        }
        
        class S extends P {
          F f;
        }
        
        class T extends S { }                
		

Then the join point signatures for a field get join point of the field f on an object with declared type T are:

        F S.f
        F T.f
		

The signatures for a field set join point are derived in an identical manner.

Join Point Modifiers

Every join point has a single set of modifiers - these include the standard Java modifiers such as public, private, static, abstract etc., any annotations, and the throws clauses of methods and constructors. These modifiers are the modifiers of the subject of the join point.

The following table defines the join point subject for each kind of join point.

Join Point KindSubject
Method callThe method picked out by Java as the static target of the method call.
Method executionThe method that is executing.
Constructor callThe constructor being called.
Constructor executionThe constructor executing.
Field getThe field being accessed.
Field setThe field being set.
Pre-initializationThe first constructor executing in this constructor chain.
InitializationThe first constructor executing in this constructor chain.
Static initializationThe type being initialized.
HandlerThe declared type of the exception being handled.
Advice executionThe advice being executed.

For example, given the following types

        public class X {        
          @Foo
          protected void doIt() {...} 
        }
        
        public class Y extends X {        
          public void doIt() {...}        
        }
		

Then the modifiers for a call to (Y y) y.doIt() are simply {public}. The modifiers for a call to (X x) x.doIt() are {@Foo,protected}.

Summary of Join Point Matching

A join point has potentially multiple signatures, but only one set of modifiers. A kinded primitive pointcut matches a particular join point if and only if:

  1. They are of the same kind
  2. The signature pattern (exactly) matches at least one signature of the join point
  3. The modifiers pattern matches the modifiers of the subject of the join point

Given the hierarchy

        interface Q {
          R m(String s);
        }
        
        class P implements Q {
          @Foo
          public R m(String s) {...}        
        }
        
        class S extends P {
          @Bar
          public R' m(String s) {...}
        }
        
        class T extends S {} 
		
		

and the program fragment:

        P p = new P();
        S s = new S();
        T t = new T();
        ...
        p.m("hello");
        s.m("hello");
        t.m("hello");
		

The the pointcut call(@Foo R P.m(String)) matches the call p.m("hello") since both the signature and the modifiers match. It does not match the call s.m("hello") because even though the signature pattern matches one of the signatures of the join point, the modifiers pattern does not match the modifiers of the method m in S which is the static target of the call.

The pointcut call(R' m(String)) matches the calls t.m("hello") and s.m("hello"). It does not match the call p.m("hello") since the signature pattern does not match any signature for the call join point of m in P.

Chapter 2. Annotations

Annotations in Java 5

This section provides the essential information about annotations in Java 5 needed to understand how annotations are treated in AspectJ 5. For a full introduction to annotations in Java, please see the documentation for the Java 5 SDK.

Using Annotations

Java 5 introduces annotation types which can be used to express metadata relating to program members in the form of annotations. Annotations in Java 5 can be applied to package and type declarations (classes, interfaces, enums, and annotations), constructors, methods, fields, parameters, and variables. Annotations are specified in the program source by using the @ symbol. For example, the following piece of code uses the @Deprecated annotation to indicate that the obsoleteMethod() has been deprecated:

		@Deprecated
		public void obsoleteMethod() { ... }
		

Annotations may be marker annotations, single-valued annotations, or multi-valued annotations. Annotation types with no members or that provide default values for all members may be used simply as marker annotations, as in the deprecation example above. Single-value annotation types have a single member, and the annotation may be written in one of two equivalent forms:

		@SuppressWarnings({"unchecked"})
		public void someMethod() {...}
		

or

		@SuppressWarnings(value={"unchecked"})
		public void someMethod() {...}
		

Multi-value annotations must use the member-name=value syntax to specify annotation values. For example:

		@Authenticated(role="supervisor",clearanceLevel=5)
		public void someMethod() {...}
		

Retention Policies

Annotations can have one of three retention policies:

Source-file retention

Annotations with source-file retention are read by the compiler during the compilation process, but are not rendered in the generated .class files.

Class-file retention

This is the default retention policy. Annotations with class-file retention are read by the compiler and also retained in the generated .class files.

Runtime retention

Annotations with runtime retention are read by the compiler, retained in the generated .class files, and also made available at runtime.

Local variable annotations are not retained in class files (or at runtime) regardless of the retention policy set on the annotation type. See JLS 9.6.1.2.

Accessing Annotations at Runtime

Java 5 supports a new interface, java.lang.reflect.AnnotatedElement, that is implemented by the reflection classes in Java (Class, Constructor, Field, Method, and Package). This interface gives you access to annotations that have runtime retention via the getAnnotation, getAnnotations, and isAnnotationPresent. Because annotation types are just regular Java classes, the annotations returned by these methods can be queried just like any regular Java object.

Annotation Inheritance

It is important to understand the rules relating to inheritance of annotations, as these have a bearing on join point matching based on the presence or absence of annotations.

By default annotations are not inherited. Given the following program

			@MyAnnotation
			class Super {
			  @Oneway public void foo() {}
			}
			
			class Sub extends Super {
			  public void foo() {}
			}
			

Then Sub does not have the MyAnnotation annotation, and Sub.foo() is not an @Oneway method, despite the fact that it overrides Super.foo() which is.

If an annotation type has the meta-annotation @Inherited then an annotation of that type on a class will cause the annotation to be inherited by sub-classes. So, in the example above, if the MyAnnotation type had the @Inherited attribute, then Sub would have the MyAnnotation annotation.

@Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.

Annotating Aspects

AspectJ 5 supports annotations on aspects, and on method, field, constructor, advice, and inter-type declarations within aspects. Method and advice parameters may also be annotated. Annotations are not permitted on pointcut declarations or on declare statements.

The following example illustrates the use of annotations in aspects:

		@AspectAnnotation
		public abstract aspect ObserverProtocol {
		
			@InterfaceAnnotation
			interface Observer {}		
		
			@InterfaceAnnotation
			interface Subject {}
		
			@ITDFieldAnnotation
			private List<Observer> Subject.observers;  
				
			@ITDMethodAnnotation
			public void Subject.addObserver(Observer o) { 
			  observers.add(o);
			}
			
			@ITDMethodAnnotation
			public void Subject.removeObserver(Observer o) {
			  observers.remove(o);
			}
			
			@MethodAnnotation
			private void notifyObservers(Subject subject) {
			  for(Observer o : subject.observers) 
			    notifyObserver(o,subject);
			}
			
			/**
			 * Delegate to concrete sub-aspect the actual form of
			 * notification for a given type of Observer.
			 */
			@MethodAnnotation
			protected abstract void notifyObserver(Observer o, Subject s);
			
			/* no annotations on pointcuts */
			protected abstract pointcut observedEvent(Subject subject);
			
			@AdviceAnnotation
			after(Subject subject) returning : observedEvent(subject) {
				notifyObservers(subject);  
			} 
		}
	

An annotation on an aspect will be inherited by sub-aspects, iff it has the @Inherited meta-annotation.

AspectJ 5 supports a new XLint warning, "the pointcut associated with this advice does not match any join points". The warning is enabled by default and will be emitted by the compiler if the pointcut expression associated with an advice statement can be statically determined to not match any join points. The warning can be suppressed for an individual advice statement by using the @SuppressAjWarnings({"adviceDidNotMatch"}) annotation. This works in the same way as the Java 5 SuppressWarnings annotation (See JLS 9.6.1.5), but has class file retention.

	    import org.aspectj.lang.annotation.SuppressAjWarnings;
	    
	    public aspect AnAspect {
		
	      pointcut anInterfaceOperation() : execution(* AnInterface.*(..));
		  
		  
	      @SuppressAjWarnings // may not match if there are no implementers of the interface...
	      before() : anInterfaceOperation() {
	         // do something...
	      }		
		  
	      @SuppressAjWarnings("adviceDidNotMatch") // alternate form
	      after() returning : anInterfaceOperation() {
	         // do something...
	      }
	    }
	

Join Point Matching based on Annotations

This section discusses changes to type pattern and signature pattern matching in AspectJ 5 that support matching join points based on the presence or absence of annotations. We then discuss means of exposing annotation values within the body of advice.

Annotation Patterns

For any kind of annotated element (type, method, constructor, package, etc.), an annotation pattern can be used to match against the set of annotations on the annotated element. Annotation patterns are defined by the following grammar.

		AnnotationPattern := '!'? '@' AnnotationTypePattern AnnotationPattern* 
        
		AnnotationTypePattern := FullyQualifiedName |
		                         '(' TypePattern ')'
  		                     
  		FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*  		
      

In simple terms, an annotation pattern element has one of two basic forms:

  • @<qualified-name>, for example, @Foo, or @org.xyz.Foo.
  • @(<type-pattern>), for example, @(org.xyz..*), or @(Foo || Boo)

These simple elements may be negated using !, and combined by simple concatentation. The pattern @Foo @Boo matches an annotated element that has both an annotation of type Foo and an annotation of type Boo.

Some examples of annotation patterns follow:

@Immutable

Matches any annotated element which has an annotation of type Immutable.

!@Persistent

Matches any annotated element which does not have an annotation of type Persistent.

@Foo @Goo

Matches any annotated element which has both an annotation of type Foo and an annotation of type Goo.

@(Foo || Goo)

Matches any annotated element which has either an annotation of a type matching the type pattern (Foo || Goo). In other words, an annotated element with either an annotation of type Foo or an annotation of type Goo (or both). (The parenthesis are required in this example).

@(org.xyz..*)

Matches any annotated element which has either an annotation of a type matching the type pattern (org.xyz..*). In other words, an annotated element with an annotation that is declared in the org.xyz package or a sub-package. (The parenthesis are required in this example).

Type Patterns

AspectJ 1.5 extends type patterns to allow an optional AnnotationPattern prefix. (Extensions to this definition for generics are shown in the next chapter).

  	  	TypePattern := SimpleTypePattern |
  	  	               '!' TypePattern |
  	  	               '(' AnnotationPattern? TypePattern ')'
  	  	               TypePattern '&&' TypePattern |
  	  	               TypePattern '||' TypePattern 
  	  	
  	  	SimpleTypePattern := DottedNamePattern '+'? '[]'*
  	  	
  		DottedNamePattern := FullyQualifiedName RestOfNamePattern? |
  		                     '*' NotStarNamePattern?
  		
  		RestOfNamePattern := '..' DottedNamePattern |
  		                     '*' NotStarNamePattern?
  		                     
  		NotStarNamePattern := FullyQualifiedName RestOfNamePattern? |
  		                      '..' DottedNamePattern               

  		FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*  				  		  		  		               									 				  		             
	

Note that in most cases when annotations are used as part of a type pattern, the parenthesis are required (as in (@Foo Hello+)). In some cases (such as a type pattern used within a this pointcut expression, the parenthesis are optional:

        OptionalParensTypePattern := AnnotationPattern? TypePattern
      

The following examples illustrate the use of annotations in type patterns:

(@Immutable *)

Matches any type with an @Immutable annotation.

(!@Immutable *)

Matches any type which does not have an @Immutable annotation.

(@Immutable (org.xyz.* || org.abc.*))

Matches any type in the org.xyz or org.abc packages with the @Immutable annotation.

((@Immutable Foo+) || Goo)

Matches a type Foo or any of its subtypes, which have the @Immutable annotation, or a type Goo.

((@(Immutable || NonPersistent) org.xyz..*)

Matches any type in a package beginning with the prefix org.xyz, which has either the @Immutable annotation or the @NonPersistent annotation.

(@Immutable @NonPersistent org.xyz..*)

Matches any type in a package beginning with the prefix org.xyz, which has both an @Immutable annotation and an @NonPersistent annotation.

(@(@Inherited *) org.xyz..*)

Matches any type in a package beginning with the prefix org.xyz, which has an inheritable annotation. The annotation pattern @(@Inherited *) matches any annotation of a type matching the type pattern @Inherited *, which in turn matches any type with the @Inherited annotation.

Signature Patterns

A FieldPattern is described by the following grammar:

  	
  		FieldPattern := 
  		    AnnotationPattern? FieldModifiersPattern? 
  		    TypePattern (TypePattern DotOrDotDot)? SimpleNamePattern

		FieldModifiersPattern := '!'? FieldModifier FieldModifiersPattern*
		                         		
		FieldModifier := 'public' | 'private' | 'protected' | 'static' | 
		                 'transient' | 'final' 

		DotOrDotDot := '.' | '..'		            		      
		            		      		            			
		SimpleNamePattern := JavaIdentifierChar+ ('*' SimpleNamePattern)?		            
	

The optional AnnotationPattern restricts matches to fields with annotations that match the pattern. For example:

@SensitiveData * *

Matches a field of any type and any name, that has an annotation of type @SensitiveData

@SensitiveData List org.xyz..*.*

Matches a member field of a type in a package with prefix org.xzy, where the field is of type List, and has an annotation of type @SensitiveData

(@SensitiveData *) org.xyz..*.*

Matches a member field of a type in a package with prefix org.xzy, where the field is of a type which has a @SensitiveData annotation.

@Foo (@Goo *) (@Hoo *).*

Matches a field with an annotation @Foo, of a type with an annotation @Goo, declared in a type with annotation @Hoo.

@Persisted @Classified * *

Matches a field with an annotation @Persisted and an annotation @Classified.

A MethodPattern is of the form

  	
  		MethodPattern := 
  		    AnnotationPattern? MethodModifiersPattern? TypePattern 
  		                       (TypePattern DotOrDotDot)? SimpleNamePattern 
  		                       '(' FormalsPattern ')'ThrowsPattern?

		MethodModifiersPattern := '!'? MethodModifier MethodModifiersPattern*
		
		MethodModifier := 'public' | 'private' | 'protected' | 'static' | 
		                  'synchronized' | 'final' 
		            		      
		FormalsPattern := '..' (',' FormalsPatternAfterDotDot)* |
		                  OptionalParensTypePattern (',' FormalsPattern)* |
		                  TypePattern '...'
		                  
		FormalsPatternAfterDotDot := 
		        OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
		        TypePattern '...'
		                                               		                  
		ThrowsPattern := 'throws' TypePatternList
		
		TypePatternList := TypePattern (',' TypePattern)*
		            					            
	

Note: compared to the previous version, this definition of MethodPattern does not allow parameter annotation matching (only matching on annotations of parameter types).

A ConstructorPattern has the form

  	
  		ConstructorPattern := 
  		    AnnotationPattern? ConstructorModifiersPattern?  
  		                       (TypePattern DotOrDotDot)? 'new' '(' FormalsPattern ')'
  		                       ThrowsPattern?
	
		ConstructorModifiersPattern := '!'? ConstructorModifier ConstructorModifiersPattern*
		
		ConstructorModifier := 'public' | 'private' | 'protected'
		
	

The optional AnnotationPattern at the beginning of a method or constructor pattern restricts matches to methods/constructors with annotations that match the pattern. For example:

@Oneway * *(..)

Matches a method with any return type and any name, that has an annotation of type @Oneway.

@Transaction * (@Persistent org.xyz..*).*(..)

Matches a method with the @Transaction annotation, declared in a type with the @Persistent annotation, and in a package beginning with the org.xyz prefix.

* *.*(@Immutable *,..)

Matches any method taking at least one parameter, where the parameter type has an annotation @Immutable.

Example Pointcuts

within(@Secure *)

Matches any join point where the code executing is declared in a type with an @Secure annotation. The format of the within pointcut designator in AspectJ 5 is 'within' '(' OptionalParensTypePattern ')'.

staticinitialization(@Persistent *)

Matches the staticinitialization join point of any type with the @Persistent annotation. The format of the staticinitialization pointcut designator in AspectJ 5 is 'staticinitialization' '(' OptionalParensTypePattern ')'.

call(@Oneway * *(..))

Matches a call to a method with a @Oneway annotation.

execution(public (@Immutable *) org.xyz..*.*(..))

The execution of any public method in a package with prefix org.xyz, where the method returns an immutable result.

set(@Cachable * *)

Matches the set of any cachable field.

handler(!@Catastrophic *)

Matches the handler join point for the handling of any exception that is not Catastrophic. The format of the handler pointcut designator in AspectJ 5 is 'handler' '(' OptionalParensTypePattern ')'.

Runtime type matching and context exposure

AspectJ 5 supports a set of "@" pointcut designators which can be used both to match based on the presence of an annotation at runtime, and to expose the annotation value as context in a pointcut or advice definition. These designators are @args, @this, @target, @within, @withincode, and @annotation

It is a compilation error to attempt to match on an annotation type that does not have runtime retention using @this, @target or @args. It is a compilation error to attempt to use any of these designators to expose an annotation value that does not have runtime retention.

The this(), target(), and args() pointcut designators allow matching based on the runtime type of an object, as opposed to the statically declared type. In AspectJ 5, these designators are supplemented with three new designators : @this() (read, "this annotation"), @target(), and @args().

Like their counterparts, these pointcut designators can be used both for join point matching, and to expose context. The format of these new designators is:

  	
  	    AtThis := '@this' '(' AnnotationOrIdentifer ')'
    
  	    AtTarget := '@target' '(' AnnotationOrIdentifier ')'
  	
  	    AnnotationOrIdentifier := FullyQualifiedName | Identifier
        
  	    AtArgs := '@args' '(' AnnotationsOrIdentifiersPattern ')'
        
  	    AnnotationsOrIdentifiersPattern :=
  	                      '..' (',' AnnotationsOrIdentifiersPatternAfterDotDot)? |
  	                      AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPattern)* |
  	                      '*' (',' AnnotationsOrIdentifiersPattern)*
		                  
  	    AnnotationsOrIdentifiersPatternAfterDotDot := 
		                  AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPatternAfterDotDot)* |
		                  '*' (',' AnnotationsOrIdentifiersPatternAfterDotDot)*
  	
	

The forms of @this() and @target() that take a single annotation name are analogous to their counterparts that take a single type name. They match at join points where the object bound to this (or target, respectively) has an annotation of the specified type. For example:

@this(Foo)

Matches any join point where the object currently bound to 'this' has an annotation of type Foo.

call(* *(..)) && @target(Classified)

Matches a call to any object where the target of the call has a @Classified annotation.

Annotations can be exposed as context in the body of advice by using the forms of @this(), @target() and @args() that use bound variables in the place of annotation names. For example:

  	pointcut callToClassifiedObject(Classified classificationInfo) :
  	    call(* *(..)) && @target(classificationInfo);

  	pointcut txRequiredMethod(Tx transactionAnnotation) :
  	    execution(* *(..)) && @this(transactionAnnotation) 
  	    && if(transactionAnnotation.policy() == TxPolicy.REQUIRED);
	

The @args pointcut designator behaves as its args counterpart, matching join points based on number and position of arguments, and supporting the * wildcard and at most one .. wildcard. An annotation at a given position in an @args expression indicates that the runtime type of the argument in that position at a join point must have an annotation of the indicated type. For example:

  	/**
  	 * matches any join point with at least one argument, and where the
  	 * type of the first argument has the @Classified annotation
  	 */
  	pointcut classifiedArgument() : @args(Classified,..);
  	
  	/**
  	 * matches any join point with three arguments, where the third
  	 * argument has an annotation of type @Untrusted.
  	 */
  	pointcut untrustedData(Untrusted untrustedDataSource) : 
  	    @args(*,*,untrustedDataSource);
	

Note: an alternative design would be to allow both annotation patterns and type patterns to be specified in the existing args pcd. This works well for matching, but is more awkward when it comes to exposing context.

Access to AnnotatedElement information is available reflectively with the body of advice through the thisJoinPoint, thisJoinPointStaticPart, and thisEnclosingJoinPointStaticPart variables. To access annotations on the arguments, or object bound to this or target at a join point you can use the following code fragments:

  	Annotation[] thisAnnotations = thisJoinPoint.getThis().getClass().getAnnotations();
  	Annotation[] targetAnnotations = thisJoinPoint.getTarget().getClass().getAnnotations();
  	Annotation[] firstParamAnnotations = thisJoinPoint.getArgs()[0].getClass().getAnnotations();
	

Note: it would be nicer to provide direct helper methods in the JoinPoint interface or a sub-interface that provide the annotations directly, something like "AnnotatedElement getThisAnnotationInfo()". The problem here is that the "AnnotatedElement" type is only in the Java 5 runtime libraries, and we don't want to tie the AspectJ runtime library to Java 5. A sub-interface and downcast solution could be used if these helpers were felt to be sufficiently important.

The @within and @withincode pointcut designators match any join point where the executing code is defined within a type (@within), or a method/constructor (@withincode) that has an annotation of the specified type. The form of these designators is:

  	
        AtWithin := '@within' '(' AnnotationOrIdentifier ')'
        AtWithinCode := '@withincode' '(' AnnotationOrIdentifier ')'        
    

Some examples of using these designators follow:

@within(Foo)

Matches any join point where the executing code is defined within a type which has an annotation of type Foo.

pointcut insideCriticalMethod(Critical c) : @withincode(c);

Matches any join point where the executing code is defined in a method or constructor which has an annotation of type @Critical, and exposes the value of the annotation in the parameter c.

The @annotation pointcut designator matches any join point where the subject of the join point has an annotation of the given type. Like the other @pcds, it can also be used for context exposure.

  	
        AtAnnotation := '@annotation' '(' AnnotationOrIdentifier ')'
    

The subject of a join point is defined in the table in chapter one of this guide.

Access to annotation information on members at a matched join point is also available through the getSignature method of the JoinPoint and JoinPoint.StaticPart interfaces. The Signature interfaces are extended with additional operations that provide access to the java.lang.reflect Method, Field and Constructor objects on which annnotations can be queried. The following fragment illustrates an example use of this interface to access annotation information.

  	Signature sig = thisJoinPointStaticPart.getSignature();
  	AnnotatedElement declaringTypeAnnotationInfo = sig.getDeclaringType();
  	if (sig instanceof MethodSignature) {
  	  // this must be a call or execution join point
  	  Method method = ((MethodSignature)sig).getMethod();
  	}
	

Note again that it would be nicer to add the method getAnnotationInfo directly to MemberSignature, but this would once more couple the runtime library to Java 5.

The @this,@target and @args pointcut designators can only be used to match against annotations that have runtime retention. The @within, @withincode and @annotation pointcut designators can only be used to match against annotations that have at least class-file retention, and if used in the binding form the annotation must have runtime retention.

Package and Parameter Annotations

Note: A previous design allowed package annotation patterns to be specified directly in type patterns, and parameter annotation patterns to be specified directly in method and constructor signature patterns. Because this made some pointcut expressions hard to read and understand, we moved in favour of the design presented below, which also has its drawbacks. Matching on package and parameter annotations will be deferred until after the 1.5.0 release so that we can gain more understanding of the kinds of uses AspectJ users are making of annotations in pointcut expressions before commiting to any one approach.

Annotation Inheritance and pointcut matching

According to the Java 5 specification, non-type annotations are not inherited, and annotations on types are only inherited if they have the @Inherited meta-annotation. Given the following program:

  	class C1 {
  	  @SomeAnnotation
  	  public void aMethod() {...}
  	}
  	
  	class C2 extends C1 {
  	  public void aMethod() {...}
  	}
  	
  	class Main {
  	  public static void main(String[] args) {
  	    C1 c1 = new C1();
  	    C2 c2 = new C2();
  	    c1.aMethod();
  	    c2.aMethod();
  	  }
  	}
  	
  	aspect X {
  	
  	  pointcut annotatedMethodCall() : 
  	    call(@SomeAnnotation * C1.aMethod());
  	
  	  pointcut c1MethodCall() :
  	    call(* C1.aMethod());
  	}
	

The pointcut annotatedMethodCall will match the call to c1.aMethod(), but not the call to c2.aMethod().

The pointcut c1MethodCall matches both c1.aMethod() and c2.aMethod().

Limitations

It would be useful to be able to match join points based on annotation values, rather than merely the presence of a class-file retention annotation of a given type. This facility may be supported in a future version of AspectJ, by expanding the definition of AnnotationPattern. Matching annotation values for annotations with runtime retention can be done by exposing the annotation value as a pointcut parameter and then using an if pointcut expression to test the value.

Using Annotations with declare statements

Declare error and declare warning

Since pointcut expressions in AspectJ 5 support join point matching based on annotations, this facility can be exploited when writing declare warning and declare error statements. For example:

  	declare warning : withincode(@PerformanceCritical * *(..)) &&
  	                  call(@ExpensiveOperation * *(..))
  	                : "Expensive operation called from within performance critical section";
	
  	declare error : call(* org.xyz.model.*.*(..)) &&
  	                !@within(Trusted)
  	                : "Untrusted code should not call the model classes directly";
	

declare parents

The general form of a declare parents statement is:

  	declare parents : TypePattern extends Type;
  	declare parents : TypePattern implements TypeList;
	

Since AspectJ 5 supports annotations as part of a type pattern specification, it is now possible to match types based on the presence of annotations with either class-file or runtime retention. For example:

declare parents : (@Secured *) implements SecuredObject;

All types with the @Secured annotation implement the SecuredObject inteface.

declare parents : (@Secured BankAccount+) implements SecuredObject;

The subset of types drawn from the BankAccount type and any subtype of BankAccount, where the @Secured annotation is present, implement the SecuredObject interface.

An annotation type may not be used as the target of a declare parents statement. If an annotation type is named explicitly as the target of a declare parents statement, a compilation error will result. If an annotation type is matched by a non-explicit type pattern used in a declare parents statement it will be ignored (and an XLint warning issued).

declare precedence

The general form of a declare precedence statement is:

  	declare precedence : TypePatList;
	

AspectJ 5 allows the type patterns in the list to include annotation information as part of the pattern specification. For example:

declare precedence : (@Security *),*;

All aspects with the @Security annotation take precedence over any other aspects in the system. (Or, more informally, all security-related aspects take precedence).

Declare Annotation

AspectJ 5 supports a new kind of declare statement, declare annotation. This takes different forms according to the recipient of the annotation: declare @type for types, declare @method for methods, declare @constructor for constructors, and declare @field for fields. declare @package may be supported in a future release.

The general form is:

  	declare @<kind> : ElementPattern : Annotation ;
	

Where annotation is a regular annotation expression as defined in the Java 5 language. If the annotation has the @Target meta-annotation, then the elements matched by ElementPattern must be of the kind specified by the @Target annotation.

ElementPattern is defined as follows:

  	        ElementPattern := TypePattern |
  	                          MethodPattern |
  	                          ConstructorPattern |
  	                          FieldPattern
	

The following examples illustrate the use of declare annotation.

declare @type : org.xyz.model..* : @BusinessDomain ;

All types defined in a package with the prefix org.xyz.model have the @BusinessDomain annotation.

declare @method : public * BankAccount+.*(..) : @Secured(role="supervisor")

All public methods in BankAccount and its subtypes have the annotation @Secured(role="supervisor").

declare @constructor : BankAccount+.new(..) : @Secured(role="supervisor")

All constructors in BankAccount and its subtypes have the annotation @Secured(role="supervisor").

declare @field : * DAO+.* : @Persisted;

All fields defined in DAO or its subtypes have the @Persisted annotation.

Inter-type Declarations

An annotation type may not be the target of an inter-type declaration.

Chapter 3. Generics

Generics in Java 5

This section provides the essential information about generics in Java 5 needed to understand how generics are treated in AspectJ 5. For a full introduction to generics in Java, please see the documentation for the Java 5 SDK.

Declaring Generic Types

A generic type is declared with one or more type parameters following the type name. By convention formal type parameters are named using a single letter, though this is not required. A simple generic list type (that can contain elements of any type E) could be declared:

		interface List<E> {
		   Iterator<E> iterator();
		   void add(E anItem);
		   E remove(E anItem);  
		}
		

It is important to understand that unlike template mechanisms there will only be one type, and one class file, corresponding to the List interface, regardless of how many different instantiations of the List interface a program has (each potentially providing a different value for the type parameter E). A consequence of this is that you cannot refer to the type parameters of a type declaration in a static method or initializer, or in the declaration or initializer of a static variable.

A parameterized type is an invocation of a generic type with concrete values supplied for all of its type parameters (for example, List<String> or List<Food>).

A generic type may be declared with multiple type parameters. In addition to simple type parameter names, type parameter declarations can also constrain the set of types allowed by using the extends keyword. Some examples follow:

class Foo<T> {...}

A class Foo with one type parameter, T.

class Foo<T,S> {...}

A class Foo with two type parameters, T and S.

class Foo<T extends Number> {...}

A class Foo with one type parameter T, where T must be instantiated as the type Number or a subtype of Number.

class Foo<T, S extends T> {...}

A class Foo with two type parameters, T and S. Foo must be instantiated with a type S that is a subtype of the type specified for parameter T.

class Foo<T extends Number & Comparable> {...}

A class Foo with one type parameter, T. Foo must be instantiated with a type that is a subtype of Number and that implements Comparable.

Using Generic and Parameterized Types

You declare a variable (or a method/constructor argument) of a parameterized type by specifying a concrete type specfication for each type parameter in the generic type. The following example declares a list of strings and a list of numbers:

        List<String> strings;
        List<Number> numbers;
		

It is also possible to declare a variable of a generic type without specifying any values for the type parameters (a raw type). For example, List strings. In this case, unchecked warnings may be issued by the compiler when the referenced object is passed as a parameter to a method expecting a parameterized type such as a List<String>. New code written in the Java 5 language would not be expected to use raw types.

Parameterized types are instantiated by specifying type parameter values in the constructor call expression as in the following examples:

        List<String> strings = new MyListImpl<String>();
        List<Number> numbers = new MyListImpl<Number>();
		

When declaring parameterized types, the ? wildcard may be used, which stands for "some type". The extends and super keywords may be used in conjunction with the wildcard to provide upper and lower bounds on the types that may satisfy the type constraints. For example:

List<?>

A list containing elements of some type, the type of the elements in the list is unknown.

List<? extends Number>

A list containing elements of some type that extends Number, the exact type of the elements in the list is unknown.

List<? super Double>

A list containing elements of some type that is a super-type of Double, the exact type of the elements in the list is unknown.

A generic type may be extended as any other type. Given a generic type Foo<T> then a subtype Goo may be declared in one of the following ways:

class Goo extends Foo

Here Foo is used as a raw type, and the appropriate warning messages will be issued by the compiler on attempting to invoke methods in Foo.

class Goo<E> extends Foo

Goo is a generic type, but the super-type Foo is used as a raw type and the appropriate warning messages will be issued by the compiler on attempting to invoke methods defined by Foo.

class Goo<E> extends Foo<E>

This is the most usual form. Goo is a generic type with one parameter that extends the generic type Foo with that same parameter. So Goo<String< is a subclass of Foo<String>.

class Goo<E,F> extends Foo<E>

Goo is a generic type with two parameters that extends the generic type Foo with the first type parameter of Goo being used to parameterize Foo. So Goo<String,Integer< is a subclass of Foo<String>.

class Goo extends Foo<String>

Goo is a type that extends the parameterized type Foo<String>.

A generic type may implement one or more generic interfaces, following the type binding rules given above. A type may also implement one or more parameterized interfaces (for example, class X implements List<String>, however a type may not at the same time be a subtype of two interface types which are different parameterizations of the same interface.

Subtypes, Supertypes, and Assignability

The supertype of a generic type C is the type given in the extends clause of C, or Object if no extends clause is present. Given the type declaration

        public interface List<E> extends Collection<E> {... }
		

then the supertype of List<E> is Collection<E>.

The supertype of a parameterized type P is the type given in the extends clause of P, or Object if no extends clause is present. Any type parameters in the supertype are substituted in accordance with the parameterization of P. An example will make this much clearer: Given the type List<Double> and the definition of the List given above, the direct supertype is Collection<Double>. List<Double> is not considered to be a subtype of List<Number>.

An instance of a parameterized type P<T1,T2,...Tn>may be assigned to a variable of the same type or a supertype without casting. In addition it may be assigned to a variable R<S1,S2,...Sm> where R is a supertype of P (the supertype relationship is reflexive), m <= n, and for all type parameters S1..m, Tm equals Sm or Sm is a wildcard type specification and Tm falls within the bounds of the wildcard. For example, List<String> can be assigned to a variable of type Collection<?>, and List<Double> can be assigned to a variable of type List<? extends Number>.

Generic Methods and Constructors

A static method may be declared with one or more type parameters as in the following declaration:

          static <T> T first(List<T> ts) { ... }
		

Such a definition can appear in any type, the type parameter T does not need to be declared as a type parameter of the enclosing type.

Non-static methods may also be declared with one or more type parameters in a similar fashion:

          <T extends Number> T max(T t1, T t2) { ... }
		

The same technique can be used to declare a generic constructor.

Erasure

Generics in Java are implemented using a technique called erasure. All type parameter information is erased from the run-time type system. Asking an object of a parameterized type for its class will return the class object for the raw type (eg. List for an object declared to be of type List<String>. A consequence of this is that you cannot at runtime ask if an object is an instanceof a parameterized type.

Generics in AspectJ 5

AspectJ 5 provides full support for all of the Java 5 language features, including generics. Any legal Java 5 program is a legal AspectJ 5 progam. In addition, AspectJ 5 provides support for generic and parameterized types in pointcuts, inter-type declarations, and declare statements. Parameterized types may freely be used within aspect members, and support is also provided for generic abstract aspects.

Matching generic and parameterized types in type patterns

The foundation of AspectJ's support for generic and parameterized types in aspect declarations is the extension of type pattern matching to allow matching against generic and parameterized types.

The type pattern "Foo" matches all types named Foo, whether they be simple types, generic types, or parameterized types. So for example, Foo, Foo<T>, and Foo<String>will all be matched.

AspectJ 5 also extends the specification of type patterns to allow explicit matching of generic and parameterized types by including one or more type parameter patterns inside angle braces (< >) immediately after the type pattern. For example, List<String>

  	  	TypePattern := SimpleTypePattern |
  	  	               '!' TypePattern |
  	  	               '(' AnnotationPattern? TypePattern ')'
  	  	               TypePattern '&&' TypePattern |
  	  	               TypePattern '||' TypePattern |
  	  	               TypePattern '<' TypeParameterPatternList '>'
  	  	               
  	  	TypeParameterPatternList ::= TypeParameterPattern (',' TypeParameterPattern)*
  	  	
  	  	TypeParameterPattern ::= TypePattern |
  	  	                                    '?' TypeBoundPattern?
  	  	                                    
  	  	TypeBoundPattern ::= 'extends' TypePattern AdditionalBoundPatternList? |
  	  	                               'super' TypePattern AdditionalBoundPatternList?
  	  	                     
  	  	AdditionalBoundPatternList ::= AdditionalBoundPattern AdditionalBoundPatternList |
  	  	                                         AdditionalBoundPattern
  	  	                               
  	  	AdditionalBoundPattern ::= '&' TypePattern 
  	  	
		

A simple identifier (such as String) occuring in a type parameter list will be treated as a type name unless a type variable of that name is in scope (declaring type variables is covered later). The type pattern List<E> will result in an "invalid absolute type name" warning if no type E is in scope (declared in the default package, or imported in the compilation unit) and no declaration of E as a type variable is in scope either.

Some simple examples of type patterns follow:

List<String>

Matches the parameterized type List<String>

List<? extends Number>

Matches the parameterized type List<? extends Number>

List<E>

Outside of a scope in which Eis defined as a type variable, this pattern matches the parameterized type List<E>. If E is not a type then an invalidAbsoluteTypeName xlint warning will be issued.

In a scope in which E is defined as a type variable, this pattern matches the generic type List<E>. The type parameter name does not have to match the name used in the declaration of List, but the bounds must match. This pattern also matches any parameterization of List that satisfies the bounds of the type variable (for example, List<String>).

The *, +, and .. wildcards may be used in type patterns matching against generic and parameterized types (just as in any other type pattern). The + wildcard matches all subtypes. Recalling the discussion on subtypes and supertypes in the previous section, note that the pattern List<Number>+ will match List<Number> and LinkedList<Number>, but not List<Double>. To match lists of any number type use the pattern List<Number+> which will match List<Number>, List<Double>, List<Float> and so on.

The generics wildcard ? is considered part of the signature of a parameterized type, and is not used as an AspectJ wildcard in type matching. For example:

List<*>

Matches any generic or parameterized Listtype (List<String>, List<Integer> and so on) with a single type parameter.

List<?>

Matches the parameterized type List<?> (and does not match List<String>, List<Integer> and so on)

List<? extends Number+>

Matches List<? extends Number>, List<? extends Double>, and so on, but does not match List<Double>.

Signature patterns

Now that we understand how to write type patterns that match generic and parameterized types, it is time to look at how these can be utilized to match member declarations by using signature patterns.

To match members declared in generic types and making use of type variables defined in those types (for example interface Foo<T> { public T doSomething(); } use a signature pattern of the form:

      	X Foo<X>.doSomething()
		

This assumes a scope in which X is declared as a type variable. As with type patterns, the name of the type variable does not have to match the name used in the member declaration, but the bounds must match. For example, if the interface was declared as Foo<T extends Number> then the signature pattern would be: X Foo<X extends Number>.doSomething().

T Util<T extends Number,S>.someFunction(List<S>)

Matches the method someFunction in a generic type Util with two type parameters, the first type parameter having an upper bound of Number.

LinkedList<E>.new()

Matches the no-argument constructor of the generic type LinkedList.

Matching a field with a generic type works in the same way. For example:

      	T *<T>.*
		

Matches a field of the type of type parameter T in any generic type with a single unbounded type parameter (the pattern*<T>). The field may be of any name.

Matching of members of parameterized types is straightforward. For example, void List<String>.add(String) matches the add method in the parameterized type List<String>.

To match a generic method the generic method type variable declarations become part of the signature pattern. For example:

          <T> List<T> *.favourites(List<T>)
		

matches a generic method favourites declared in any type. To match a static generic method simply include the static modifier in the type pattern.

Pointcuts

In this section we discuss how type patterns and signature patterns matching on generic and parameterized types, methods, and constructors can be used in pointcut expressions. We distinguish between pointcuts that match based on static type information, and pointcuts that match based on runtime type information (this, target, args).

First however we need to address the notion of type variables and scopes. There is a convention in Java, but no requirement, that type variables are named with a single letter. Likewise it is rare, but perfectly legal, to declare a type with a single character name. Given the type pattern List<Strng>, is this a mis-spelling of the parameterized type pattern List<String> or is it a generic type pattern with one unbounded type variable Strng?. Alternatively, given the type pattern List<E>, if the type E cannot be found, is this a missing import statement or an implied type variable? There is no way for AspectJ to disambiguate in these situations without an explicit declaration of type variable names. If E is defined as a type variable, and Strng is not, then both declarations can be correctly interpreted.

Type Variables in Pointcut Expressions

The type variables in scope for a pointcut primitive are declared in a type variable list immediately following the pointcut desginator keyword. For example:

          call<T>(* Foo<T>.*(T))
		

matches a call to a method with any name (*) declared by a generic type Foo with one unbounded type parameter. The method takes one argument which is of the type of the type variable.

In contrast, the pointcut

          call(* Foo<T>.*(T))
		

matches a call to a method with any name that takes an argument of type T, where the target of the call is declared as the parameterized type Foo<T>. If there is no type T in scope, an "invalid absolute type name (T)" warning will be issued.

The type variables declaration following a pointcut designator permits only simple identifiers (e.g. <S,T> and not <S extends Number>).

A type variable declaration list can appear following any pointcut designator except for handler (Java 5 does not permit a generic class to be a direct or indirect subtype of Throwable - see JLS 8.1.2), the dynamic pointcuts this, target, args, if, cflow, cflowbelow, and the annotation pointcut designators (@args, @this, @within and so on).

Initialization and execution pointcuts

Recall that there is only ever one type for a generic type (e.g. List<E>) regardless of how many different parameterizations of that type (e.g. List<String>, List<Double>) are used within a program. For join points that occur within a type, such as execution join points, it therefore only makes sense to talk about execution join points for the generic type. Given the generic type

        public class Foo<T> {
            
          T doSomething(T toSomeT) {
             return T;
          }  
          
        }
		

then

execution<T>(T Foo<T>.doSomething(..))

matches the execution of the doSomething method in Foo.

execution(* Foo.doSomething(..))

also matches the execution of the doSomething method in Foo.

execution(T Foo.doSomething(..))

results in an "invalid absolute type name (T)" warning since T is interpreted as a type, not a type variable.

execution(String Foo<String>.doSomething(..))

results in a compilation error "no execution join points for parameterized type Foo<String>, use a generic signature instead".

Given the type declaration

        public class Bar<N extends Number> {
            
          N doSomething(N toSomeN) {
             return N;
          }  
          
        }
		

then

execution<T>(T Bar<T>.doSomething(..))

does not match the execution of Bar.doSomething since the bounds of the type parameter T in the pointcut expression do not match the bounds of the type parameter N in the type declaration.

execution<T>(T Bar<T extends Number>.doSomething(..))

matches the execution of the doSomething method in Bar.

execution<T extends Number>(T Bar<T>.doSomething(..))

results in a compilation error, since type variable bounds must be specified as part of the declaring type pattern, and not in the type variable list.

If a type implements a parameterized interface, then execution join points exist and can be matched for the parameterized interface operations within the implementing type. For example, given the pair of types:

        public interface Greatest<T> {
            T greatest(List<T> ts);  
        }
        
        public class  NumberOperations implements Greatest<Number> {
             public Number greatest(List<Number> numbers) {
                //...
             }
        }
		

then

        execution(* Greatest<Number>.*(..))
		

will match the execution of the greatest method declared in NumberOperations. However, it does not match the execution of greatest in the program below:

        public interface Greatest<T> {
            T greatest(List<T> ts);  
        }
        
        public class  NumberOperations<N extends Number> implements Greatest<N> {
             public N greatest(List<N> numbers) {
                //...
             }
        }
        
        // in some fragment of code...
        NumberOperations<Number> numOps = new NumberOperations<Number>();
        numOps.greatest(numList);
		

Since there is only one generic type, NumberOperations, which implements a generic interface. Either of the pointcut expressions execution<T>(* Greatest<T>>.*(..)) or execution<T>(* Greatest<T extends Number>>.*(..)) will match the execution of greatest in this example. Recall from chapter Join Point Signatures that a kinded pointcut primitive matches a join point if it exactly matches one of the signatures of the join point. The signatures of the execution join point for greatest in the example above are:

public N Greatest<N>.greatest(List<N>)

from the declaration in the Greatest interface, and

public N Greatest<N extends Number>.greatest(List<N>)

from the additional bounds restriction of N in the declaration of NumberOperations

Join points for staticinitialization,initialization and preinitialization only ever exist on a generic type (an interface cannot define a constructor). The expression initialization<T>(Foo<T>.new(..)) which match any initialization join point for the generic type Foo<T>, and staticinitialization<T>(Foo<T>) matches the static initialization of that same type.

The expression staticinitialization(List<String>) will result in a compilation error: there is no static initialization join point for the parameterized type List<String>. However, the expression staticinitialization(List<String>+) is legal, and will match the static initialization of any type that implements List<String>. The expression staticinitialization<T>(List<T>+) will match the static initialization join point of any type that either extends or implements the generic type List<T> or implements any parameterization of that interface.

Static scoping: within and withincode

The within and withincode pointcut designators both match the execution of join points that occur within a type or a member of a type respectively. Therefore the same considerations with respect to there only being one type for a generic type regardless of how many parameterizations of that type are used in a program apply.

The within pointcut designator can never be used in conjunction with a simple parameterized type. So

within<T>(Foo<T>)

matches all join points occurring within the generic type Foo<T>, and

within(Foo<String>)

results in a compilation error since there is no concept of a join point within a parameterized type, but

within(Foo<String>+)

matches any join point occurring within a type that implements Foo<String>.

The withincode designator is likewise normally used with a generic type, but can be used with a parameterized interface type to match join points arising from code lexically within the implementation of the interface methods in a type that implements the parameterized interface.

withincode<T>(* Foo<T>.*(..))

matches all join points arising from code lexically within a method of the generic type Foo<T>

withincode(* Foo<String>.*(..))

results in a compilation error if Foo is not an interface. If Foo is an interface then it matches all join points arising from code lexically within the implementation of the interface methods in a type that implements Foo<String>.

withincode(* Foo<String>+.*(..))

matches any join point occurring within a method of a type that implements Foo<String>.

Call, get and set pointcuts

The call, get, and set join points can occur on the client side (ie. outside of the type owning the member being called, accessed, or updated) or within the type that owns the target member. The following short program demonstrates this:

        public class Foo<T> {
        
          public T timeFor;  
          
          public Foo<T>(T aCuppa) {
                timeFor = aCuppa;      // set-site A
          }
        
          public void doThis(T t) {
            doThat(t);  // call-site A
          }    
          
          public void doThat(T t) {
             return;
          }
            
        }
        
        public class Main {
          public static void main(String[] args) {
            Foo<String> foos = new Foo<String>();
            foos.doThis("b");  //call-site B  
            foos.doThat("c");  // call-site C
            foos.timeFor = "a cuppa"; // set-site B
          }
        }
		

We have annotated the three method call sites as call-site A, call-site B, and call-site C. Call-site A is situated within the generic type Foo<T> and the call join point has signature public void Foo<T>doThat(T). The join point arising from call-site B is a client-side call join point and has the signatures public void Foo<String>doThis(String) (from the static type of foos) and public void Foo<T>doThis(T). Likewise the call join point arising from call-site C has the signatures public void Foo<String>doThat(String) (from the static type of foos) and public void Foo<T>doThat(T). A call pointcut expression matches if the signature pattern exactly matches one of the signatures of the call join point.

The signatures for get and set join points works in a similar fashion. At set-site A in the above example, the set join point has signature public T Foo<T>.timeFor. At set-site B the set join point has signatures public T Foo<T>.timeFor and public String Foo<String>.timeFor. A get or set pointcut expression matches if the signature pattern exactly matches one of the signatures of the join point.

Some examples follow:
call(* List<?>.*(..))

matches a call to any method of a List<?> (a call where the target is declared to be a List<?>). For example:

        int countItems(List<?> anyList) {
          return anyList.size();   // matched by call(* List<?>.*(..))
        }
		
call<T>(* List<T>.*(..))

matches any call to an operation defined in the generic type List<E>. This includes calls made to List<String>, List<Number>, List<? super Foo> and so on.

get<T>(T *<T extends Account>.*)

matches the get of any field defined in a generic type with one type parameter that has an upper bound of Account. The field has the type of the type parameter, and can be of any name. This pointcut expression matches both gets of the field within the declaring type, and also gets on parameterized instances of the type.

set(Account Foo<Account>.*Account)

matches the set of a field of type Account where the target is of type Foo<Account> and the field name ends with "Account". Does not match sets of any "*Account" field occurring within the Foo type itself.

call(* List<? extends Number>.add(..))

matches any call to add an element to a list of type List<? extends Number>. Does not match calls to add elements to lists of type List<Number> or List<Double> as these are distinct types.

call(* List<Number+>.add(..))

matches any call to add an element to a list of type Number or any subclass of Number. For example, List<Number>, List<Double> List<Float>. Does not match calls to add elements to lists of type List<? extends Number> as this is a distinct type.

Handler

The Java Language Specification states that a generic class may not be a direct or indirect subclass of Throwable. Therefore it is a compilation error to use a generic or parameterized type pattern in a handler pointcut expression.

Runtime type matching: this, target and args

Java 5 generics are implemented using a technique known an erasure. In particular, what gets "erased" is the ability to find out the parameterized runtime type of an instance of a generic type. You can ask if something is an instanceof List, but not if something is an instanceof List<String>

The this, target and args pointcut designators all match based on the runtime type of the appropriate object (this, target, or argument) at a join point. To match any parameterization of a generic type, simply use the raw type (type variables are not permitted with these designators). For example:

target(List)

matches any call to an instance of List (including List<String>, List<Number>, and so on.

args (List)

matches any join point with a single argument that is an instance of List.

To match specific parameterizations of a generic type, simply use the type that you require the relevant object to be an instance of inside the pointcut expression. For example: target(List<String>).

Recall that runtime tests to determine whether an object is an instance of a parameterized type are not possible due to erasure. Therefore AspectJ matching behaviour with parameterized types for this, target and args is as follows.

If it can be statically determined that a given object will always be an instance of the required type, then the pointcut expressions matches. For example, given a variable bankAccounts of type Set<BankAccount> and the pointcut expression target(Set<BankAccount>) then any call made to bankAccounts will be matched.
If it can be statically determined that a given object can never be an instance of the required type, then the pointcut expression does not match. The expression target(List<String>)will never match a call made using a variable of type List<Number> (it is not possible for a type to implement two different parameterizations of the same interface).
If an object might be an instance of the required type in some circumstances but not in others, then since it is not possible to perform the runtime test, AspectJ deems the pointcut expression to match, but issues an unchecked warning. This is analogous to the behaviour of the Java compiler when converting between raw and parameterized types. Given a variable of type List<? extends Number> and a call join point with target pointcut expression target(List<Double>) then the expression matches but with an unchecked warning. The warning can be suppressed by annotating the associated advice with either @SuppressAjWarnings or @SuppressAjWarnings("unchecked").

When using a parameterized type with the this pointcut designator then a joinpoint is unambiguously matched if and only if one or more of the following conditions hold:

the runtime type of the this object extends or implements the parameterized type. For example, class Foo implements List<String> will match this(List<String>).
The parameterized "this" type is given using a generics wildcard in the pointcut expression, and the bounds of the generic runtime type of this are such that all valid parameterizations are matched by the wildcard. For example, the pointcut expression this(List<? extends Number>) will match a this object of type class Foo<N extends Number> implements List<N>, but not an object of type class Foo<N> implements List<N>.

You've already seen some examples of using the generic wildcard ? in parameterized type patterns. Since this, target and args match using an instance of test, the generic wildcard can be useful in specifying an acceptable range of parameterized types to match. When used in the binding form, the same restrictions on operations permitted on the bound variable apply as when a method declares a parameter with a wildcard type. For example, in the advice below, it is a compilation error to attemp to add an element into the list aList.

        before(List<? extends Number> aList) : 
          execution(* org.xyz.Foo.*(..)) && args(aList) {
           aList.add(new Double(5.0d));  // Compilation error on this line
        }
		

Declaring pointcuts in generic classes

AspectJ permits pointcuts to be declared in classes as well as aspects. A pointcut defined inside a generic class may not use the type variables of the class in the pointcut expression (just as static members of a generic class may not use type variables). For example:

        public class Foo<T extends Number> {
          
          ...
          
          // Not allowed - uses T in the pointcut expression
          public pointcut fooOperationCall(T t) : 
             call(* Foo<T>.*(T)) && args(t);   
            
            
          // permitted, but recommended to use an alternate variable name in the local 
          // type variable declaration - e.g. execution<S>(...)
          public pointcut fooExecution(Number n) :
             execution<T>(* Foo<T>.*(T)) && args(n); 
        }
		

Inter-type Declarations

AspectJ 5 allows type parameters to be used in inter-type declarations - either for declaring generic methods and constructors, or for declaring members on generic types. The syntax for declaring generic methods and constructors follows the regular AspectJ convention of simply qualifying the member name with the target type.

<T extends Number> T Utils.max(T first, T second) {...}

Declares a generic instance method max on the class Util. The max method takes two arguments, first and second which must both be of the same type (and that type must be Number or a subtype of Number) and returns an instance of that type.

static <E> E Utils.first(List<E> elements) {...}

Declares a static generic method first on the class Util. The first method takes a list of elements of some type, and returns an instance of that type.

<T> Sorter.new(List<T> elements,Comparator<? super T> comparator) {...}

Declares a constructor on the class Sorter. The constructor takes a list of elements of some type, and a comparator that can compare instances of the element type.

A generic type may be the target of an inter-type declaration, used either in its raw form or with type parameters specified. If type parameters are specified, then the number of type parameters given must match the number of type parameters in the generic type declaration. Type parameter names do not have to match. For example, given the generic type Foo<T,S extends Number> then:

String Foo.getName() {...}

Declares a getName method on behalf of the raw type Foo. It is not possible to refer to the type parameters of Foo in such a declaration.

R Foo<Q, R>.getMagnitude() {...}

Declares a method getMagnitude on the generic class Foo. The method returns an instance of the type substituted for the second type parameter in an invocation of Foo.

R Foo<Q, R extends Number>.getMagnitude() {...}

Results in a compilation error since a bounds specification is not allowed in this form of an inter-type declaration (the bounds are determined from the declaration of the target type).

A parameterized type may not be the target of an inter-type declaration. This is because there is only one type (the generic type) regardless of how many different invocations (parameterizations) of that generic type are made in a program. Therefore it does not make sense to try and declare a member on behalf of (say) Foo<String>, you can only declare members on the generic type Foo<T>.

If an inter-type member is declared inside a generic aspect, then the type parameter names from the aspect declaration may be used in the signature specification of the inter-type declaration, but not as type parameter names for a generic target type. In other words the example that follows is legal:

            public abstract aspect A<T> {
              
              private T Foo.data;
              
              public T Foo.getData(T defaultValue) {
                return (this.data != null ? data : defaultValue);
              }   
                
            }
		

Whereas the following example is not allowed and will report an error that a parameterized type may not be the target of an inter-type declaration (since when the type parameter T in the aspect is subsituted with say, String, then the target of the inter-type declaration becomes Goo<String>).

            public abstract aspect A<T> {
              
              private T Goo<T>.data;
              
              public T Goo<T>.getData(T defaultValue) {
                return (this.data != null ? data : defaultValue);
              }   
                
            }
		

Declare Parents

Both generic and parameterized types can be used as the parent type in a declare parents statement (as long as the resulting type hierarchy would be well-formed in accordance with Java's sub-typing rules). Generic types may also be used as the target type of a declare parents statement: a type variable list follows the parents keyword in these cases to declare the type variables in scope. Some examples follow:

declare parents: Foo implements List<String>

The Foo type implements the List<String> interface. If Foo already implements some other parameterization of the List interface (for example, List<Integer> then a compilation error will result since a type cannot implement multiple parameterizations of the same generic interface type.

declare parents <T>: org.xyz..*<T> extends Base<T>

All generic types declared in a package beginning with org.xyz and with a single unbounded type parameter, extend the generic type Base<T>.

declare parents <T>: org.xyz..*<T> extends Base<S>

Results in a compilation error (unless S is a type) since S is not bound in the type pattern.

Declare Soft

It is an error to use a generic or parameterized type as the softened exception type in a declare soft statement. Java 5 does not permit a generic class to be a direct or indirect subtype of Throwable (JLS 8.1.2).

Parameterized Aspects

AspectJ 5 allows an abstract aspect to be declared as a generic type. Any concrete aspect extending a generic abstract aspect must extend a parameterized version of the abstract aspect. Wildcards are not permitted in this parameterization.

Given the aspect declaration:

            public abstract aspect ParentChildRelationship<P,C> {
                ...
            }
		

then

public aspect FilesInFolders extends ParentChildRelationship<Folder,File> {...

declares a concrete sub-aspect, FilesInFolders which extends the parameterized abstract aspect ParentChildRelationship<Folder,File>.

public aspect FilesInFolders extends ParentChildRelationship {...

results in a compilation error since the ParentChildRelationship aspect must be fully parameterized.

public aspect ThingsInFolders<T> extends ParentChildRelationship<Folder,T>

results in a compilation error since concrete aspects may not have type parameters.

public abstract aspect ThingsInFolders<T> extends ParentChildRelationship<Folder,T>

declares a sub-aspect of ParentChildRelationship in which Folder plays the role of parent (is bound to the type variable P).

An exception to the rule that concrete aspects may not be generic is a pertypewithin aspect, which may be declared with a single unbounded type parameter. This is discussed in the chapter on pertypewithin.

The type parameter variables from a generic aspect declaration may be used in place of a type within any member of the aspect. For example, we can declare a ParentChildRelationship aspect to manage the bi-directional relationship between parent and child nodes as follows:

            public abstract aspect ParentChildRelationship<P,C> {
                
                /**
                 * Parents contain a list of children
                 */
                private List<C> P.children;
                    
                /**
                 * Each child has a parent
                 */
                private P C.parent;

                /**
                  * Parents provide access to their children
                  */
                public List<C> P.getChildren() {
                    return Collections.unmodifiableList(children);  
                }
                
                /**
                 * A child provides access to its parent
                 */
                 public P C.getParent() {
                   return parent;
                 }
                
                /**
                 * ensure bi-directional navigation on adding a child
                 */
                public void P.addChild(C child) {
                   if (child.parent != null) {
                     child.parent.removeChild(child);
                   }
                   children.add(child);
                   child.parent = this;
                }

                /**
                 * ensure bi-directional navigation on removing a child
                 */
                public void P.removeChild(C child) {
                   if (children.remove(child)) {
                     child.parent = null;
                   }
                }

               /**
                 * ensure bi-directional navigation on setting parent
                 */
                public void C.setParent(P parent) {
                   parent.addChild(this);
                }
                
                public pointcut addingChild(P p, C c) :
                  execution(* P.addChild(C)) && this(p) && args(c);
                  
                public pointcut removingChild(P p, C c) :
                  execution(* P.removeChild(C)) && this(p) && args(c);
            }
		

Note in the above example how the type parameters P and C can be used in inter-type declarations, pointcut expressions, and any other member of the aspect type.

The example aspect captures the protocol for managing a bi-directional parent-child relationship between any two types playing the role of parent and child. In a compiler implementation managing an abstract syntax tree (AST) in which AST nodes may contain other AST nodes we could declare the concrete aspect:

            public aspect ASTNodeContainment extends ParentChildRelationship<ASTNode,ASTNode> {
                
                before(ASTNode parent, ASTNode child) : addingChild(parent, child) {
                  ...
                }
                
            }
		

As a result of this declaration, ASTNode gains members:

List<ASTNode> children
ASTNode parent
List<ASTNode>getChildren()
ASTNode getParent()
void addChild(ASTNode child)
void removeChild(ASTNode child)
void setParent(ASTNode parent)

In a system managing files and folders, we could declare the concrete aspect:

            public aspect FilesInFolders extends ParentChildRelationship<Folder,File> {
                                
            }
		

As a result of this declaration, Folder gains members:

List<File> children
List<File> getChildren()
void addChild(File child)
void removeChild(File child)

and File gains members:

Folder parent
Folder getParent()
void setParent(Folder parent)

When used in this way, the type parameters in a generic abstract aspect declare roles, and the parameterization of the abstract aspect in the extends clause binds types to those roles. This is a case where you may consider departing from the standard practice of using a single letter to represent a type parameter, and instead use a role name. It makes no difference to the compiler which option you choose of course.

            public abstract aspect ParentChildRelationship<Parent,Child> {
                
                /**
                 * Parents contain a list of children
                 */
                private List<Child> Parent.children;
                    
                /**
                 * Each child has a parent
                 */
                private Parent Child.parent;
                
                /**
                  * Parents provide access to their children
                  */
                public List<Children> Parent.getChildren() {
                    return Collections.unmodifiableList(children);  
                }
                
                /**
                 * A child provides access to its parent
                 */
                 public Parent Children.getParent() {
                   return parent;
                 }
                                
                /**
                 * ensure bi-directional navigation on adding a child
                 */
                public void Parent.addChild(Child child) {
                   if (child.parent != null) {
                     child.parent.removeChild(child);
                   }
                   children.add(child);
                   child.parent = this;
                }
         
                ...
		

Chapter 4. Autoboxing and Unboxing

Autoboxing and Unboxing in Java 5

Java 5 (and hence AspectJ 1.5) supports automatic conversion of primitive types (int, float, double etc.) to their object equivalents (Integer, Float, Double,...) in assignments and method and constructor invocations. This conversion is know as autoboxing.

Java 5 also supports automatic unboxing, where wrapper types are automatically converted into their primitive equivalents if needed for assignments or method or constructor invocations.

For example:

		int i = 0;
		i = new Integer(5); // auto-unboxing
		
		Integer i2 = 5;  // autoboxing
		

Autoboxing and Join Point matching in AspectJ 5

Most of the pointcut designators match based on signatures, and hence are unaffected by autoboxing. For example, a call to a method

   		public void foo(Integer i);
   		

is not matched by a pointcut call(void foo(int)) since the signature declares a single Integer parameter, not an int.

The args pointcut designator is affected by autoboxing since it matches based on the runtime type of the arguments. AspectJ 5 applies autoboxing and unboxing in determining argument matching. In other words, args(Integer) will match any join point at which there is a single argument of type Integer or of type int.

  • args(Integer) and args(int) are equivalent
  • args(Float) and args(float) are equivalent
  • args(Double) and args(double) are equivalent
  • args(Short) and args(short) are equivalent
  • args(Byte) and args(byte) are equivalent
  • args(Long) and args(long) are equivalent
  • args(Boolean) and args(boolean) are equivalent

Autoboxing and unboxing are also applied when binding pointcut or advice parameters, for example:

   		pointcut foo(int i) : args(i);
   		
   		before(Integer i) : foo(i) {
   		  ...
   		}
   		

Inter-type method declarations and method dispatch

Autoboxing, unboxing, and also varargs all affect the method dispatch algorithm used in Java 5. In AspectJ 5, the target method of a call is selected according to the following algorithm:

  1. Attempt to locate a matching method or inter-type declared method without considering autoboxing, unboxing, or vararg invocations.
  2. If no match is found, try again considering autoboxing and unboxing.
  3. Finally try again considering both autoboxing, unboxing, and varargs.

One consequence is that a directly matching inter-type declared method will take precedence over a method declared locally in the target class but that only matches via autoboxing.

Chapter 5. Covariance

Covariance in Java 5

Java 5 (and hence AspectJ 5) allows you to narrow the return type in an overriding method. For example:

		class A {
		  public A whoAreYou() {...}
		}
		
		class B extends A {
		  // override A.whoAreYou *and* narrow the return type.
		  public B whoAreYou() {...}
		}
		

Covariant methods and Join Point matching

The join point matching rules for call and execution pointcut designators are extended to match against covariant methods.

Given the classes A and B as defined in the previous section, and the program fragment

		A a = new A();
		B b = new B();
		a.whoAreYou();
		b.whoAreYou();
		

The signatures for the call join point a.whoAreYou() are simply:

		A A.whoAreYou()
		

The signatures for the call join point b.whoAreYou() are:

		A A.whoAreYou()
		B B.whoAreYou()
		

Following the join point matching rules given in Join Point Signatures,

call(* whoAreYou())

Matches both calls, (since each call join point has at least one matching signature).

call(* A.whoAreYou())

Matches both calls, (since each call join point has at least one matching signature).

call(A whoAreYou())

Matches both calls, (since each call join point has at least one matching signature).

call(A B.whoAreYou())

Does not match anything - neither of the call join points has a signature matched by this pattern. A lint warning is given for the call a.whoAreYou() ("does not match because declaring type is A, if match required use target(B)").

call(A+ B.whoAreYou())

Matches the call to b.whoAreYou() since the signature pattern matches the signature B B.whoAreYou(). A lint warning is given for the call a.whoAreYou() ("does not match because declaring type is A, if match required use target(B)").

call(B A.whoAreYou())

Does not match anything since neither join point has a signature matched by this pattern.

call(B whoAreYou())

Matches the call to b.whoAreYou() only.

call(B B.whoAreYou())

Matches the call to b.whoAreYou() only.

The rule for signature matching at call and execution join points is unchanged from AspectJ 1.2: a call or execution pointcut matches if the signature pattern matches at least one of the signatures of the join point, and if the modifiers of the method or constructor are matched by any modifier pattern or annotation pattern that may be present.

Chapter 6. Varargs

Variable-length Argument Lists in Java 5

Java 5 (and hence AspectJ 5) allows you to specify methods that take a variable number of arguments of a specified type. This is achieved using an ellipsis (...) in the method signature as shown:

		public void foo(int i, String... strings) { 
		}
		

A method or constructor may take at most one variable length argument, and this must always be the last declared argument in the signature.

Calling Methods and Constructors with variable-length arguments

A varargs method may be called with zero or more arguments in the variable argument position. For example, given the definition of foo above, the following calls are all legal:

    	foo(5);
    	foo(5,"One String");
    	foo(7,"One String","Two Strings");
    	foo(3,"One String","Two Strings","Three Strings");	
    	

A varargs parameter is treated as an array within the defining member. So in the body of foo we could write for example:

    	public void foo(int i, String... strings) {
    	  String[] someStrings = strings;
    	  // rest of method body
    	}
    	

One consequence of this treatment of a varargs parameter as an array is that you can also call a varargs method with an array:

    	foo(7,new String[] {"One String","Two Strings"});
    	

Using Variable-length arguments in advice and pointcut expressions

AspectJ 5 allows variable-length arguments to be used for methods declared within aspects, and for inter-type declared methods and constructors, in accordance with the rules outlined in the previous section.

AspectJ 5 also allows variable length arguments to be matched by pointcut expressions and bound as formals in advice.

Matching signatures based on variable length argument types

Recall from the definition of signature patterns given in the chapter on annotations (Signature Patterns), that MethodPattern and ConstructorPattern are extended to allow a varargs pattern in the last argument position of a method or constructor signature.

  	
 		FormalsPattern := '..' (',' FormalsPatternAfterDotDot)? |
		                  OptionalParensTypePattern (',' FormalsPattern)* |
		                  TypePattern '...'
		                  
		FormalsPatternAfterDotDot := 
		        OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
		        TypePattern '...'

    	

Method and constructor patterns are used in the call, execution, initialization, preinitialization, and withincode pointcut designators. Some examples of usage follow:

call(* org.xyz.*.*(int, String...))

Matches a call join point for a call to a method defined in the org.xyz package, taking an int and a String vararg.

execution(* org.xyz.*.*(Integer...))

Matches an execution join point for the execution of a method defined in the org.xyz package, taking an Integer vararg.

initialization(org.xyz.*.new((Foo || Goo)...))

Matches the initialization join point for the construction of an object in the org.xyz package via a constructor taking either a variable number of Foo parameters or a variable number of Goo parameters. (This example illustrating the use of a type pattern with ...).

A variable argument parameter and an array parameter are treated as distinct signature elements, so given the method definitions:

    	void foo(String...);
    	void bar(String[]);
    	

The pointcut execution(* *.*(String...)) matches the execution join point for foo, but not bar. The pointcut execution(* *.*(String[])) matches the execution join point for bar but not foo.

Exposing variable-length arguments as context in pointcuts and advice

When a varargs parameter is used within the body of a method, it has an array type, as discussed in the introduction to this section. We follow the same convention when binding a varargs parameter via the args pointcut designator. Given a method

		public void foo(int i, String... strings) { 
		}
		

The call or execution join points for foo will be matched by the pointcut args(int,String[]). It is not permitted to use the varargs syntax within an args pointcut designator - so you cannot write args(int,String...).

Binding of a varargs parameter in an advice statement is straightforward:

		before(int i, String[] ss) : call(* foo(int,String...)) && args(i,ss) {
		  // varargs String... argument is accessible in advice body through ss
		  // ...
		}
		

Since you cannot use the varargs syntax in the args pointcut designator, you also cannot use the varargs syntax to declare advice parameters.

Note: the proposal in this section does not allow you to distinguish between a join point with a signature (int, String...) and a join point with a signature (int, String[]) based solely on the use of the args pointcut designator. If this distinction is required, args can always be coupled with call or execution.

Chapter 7. Enumerated Types

Enumerated Types in Java 5

Java 5 (and hence AspectJ 5) provides explicit support for enumerated types. In the simplest case, you can declare an enumerated type as follows:

      public enum ProgrammingLanguages {
       COBOL,C,JAVA,ASPECTJ
      }
      

Enumerated types are just classes, and they can contain method and field declarations, and may implement interfaces. Enums may only have private constructors, and may not be extended.

Enumerated types in Java 5 all implicitly extend the type java.lang.Enum. It is illegal to explicitly declare a subtype of this class.

Enumerated Types in AspectJ 5

AspectJ 5 supports the declaration of enumerated types just as Java 5 does. Because of the special restrictions Java 5 places around enumerated types, AspectJ makes the following additional restrictions:

  • You cannot use declare parents to change the super type of an enum.
  • You cannot use declare parents to declare java.lang.Enum as the parent of any type.
  • You cannot make inter-type constructor declarations on an enum.
  • You cannot extend the set of values in an enum via any ITD-like construct.
  • You cannot make inter-type method or field declarations on an enum.
  • You cannot use declare parents to make an enum type implement an interface.

In theory, the last of these two items could be supported. However, AspectJ 5 follows the simple rule that an enum type cannot be the target of an inter-type declaration or declare parents statement. This position may be relaxed in a future version of AspectJ.

If an enum is named explicitly as the target of a declare parents statement, a compilation error will result. If an enumerated type is matched by a non-explicit type pattern used in a declare parents statement it will be ignored (and an XLint warning issued).

Chapter 8. The pertypewithin Aspect Instantiation Model

AspectJ 5 defines a new per-clause type for aspect instantiation: pertypewithin. Unlike the other per-clauses, pertypewithin takes a type pattern:

  	PerTypeWithin := 'pertypewithin' '(' OptionalParensTypePattern ')'
	

When an aspect is declared using the pertypewithin instantiation model, one new aspect instance will be created for each type matched by the associated type pattern.

Pertypewithin aspects have aspectOf and hasAspect methods with the following signatures:

  	/**
  	 * return true if this aspect has an instance associated with
  	 * the given type.
  	 */
  	public static boolean hasAspect(Class clazz)
  	
  	/**
  	 * return the instance associated with the given type.
  	 * Throws NoAspectBoundException if there is no such
  	 * aspect.
  	 */
  	 public static P aspectOf(Class clazz)
	

Where P is the type of the pertypewithin aspect.

In common with the other per-clause instantiation models, the execution of any advice declared within a pertypewithin aspect is conditional upon an implicit pointcut condition. In this case, that any join point be within the type that the executing aspect is an aspectOf. For example, given the aspect definition

     import java.util.*;
     
     public aspect InstanceTracking pertypewithin(org.xyz..*) {
     
       // use WeakHashMap for auto-garbage collection of keys  	 
       private Map<Object,Boolean> instances = new WeakHashMap<Object,Boolean>();

       after(Object o) returning() : execution(new(..)) && this(o) {
         instances.put(o,true);
       }

       public Set<?> getInstances() {
         return instances.keySet();
       }
       
     } 
	

Then one aspect instance will be created for each type within org.xyz..*. For each aspect instance, the after returning advice will match only the execution of constructors in the type that the aspect is an instance of. The net result is that the aspect tracks all known instances of each type within org.xyz..*. To get access to the instances, a programmer can simply write InstanceTracking.aspectOf(org.xyz.SomeType.class).getInstances().

A pertypewithin aspect may optionally be declared with a single generic type parameter. In this case, for each type T matched by the type pattern, the aspect instance created will be of type PerTypeWithinAspect<T>. So the previous example could also be written as:

  	 public aspect InstanceTracking<T> pertypewithin(org.xyz..*) {
  	 
	   // use WeakHashMap for auto-garbage collection of keys  	 
  	   private Map<T, Boolean> instances = new WeakHashMap<T, Boolean>();
  	   
  	   after(T t) returning : execution(new(..)) && this(t) {
  	     instances.put(t, true);
  	   }
  	 
  	   public Set<T> getInstances() {
  	   	  return instances.keySet();
  	   }
  	   
  	 }
	

The pertypewithin aspect instantiation model should be used when the implementation of a crosscutting concern requires that some state be maintained for each type in a set of types. To maintain state for a single type, it is easier to use a static inter-type declared field. Examples of usage include instance tracking, profiling, and the implementation of a common tracing idiom that uses one Logger per traced class.

Chapter 9. An Annotation Based Development Style

Introduction

In addition to the familiar AspectJ code-based style of aspect declaration, AspectJ 5 also supports an annotation-based style of aspect declaration. We informally call the set of annotations that support this development style the "@AspectJ" annotations.

AspectJ 5 allows aspects and their members to be specified using either the code style or the annotation style. Whichever style you use, the AspectJ weaver ensures that your program has exactly the same semantics. It is, to quote a famous advertising campaign, "a choice, not a compromise". The two styles can be mixed within a single application, and even within a single source file, though we doubt this latter mix will be recommended in practice.

The use of the @AspectJ annotations means that there are large classes of AspectJ applications that can be compiled by a regular Java 5 compiler, and subsequently woven by the AspectJ weaver (for example, as an additional build stage, or as late as class load-time). In this chapter we introduce the @AspectJ annotations and show how they can be used to declare aspects and aspect members.

Aspect Declarations

Aspect declarations are supported by the org.aspectj.lang.annotation.Aspect annotation. The declaration:

     @Aspect
     public class Foo {}
         

Is equivalent to:

     public aspect Foo {}
         

And since issingleton() is the default aspect instantiation model it is equivalent to:

      @Aspect("issingleton()")
      public class Foo {}
         

Privileged aspects are not supported by the annotation style

To specify an aspect an aspect instantiation model (the default is singleton), provide the perclause as the @Aspect value. For example:

     @Aspect("perthis(execution(* abc..*(..)))")
     public class Foo {}

     is equivalent to...

     public aspect Foo perthis(execution(* abc..*(..))) {}
         

Pointcuts and Advice

Pointcut and advice declarations can be made using the Pointcut, Before, After, AfterReturning, AfterThrowing, and Around annotations.

Pointcuts

Pointcuts are specified using the org.aspectj.lang.annotation.Pointcut annotation on a method declaration. The method should have a void return type. The parameters of the method correspond to the parameters of the pointcut. The modifiers of the method correspond to the modifiers of the pointcut.

As a general rule, the @Pointcut annotated method must have an empty method body and must not have any throws clause. If formal are bound (using args(), target(), this(), @args(), @target(), @this(), @annotation()) in the pointcut, then they must appear in the method signature.

There is one special case to the general rule for when you use if() pointcut as detailled in the next section.

A simple example:

     @Pointcut("call(* *.*(..))")
     void anyCall() {}

     is equivalent to...

     pointcut anyCall() : call(* *.*(..));
             

An example with formal bindings:

     @Pointcut("call(* *.*(int)) && args(i) && target(callee)")
     void someCall(int i, Foo callee) {}

     is equivalent to...

     pointcut anyCall(int i, Foo callee) : call(* *.*(int)) && args(i) && target(callee);
             

An example with modifiers (it is also good to remember that Java 5 annotations are not inherited):

     @Pointcut("")
     protected abstract void anyCall();

     is equivalent to...

     protected abstract pointcut anyCall();
             

Using the code style, types referenced in pointcut expressions are resolved with respect to the imported types in the compilation unit. When using the annotation style, types referenced in pointcut expressions are resolved in the absence of any imports and so have to be fully qualified if they are not by default visible to the declaring type (outside of the declaring package and java.lang). This to not apply to type patterns with wildcards, which are always resolved in a global scope.

Consider the following compilation unit:

     package org.aspectprogrammer.examples;

     import java.util.List;

     public aspect Foo {

       pointcut listOperation() : call(* List.*(..));

       pointcut anyUtilityCall() : call(* java.util..*(..));

     }
             

Using the annotation style this would be written as:

     package org.aspectprogrammer.examples;

     import java.util.List; // redundant but harmless

     @Aspect
     public class Foo {

       @Pointcut("call(* java.util.List.*(..))") // must qualify
       void listOperation() {}

       @Pointcut("call(* java.util..*(..))")
       void anyUtilityCall() {}

     }
             

The value attribute of the Pointcut declaration may contain any valid AspectJ pointcut declaration - though if() pointcut is a special case explained below.

The special case for the if() pointcut.

In code style, it is possible to use the if(...) poincut to implement conditional pointcut whose residual if form will be evaluated at runtime. The if(...) body can be any valid Java boolean expression, and can use any exposed formal, as well as the join point forms thisJoinPoint, thisJoinPointStaticPart and thisJoinPointEnclosingStaticPart.

When using the annotation style, it would be really a pain to write a valid Java expression within the annotation value so the syntax differs sligthly, whilst providing the very same semantics and runtime behaviour. Take the following examples:

     @Pointcut("call(* *.*(int)) && args(i) && if()")
     public static boolean someCallWithIfTest(int i) {
        return i > 0;
     }

     is equivalent to...

     pointcut someCallWithIfTest(int i) : call(* *.*(int)) && args(i) && if(i > 0);
             

and the following is also a valid form:

     static int COUNT = 0;

     @Pointcut("call(* *.*(int)) && args(i) && if()")
     public static boolean someCallWithIfTest(int i, JoinPoint jp, JoinPoint.EnclosingStaticPart esjp) {
        // can call any kind of method (though this method is a static one)
        return i > 0
               && jp.getSignature().getName.startsWith("doo")
               && esjp.getSignature().getName().startsWith("test")
               && COUNT++ < 10;
     }

     @Before("someCallWithIfTest(arg0, jp, enc)") // see how the pointcut is referenced: we obey its exact signature
     public void beforeAdviceWithRuntimeTest(int arg0, JoinPoint jp, JoinPoint.EnclosingStaticPart enc) {
        //...
     }

     // Note that the following is NOT valid
     /*
     @Before("call(* *.*(int)) && args(i) && if()")
     public void advice(int i) {
        // so you were writing an advice or an if body ?
     }
     */
             

It is thus possible with the annotation style to use the if() pointcut only within an @Pointcut expression. The if() must not contain any body. The so annotated @Pointcut method must then be of the form public static boolean and can use formal bindings as usual. Extra implicit (thus unbound) arguments of type JoinPoint, JoinPoint.StaticPart and JoinPoint.EnclosingStaticPart can also be used (they can't for regular pointcut without if() form).

The special forms if(true) and if(false) can be used in a more general way and don't imply that the pointcut method must have a body. You can thus write @Before("somePoincut() && if(false)").

Advice

In this section we first discuss the use of annotations for simple advice declarations. Then we show how thisJoinPoint and its siblings are handled in the body of advice and discuss the treatment of proceed in around advice.

Using the annotation style, an advice declaration is written as a regular Java method with one of the Before, After, AfterReturning, AfterThrowing, or Around annotations. Except in the case of around advice, the method should return void. The method should be declared public.

A method that has an advice annotation is treated exactly as an advice declaration by AspectJ's weaver. This includes the join points that arise when the advice is executed (an adviceexecution join point, not a method execution join point), and the restriction that advice cannot be invoked explicitly (the weaver will issue an error if an advice method is explicitly invoked).

The following example shows a simple before advice declaration in both styles:

     before() : call(* org.aspectprogrammer..*(..)) && this(Foo) {
       System.out.println("Call from Foo");
     }

     is equivalent to...

     @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
     public void callFromFoo() {
       System.out.println("Call from Foo");
     }
             

Notice one slight difference between the two advice declarations: in the annotation style, the advice has a name, "callFromFoo". Even though advice cannot be invoked explicitly, this name is useful in join point matching when advising advice execution. For this reason, and to preserve exact semantic equivalence between the two styles, we also support the org.aspectj.lang.annotation.AdviceName annotation. The exact equivalent declarations are:

     @AdviceName("callFromFoo")
     before() : call(* org.aspectprogrammer..*(..)) && this(Foo) {
       System.out.println("Call from Foo");
     }

     is equivalent to...

     @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
     public void callFromFoo() {
       System.out.println("Call from Foo");
     }
             

If the advice body needs to know which particular Foo was doing the calling, just add a parameter to the advice declaration.

     @AdviceName("callFromFoo")
     before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) {
       System.out.println("Call from Foo: " + foo);
     }

     is equivalent to...

     @Before("call(* org.aspectprogrammer..*(..)) && this(foo)")
     public void callFromFoo(Foo foo) {
       System.out.println("Call from Foo: " + foo);
     }
             

If the advice body needs access to thisJoinPoint, thisJoinPointStaticPart, thisEnclosingJoinPointStaticPart then these need to be declared as additional method parameters when using the annotation style.

     @AdviceName("callFromFoo")
     before(Foo foo) : call(* org.aspectprogrammer..*(..)) && this(foo) {
       System.out.println("Call from Foo: " + foo + " at "
                          + thisJoinPoint);
     }

     is equivalent to...

     @Before("call(* org.aspectprogrammer..*(..)) && this(foo)")
     public void callFromFoo(JoinPoint thisJoinPoint, Foo foo) {
       System.out.println("Call from Foo: " + foo + " at "
                          + thisJoinPoint);
     }
             

Advice that needs all three variables would be declared:

     @Before("call(* org.aspectprogrammer..*(..)) && this(Foo)")
     public void callFromFoo(JoinPoint thisJoinPoint,
                             JoinPoint.StaticPart thisJoinPointStaticPart,
                             JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart) {
         // ...
     }
             

JoinPoint.EnclosingStaticPart is a new (empty) sub-interface of JoinPoint.StaticPart which allows the AspectJ weaver to distinguish based on type which of thisJoinPointStaticPart and thisEnclosingJoinPointStaticPart should be passed in a given parameter position.

After advice declarations take exactly the same form as Before, as do the forms of AfterReturning and AfterThrowing that do not expose the return type or thrown exception respectively.

To expose a return value with after returning advice simply declare the returning parameter as a parameter in the method body and bind it with the "returning" attribute:

      after() returning : criticalOperation() {
        System.out.println("phew");
      }

      after() returning(Foo f) : call(Foo+.new(..)) {
        System.out.println("It's a Foo: " + f);
      }

      can be written as...

      @AfterReturning("criticalOperation()")
      public void phew() {
        System.out.println("phew");
      }

      @AfterReturning(pointcut="call(Foo+.new(..))",returning="f")
      public void itsAFoo(Foo f) {
        System.out.println("It's a Foo: " + f);
      }
            

(Note the use of the "pointcut=" prefix in front of the pointcut expression in the returning case).

After throwing advice works in a similar fashion, using the throwing attribute when needing to expose a thrown exception.

For around advice, we have to tackle the problem of proceed. One of the design goals for the annotation style is that a large class of AspectJ applications should be compilable with a standard Java 5 compiler. A straight call to proceed inside a method body:

     @Around("call(* org.aspectprogrammer..*(..))")
     public Object doNothing() {
       return proceed(); // CE on this line
     }
             

will result in a "No such method" compilation error. For this reason AspectJ 5 defines a new sub-interface of JoinPoint, ProceedingJoinPoint.

     public interface ProceedingJoinPoint extends JoinPoint {
       public Object proceed(Object[] args);
     }
             

The around advice given above can now be written as:

     @Around("call(* org.aspectprogrammer..*(..))")
     public Object doNothing(ProceedingJoinPoint thisJoinPoint) {
       return thisJoinPoint.proceed();
     }
             

Here's an example that uses parameters for the proceed call:

     public aspect ProceedAspect {
       pointcut setAge(int i): call(* setAge(..)) && args(i);

       Object around(int i): setAge(i) {
         return proceed(i*2);
       }
     }

     can be written as...

     @Aspect
     public class ProceedAspect {

       @Pointcut("call(* setAge(..)) && args(i)")
       void setAge(int i) {}

       @Around("setAge(i)")
       public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, int i) {
         return thisJoinPoint.proceed(new Object[]{i*2}); //using Java 5 autoboxing
       }

     }

     Note that the ProceedingJoinPoint does not need to be passed as the proceed(..) arguments.
             

Inter-type Declarations

Inter-type declarations are challenging to support using an annotation style. It's very important to preserve the exact same semantics between the code style and the annotation style. We also want to support compilation of a large set of AspectJ applications using a standard Java 5 compiler. For these reasons, in the initial release of AspectJ 5 we will only support inter-type declarations on interfaces using the annotation style.

Consider the following aspect:

     public aspect MoodIndicator {

        public interface Moody {};

        private Mood Moody.mood = Mood.HAPPY;

        public Mood Moody.getMood() {
          return mood;
        }

        declare parents : org.xyz..* implements Moody;

        before(Moody m) : execution(* *.*(..)) && this(m) {
           System.out.println("I'm feeling " + m.getMood());
        }
     }
         

This declares an interface Moody, and then makes two inter-type declarations on the interface - a field that is private to the aspect, and a method that returns the mood. Within the body of the inter-type declared method getMoody, the type of this is Moody (the target type of the inter-type declaration).

Using the annotation style this aspect can be written:

     @Aspect
     public class MoodIndicator {

        public interface Moody {
          Mood getMood();
        };

        @DeclareParents("org.xzy..*")
        class MoodyImpl implements Moody {
           private Mood mood = Mood.HAPPY;

           public Mood getMood() {
             return mood;
           }
        }

        @Before("execution(* *.*(..)) && this(m)")
        void feelingMoody(Moody m) {
           System.out.println("I'm feeling " + m.getMood());
        }
     }
         

This is very similar to the mixin mechanism supported by AspectWerkz. The effect of the @DeclareParents annotation is equivalent to a declare parents statement that all types matching the type pattern implement the interface implemented by the annotated class. In addition, the member declarations within the annotated class are treated as inter-type declarations on the implemented interface. Note how this scheme operates within the constraints of Java type checking and ensures that this has access to the exact same set of members as in the code style example.

The annotated class may only extend Object, and may only implement a single interface. The interface implemented by the class may itself extend other interfaces.

Declare statements

The previous section on inter-type declarations covered the case of declare parents ... implements. The 1.5.0 release of AspectJ 5 will not support annotation style declarations for declare parents ... extends and declare soft (programs with these declarations would not in general be compilable by a regular Java 5 compiler, reducing the priority of their implementation). These may be supported in a future release.

Declare precedence and declare annotation will be supported. For declare precedence, use the @DeclarePrecedence annotation as in the following example:

     public aspect SystemArchitecture {
       declare precedence : Security*, TransactionSupport, Persistence;

       // ...
     }

     can be written as:

     @Aspect
     @DeclarePrecedence("Security*,org.xyz.TransactionSupport,org.xyz.Persistence")
     public class SystemArchitecture {

       // ...
     }
         

Declare annotation is supported via annotations on a dummy type member. If the Target specification of the annotation allows it, use a field, otherwise declare a member of the type required by the Target. For example:

     public aspect DeclareAnnotationExamples {
       declare annotation : org.xyz.model..* : @BusinessDomain;

       declare annotation : public * BankAccount+.*(..) : @Secured(role="supervisor");

       declare anotation : * DAO+.* : @Persisted;

     }

     can be written as...

     @Aspect
     public class DeclareAnnotationExamples {

       @DeclareAnnotation("org.xyz.model..*)
       @BusinessDomain Object modelClass;

       // this example assumes that the @Secured annotation has a Target
       // annotation with value ElementType.METHOD
       @DeclareAnnotation("public * org.xyz.banking.BankAccount+.*(..)")
       @Secured(role="supervisor) void bankAccountMethod();

       @DeclareAnnotation("* DAO+.*")
       @Persisted Object daoFields;
     }
         

We also support annotation style declarations for declare warning and declare error - any corresponding warnings and errors will be emitted at weave time, not when the aspects containing the declarations are compiled. (This is the same behaviour as when using declare warning or error with the code style). Declare warning and error declarations are made by annotating a string constant whose value is the message to be issued.

Note that the String must be a constant and not the result of the invocation of a static method for example.

       declare warning : call(* javax.sql..*(..)) && !within(org.xyz.daos..*)
                       : "Only DAOs should be calling JDBC.";

       declare error : execution(* IFoo+.*(..)) && !within(org.foo..*)
                     : "Only foo types can implement IFoo";

       can be written as...

       @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
       static final String aMessage = "Only DAOs should be calling JDBC.";

       @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
       static final String badIFooImplementors = "Only foo types can implement IFoo";

       // the following is not valid since the message is not a String constant
       @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
       static final String badIFooImplementorsCorrupted = getMessage();
       static String getMessage() {
           return "Only foo types can implement IFoo " + System.currentTimeMillis();
       }

         

aspectOf() and hasAspect() methods

A central part of AspectJ's programming model is that aspects written using the code style and compiled using ajc support aspectOf and hasAspect static methods. When developing an aspect using the annotation style and compiling using a regular Java 5 compiler, these methods will not be visible to the compiler and will result in a compilation error if another part of the program tries to call them.

To provide equivalent support for AspectJ applications compiled with a standard Java 5 compiler, AspectJ 5 defines the Aspects utility class:

      public class Aspects {

        /* variation used for singleton, percflow, percflowbelow */
        static<T> public static T aspectOf(T aspectType) {...}

        /* variation used for perthis, pertarget */
        static<T> public static T aspectOf(T aspectType, Object forObject) {...}

        /* variation used for pertypewithin */
        static<T> public static T aspectOf(T aspectType, Class forType) {...}

        /* variation used for singleton, percflow, percflowbelow */
        public static boolean hasAspect(Object anAspect) {...}

        /* variation used for perthis, pertarget */
        public static boolean hasAspect(Object anAspect, Object forObject) {...}

        /* variation used for pertypewithin */
        public static boolean hasAspect(Object anAspect, Class forType) {...}
      }
        

Chapter 10. New Reflection Interfaces

Table of Contents

The Aspect Class

AspectJ 5 provides support for runtime reflection of aspect types. The class Aspect is analogous to the Java class Class and provides access to the members of an aspect.

The Aspect Class

Chapter 11. Other Changes in AspectJ 5

Pointcuts

Binding of formals

AspectJ 5 is more liberal than AspectJ 1.2.1 in accepting pointcut expressions that bind context variables in more than one location. For example, AspectJ 1.2.1 does not allow:

       	pointcut foo(Foo foo) : (execution(* *(..)) && this(foo) ) ||
       	                                (set(* *) && target(foo));
		

whereas this expression is permitted in AspectJ 5. Each context variable must be bound exactly once in each branch of a disjunction, and the disjunctive branches must be mutually exclusive. In the above example for instance, no join point can be both an execution join point and a set join point so the two branches are mutually exclusive.

Additional lint warnings

Discuss detection of common errors -> warning/error, eg. conjunction of more than one kind of join point. Differing numbers of args in method signature / args / @args / @parameters.

Declare Soft

The semantics of the declare soft statement have been refined in AspectJ 5 to only soften exceptions that are not already runtime exceptions. If the exception type specified in a declare soft statement is RuntimeException or a subtype of RuntimeException then a new XLint warning will be issued:

       	  declare soft : SomeRuntimeException : execution(* *(..));
       	  
       	  &gt;&gt; "SomeRuntimeException will not be softened as it is already a RuntimeException" [XLint:runtimeExceptionNotSoftened]
		

This XLint message can be controlled by setting the runtimeExceptionNotSoftened XLint parameter.

If the exception type specified in a declare soft statement is a super type of RuntimeException (such as Exception for example) then any <i>checked</i> exception thrown at a matched join point, where the exception is an instance of the softened exception, will be softened to an org.aspectj.lang.SoftException.

       	public aspect SoftenExample {
       	
       	  declare soft : Exception : execution(* Foo.*(..));
       	
       	}
       	
       	class Foo {
       	
       	  public static void main(String[] args) {
       	    Foo foo = new Foo();
       	    foo.foo();   
       	    foo.bar();
       	  }
       	
       	  void foo() throws Exception {
       	    throw new Exception();        // this will be converted to a SoftException
       	  }
       	  
       	  void bar() throws Exception {
       	    throw new RuntimeException();  // this will remain a RuntimeException
       	  }
       	
       	}
		

Tools

Aspectpath

AspectJ 5 allows the specification of directories (containing .class files) on the aspectpath in addition to jar/zip files.

Chapter 12. Load-Time Weaving

Introduction

The AspectJ 5 weaver takes class files as input and produces class files as output. The weaving process itself can take place at one of three different times: compile-time, post-compile time, and load-time. The class files produced by the weaving process (and hence the run-time behaviour of an application) are the same regardless of the approach chosen.

  • Compile-time weaving is the simplest approach. When you have the source code for an application, ajc will compile from source and produce woven class files as output. The invocation of the weaver is integral to the ajc compilation process. The aspects themselves may be in source or binary form.
  • Post-compile weaving (also sometimes called binary weaving) is used to weave existing class files and JAR files. As with compile-time weaving, the aspects used for weaving may be in source or binary form.
  • Load-time weaving (LTW) is simply binary weaving defered until the point that a class loader loads a class file and defines the class to the JVM. To support this, one or more "weaving class loaders", either provided explicitly by the run-time environment or enabled through a "weaving agent" are required.

You may also hear the term "run-time weaving". We define this as the weaving of classes that have already been defined to the JVM (without reloading those classes). AspectJ 5 does not provide explicit support for run-time weaving although simple coding patterns can support dynamically enabling and disabling advice in aspects.

Weaving class files more than once

By default a class file that has been woven by the AspectJ compiler cannot subsequently be rewoven (passed as input to the weaver). If you are developing AspectJ applications that are to be used in a load-time weaving environment, you need to specify the -Xreweavable compiler option when building them. This causes AspectJ to save additional state in the class files that is used to support subsequent reweaving.

As per AspectJ 1.5.0 M3 aspects (code style or annotation style) are reweavable by default, and weaved classes may be as well in 1.5.0 final.

Load-time Weaving Requirements

All load-time weaving is done in the context of a class loader, and hence the set of aspects used for weaving and the types that can be woven are affected by the class loader delegation model. This ensures that LTW complies with the Java 2 security model. The following rules govern the interaction of load-time weaving with class loading:

  1. All aspects to be used for weaving must be defined to the weaver before any types to be woven are loaded.
  2. All abstract and concrete aspects visible to the weaver are available for extending (abstract aspects) and using for weaving. A visible aspect is one defined by the weaving class loader or one of its parent class loaders.
  3. A class loader may only weave classes that it defines. It may not weave classes loaded by a delegate or parent class loader.

Configuration

AspectJ 5 supports a number of mechanisms designed to make load-time weaving as easy to use as possibe. The load-time weaving mechanism is chosen through JVM startup options. Configuration files determine the set of aspects to be used for weaving and which types will be woven. Additional diagnostic options allow the user to debug the configuration and weaving process.

Enabling Load-time Weaving

AspectJ 5 supports three different ways of enabling load-time weaving for an application: agents, a command-line launch script, and a set of interfaces for integration of AspectJ load-time weaving in custom environments.

Agents

AspectJ 5 ships with a number of load-time weaving agents that enable load-time weaving. These agents and their configuration are execution environment dependent. Using Java 5 for example, you can specify the "-javaagent" option to the JVM. Configuration for the supported environments is discussed later in this chapter. AspectJ 5 has several agents including those that use JVMTI, and the JRockit MAPI.

Command line

AspectJ includes a script "aj" that allows programs executed at the command line to take advantage of load-time weaving. The script is customized when AspectJ is installed depending on the chosen JDK. For example, for JDK 1.4 the script uses the -Djava.system.class.loader system property to replace the system class loader with a weaving class loader allowing classes loaded from the CLASSPATH to be woven. For JDK 1.5 the JVMTI weaving agent is used allowing classes loaded by all class loaders to be woven. Versions of the JDK prior to 1.3 are not supported by the "aj" mechanism.

Custom Integration

A public interface is provided to allow a user written class loader to instantiate a weaver and weave classes after loading and before defining them in the JVM. This enables load-time weaving to be supported in environments where no weaving agent is available. It also allows the user to explicity restrict by class loader which classes can be woven.

Configuring Load-time Weaving with aop.xml files

The weaver is configured using one or more META-INF/aop.xml files located on the class loader search path. Each file may define a list of concrete aspects to be used for weaving, type patterns describing which types should woven, and a set of options to be passed to the weaver. In addition AspectJ 5 supports the definition of concrete aspects in XML. Aspects defined in this way must extend an abstract aspect visible to the weaver. The abstract aspect may define abstract pointcuts (but not abstract methods). The following example shows a simple aop.xml file:

          <aspectj>
          
            <aspects>
              <!-- declare two existing aspects to the weaver -->
              <aspect name="com.MyAspect"/>
              <aspect name="com.MyAspect.Inner"/>

              <!-- define a concrete aspect inline -->
              <concrete-aspect name="com.xyz.tracing.MyTracing" extends="tracing.AbstractTracing">
                <pointcut name="tracingScope" expression="within(org.maw.*)"/>
              </concrete-aspect>
		  	  
              <!-- Of the set of aspects known to the weaver, use aspects matching
                   the type pattern "com..*" for weaving. -->
              <include within="com..*"/>
			  
              <!-- Do not use any aspects with the @CoolAspect annotation for weaving -->
              <exclude within="@CoolAspect *"/>    	
			  
            </aspects>

            <weaver options="-verbose -XlazyTjp">
              <!-- Weave types that are within the javax.* or org.aspectj.*
                   packages. Also weave all types in the foo package that do
                   not have the @NoWeave annotation. -->
              <include within="javax.*"/>
              <include within="org.aspectj.*"/>
              <include within="(!@NoWeave foo.*) AND foo.*"/>
            </weaver>
          
          </aspectj>
          
		  

An aop.xml file contains two key sections: "aspects" defines one or more aspects to the weaver and controls which aspects are to be used in the weaving process; "weaver" defines weaver options and which types should be woven.

The simplest way to define an aspect to the weaver is to specify the fully-qualified name of the aspect type in an aspect element. You can also declare (and define to the weaver) aspects inline in the aop.xml file. This is done using the "concrete-aspect" element. A concrete-aspect declaration must provide a pointcut definition for every abstract pointcut in the abstract aspect it extends. This mechanism is a useful way of externalizing configuration for infrastructure and auxiliary aspects where the pointcut definitions themselves can be considered part of the configuration of the service.

The aspects element may optionally contain one or more include and exclude elements (by default, all defined aspects are used for weaving). Specifying include or exclude elements restricts the set of defined aspects to be used for weaving to those that are matched by an include pattern, but not by an exclude pattern. The 'within' attribute accepts a type pattern of the same form as a within pcd, except that && and || are replaced by 'AND' and 'OR'.

The weaver element is used to pass options to the weaver and to specify the set of types that should be woven. If no include elements are specified then all types seen by the weaver will be woven.

When several configuration files are visible from a given weaving class loader their contents are conceptually merged (this applies to both aop.xml files and to aop.properties files as described in the next section). The files are merged in the order they are found on the search path (regular getResourceAsStream lookup) according to the following rules:

  • The set of available aspects is the set of all declared and defined aspects (aspect and concrete-aspect elements of the aspects section).
  • The set of aspects used for weaving is the subset of the available aspects that are matched by at least one include statement and are not matched by any exclude statements. If there are no include statements then all non-excluded aspects are included.
  • The set of types to be woven are those types matched by at least one weaver include element and not matched by any weaver exclude element. If there are no weaver include statements then all non-excluded types are included.
  • The weaver options are derived by taking the union of the options specified in each of the weaver options attribute specifications. Where an option takes a value e.g. -warn:none the most recently defined value will be used.

It is not an error for the same aspect to be defined to the weaver in more than one visible META-INF/aop.xml file. However, if a declarative concrete aspect is declared in more than aop.xml file then an error will be issued. A concrete aspect defined in this way will be used to weave types loaded by the class loader that loaded the aop.xml file in which it was defined.

A META-INF/aop.xml file will automatically be generated when using the -outjar option of the AspectJ compiler. It will simply contain a (possibly empty) set of aspect elements, one for each concrete aspect included in the JAR.

Configuring Load-time Weaving with Properties Files

For memory constrained environments or those without support for XML a simple Java Properties file can be used to configure LTW. Just like XML files, META-INF/aop.properties files are loaded from the class loader search path. Everything that can be configured through XML can be configured using a Properties file, with the exception of declarative concrete aspects. For example:

          aspects.names=com.MyAspect,com.MyAspect.Inner
          aspects.include=com..*
          aspects.exclude=@CoolAspect

          weaver.options=-verbose -XlazyTjp
          weaver.include=javax.* OR org.aspectj.*
		  

Weaver Options

The table below lists the AspectJ options supported by LTW. All other options will be ignored and a warning issued.

OptionPurpose
-1.5Run the weaver in 1.5 mode (supports autoboxing in join point matching)
-XlazyTjpPerformance optimization for aspects making use of thisJoinPoint (non-static parts)
-Xlint, -Xlint:ignore, ...Configure lint messages
-nowarn, -warn:noneSuppress warning messages
-proceedOnErrorContinue weaving even if errors occur (for example, "... already woven" errors)
-verboseIssue informational messages about the weaving process
-XreweavableProduce class files that can subsequently be rewoven
-XnoInlineDon't inline around advice.
-showWeaveInfoIssue informational messages whenever the weaver touches a class file
-XmessageHolderClass:...Provide alternative output destination to stdout/stderr for all weaver messages. The given value must be the full qualified class name of a class that implements org.aspectj.weaver.loadtime and that is visible from where the aop.xml is packed. If more than one such options are used, the first occurence only is taken into account.

Runtime Requirements for Load-time Weaving

To use LTW the aspectjweaver.jar library must be added to the classpath. This contains the AspectJ 5 runtime, weaver, weaving class loader and weaving agents. It also contains the DTD for parsing XML weaving configuration files.

Supported Agents

JVMTI

When using JDK 1.5 the JVMTI agent can be used by starting the JVM with the following option (adapt according to the path to aspectjweaver.jar):

			-javaagent=aspectjweaver.jar          
		  

JRockit

The JRockit agent is configured with the following JVM option:

			-Xmanagement:class=org.aspectj.weaver.tools.JRockitWeavingAgent
		  

Appendix A. A Grammar for the AspectJ 5 Language

        === type patterns ===
      
        TypePattern := SimpleTypePattern |
  	                   '!' TypePattern |
  	                   '(' AnnotationPattern? TypePattern ')'
  	                   TypePattern '&&' TypePattern |
  	                   TypePattern '||' TypePattern
  	  	
        SimpleTypePattern := DottedNamePattern '+'? '[]'*
  	  	
        DottedNamePattern := FullyQualifiedName RestOfNamePattern? |
  		                     '*' NotStarNamePattern?
  		
        RestOfNamePattern := '..' DottedNamePattern |
  		                     '*' NotStarNamePattern?
  		                     
        NotStarNamePattern := FullyQualifiedName RestOfNamePattern? |
  		                      '..' DottedNamePattern               

        FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*  				  		  		  		               									 				  		             
 
        === annotation patterns ===
 
        AnnotationPattern := '!'? '@' AnnotationTypePattern AnnotationPattern* 

        
        AnnotationTypePattern := FullyQualifiedName |
                                 '(' TypePattern ')'
 
        === signature patterns ===
        
        -- field --
        
        FieldPattern := 
  		    AnnotationPattern? FieldModifiersPattern? 
  		    TypePattern (TypePattern DotOrDotDot)? SimpleNamePattern

        FieldModifiersPattern := '!'? FieldModifier FieldModifiersPattern*
		                         		
        FieldModifier := 'public' | 'private' | 'protected' | 'static' | 
		                 'transient' | 'final' 
		            			
        DotOrDotDot := '.' | '..'
		
        SimpleNamePattern := JavaIdentifierChar+ ('*' SimpleNamePattern)?		            
        
        -- method --
        
        MethodPattern := 
  		    AnnotationPattern? MethodModifiersPattern? TypePattern 
  		                       (TypePattern DotOrDotDot)? SimpleNamePattern 
  		                       '(' FormalsPattern ')' ThrowsPattern?

        MethodModifiersPattern := '!'? MethodModifier MethodModifiersPattern*
		
        MethodModifier := 'public' | 'private' | 'protected' | 'static' | 
		                  'synchronized' | 'final' 
		            		      
        FormalsPattern := '..' (',' FormalsPatternAfterDotDot)? |
		                  OptionalParensTypePattern (',' FormalsPattern)* |
		                  TypePattern '...'
		                  
        FormalsPatternAfterDotDot := 
		        OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
		        TypePattern '...'
		                  
        ThrowsPattern := 'throws' TypePatternList
		
        TypePatternList := TypePattern (',' TypePattern)*
		
        -- constructor --
		            					            
        ConstructorPattern := 
  		    AnnotationPattern? ConstructorModifiersPattern?  
  		                       (TypePattern DotOrDotDot)? 'new' '(' FormalsPattern ')'
  		                       ThrowsPattern?
	
        ConstructorModifiersPattern := '!'? ConstructorModifier ConstructorModifiersPattern*
		
        ConstructorModifier := 'public' | 'private' | 'protected'
        
        === Pointcuts ===
        
        PointcutPrimitive := 
                    Call | Execution | Get | Set | Handler | 
                    Initialization | PreInitialization |
                    StaticInitialization | AdviceExecution |
                    This | Target | Args | CFlow | CFlowBelow |
                    Within | WithinCode | If |
                    AnnotationPointcut
                    
        AnnotationPointcut := AtAnnotation | AtThis | AtTarget |
                              AtWithin | AtWithinCode | AtArgs
                    
        
        Call := 'call' '(' MethodOrConstructorPattern ')'
        
        MethodOrConstructorPattern := MethodPattern | ConstructorPattern
        
        Execution := 'execution' '(' MethodOrConstructorPattern ')'
        
        Get := 'get' '(' FieldPattern ')'        
        Set := 'set' '(' FieldPattern ')'
        Handler := 'handler' '(' OptionalParensTypePattern ')'
        Initialization := 'initialization' '(' ConstructorPattern ')'
        PreInitialization := 'preinitialization' '(' ConstructorPattern ')'
        StaticInitialization := 'staticinitialization' '(' OptionalParensTypePattern ')'
        AdviceExecution := 'adviceexecution' '(' ')'
        This := 'this' '(' TypeOrIdentifier ')'
        Target := 'target' '(' TypeOrIdentifier ')'
        Args := 'args' '(' FormalsOrIdentifiersPattern ')'
        CFlow := 'cflow' '(' Pointcut ')'
        CFlowBelow := 'cflowbelow' '(' Pointcut ')'
        Within := 'within' '(' OptionalParensTypePattern ')'
        WithinCode := 'withincode' '(' OptionalParensTypePattern ')'
        If := 'if' '(' BooleanJavaExpression ')'
        
        TypeOrIdentifier := FullyQualifiedName ('[' ']')* | Identifier
        Identifier := JavaIdentifierChar+
        
        FormalsOrIdentifiersPattern :=
                          '..' (',' FormalsOrIdentifiersPatternAfterDotDot)? |
		                  TypeOrIdentifier (',' FormalsOrIdentifiersPattern)* |
		                  '*' (',' FormalsOrIdentifiersPattern)* 
		                  
        FormalsOrIdentifiersPatternAfterDotDot := 
		                  TypeOrIdentifier (',' FormalsOrIdentifiersPatternAfterDotDot)* |
		                  '*' (',' FormalsOrIdentifiersPatternAfterDotDot)*
        
        AtAnnotation := '@annotation' '(' AnnotationOrIdentifier ')'
        AtThis := '@this' '(' AnnotationOrIdentifer ')'
        AtTarget := '@target' '(' AnnotationOrIdentifier ')'
        AtWithin := '@within' '(' AnnotationOrIdentifier ')'
        AtWithinCode := '@withincode' '(' AnnotationOrIdentifier ')'        
        
        AnnotationOrIdentifier := FullyQualifiedName | Identifier
        
        AtArgs := '@args' '(' AnnotationsOrIdentifiersPattern ')'
        
        AnnotationsOrIdentifiersPattern :=
                          '..' (',' AnnotationsOrIdentifiersPatternAfterDotDot)? |
                          AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPattern)* |
                          '*' (',' AnnotationsOrIdentifiersPattern)*
		                  
        AnnotationsOrIdentifiersPatternAfterDotDot := 
		                  AnnotationOrIdentifier (',' AnnotationsOrIdentifiersPatternAfterDotDot)* |
		                  '*' (',' AnnotationsOrIdentifiersPatternAfterDotDot)*
        
        PointcutDeclaration := PointcutModifiers? 'pointcut' Identifier Formals
                               ':' PointcutExpression
                               
        PointcutModifiers := PointcutModifier*
        
        PointcutModifier :=  'public' | 'private' | 'protected' | 'abstract'
        
        Formals := '(' ParamList? ')'        
        ParamList := FullyQualifiedName Identifier (',' ParamList)*
        
        ReferencePointcut := (FullyQualifiedName '.')? Identifier Formals
        
        PointcutExpression := (PointcutPrimitive | ReferencePointcut) |
                              '!' PointcutExpression |
                              '(' PointcutExpression ')' |
                              PointcutExpression '&&' PointcutExpression |
                              PointcutExpression '||' PointcutExpression 
        
        === Advice ===
        
        to be written...
        
        === Inter-type Declarations ===
        
        to be written...
        
        === Declare Statements ===
        
        to be written...
        
        === Aspects ===
        
        to be written...