Aspectivity


11/17/2004

Preferred way of specifying metadata

Category:

I share Dion's concern about metadata hell. I think the key to using annotations correctly is to realize that annotations supply metadata i.e. additional data about the element being annotated. In other words, annotations should describe the element’s characteristics, and not what should happen before/after/around those elements. Here are some examples to illustrate the point.

In the first example, the apparent intention is to show the wait cursor around methods with the @WaitCursor annotation (using annotation processor or AOP etc.):

// Not preferred
@WaitCursor
public void foo() {
}

The problem is that the method is carrying too implementation-specific metadata. A preferred way is to specify the expected time of execution for the method (many fine details omitted; see AspectJ language support for metadata for more detailed examples):

// Preferred
@Timing(avg=50)
public void foo() {
}

An annotation processor or an aspect may use the timing information to show the wait cursor around slow methods. The biggest difference is that the processors and aspects get to decide how to classify methods as slow and what needs to happen for the slow methods. For example, in a non-UI application, it may choose to do nothing. Further, the same information can be used for other purposes: rate monotonic analysis (RMA), connect with a monitoring agent to implement alerts, etc.

As a second example, the apparent intention is to introduce automatic fault tolerance through retries for retry-safe operations:

// Not preferred
@RetryUponFailure
public void getBalance() {
}

A better way is to specify that operation is idempotent and let the annotation processors or aspects decide how to use this additional information:

// Preferred
@Idempotent
public void getBalance() {
}

Finally, here is another example, where the programmer wishes to remove verbose code needed for read-write lock management through the use of annoations.

// Not preferred
@ReadLock
public float getBalance() {
}

@WriteLock
public void credit(float amount) {
}

A better way is to specify if methods are modifying object’s state:

// Preferred
@ReadOperation
public float getBalance() {
}

@WriteOperation
public void credit(float amount) {
}

That said, there are cases where the additional information makes sense only in the context of specific concerns. In those cases, the scenario becomes a little murky. Consider for example, situation where database table names are specified using annotations:

@Persistence(table="myTable")
public class MyClass {
}

The Persistence annotation makes sense only in the context of persistence concern and not when MyClass is considered in isolation. The question to ponder over: Should such metadata be specified in the class itself? The issue with this example is not surprising given debate over the best place to put such annotations -- doclet tags or XML mapping document. It will be interesting to watch how the Java community ends up using metadata.


11/16/2004

AspectWerkz 2.0 RC1 is released

Category:

AspectWerkz team has just released the 2.0 version. The new version contains many good features from the AspectJ users' perspective :

  • AspectWerkz is now an extensible aspect container. AspectJ excels at providing a language-level support for AOP concepts and AspectWerkz excels at its integration with application servers. It seems that one can now write aspects in AspectJ and deploy using AspectWerkz's deployment technology.
  • AspectWerkz's AOP support is now more closely aligned to that implemented in AspectJ:
    • variations of the after() advice
    • percflow() aspect association
    • this(), target(), and args() pointcuts to capture join point context

    The AspectWerkz team plans to address some of the remaining differences from AspectJ (cflow(), cflowbelow() etc.) in a future version. It seems like AspectJ and AspectWerkz will become nearly isomorphic -- you can write program in AspectJ or AspectWerkz and translate between them without much loss of information.

Of course, there are many AspectWerkz-specific improvements such as 20x improvement in performance and hot deployment/redeployment that got to make it more useful as an enterprise AOP solution.

This is exciting! Congratulation to the AspectWerkz team.


11/8/2004

AspectJ language support for metadata

Category:

Metadata and aspect-oriented programming (AOP) is an interesting combination. I am particularly interested in how the metadata facility will impact the AspectJ language. So I gathered all my thoughts in form of a proposal to modify the AspectJ language to support metadata.

At a high level, metadata support in AspectJ requires:

  • Language support for consuming annotations
    • Selecting join points based on annotations associated with program elements
    • Capturing the annotation instances associated with the captured join points using
      1. pointcuts
      2. reflective APIs
  • Language support for supplying annotations
    • Attaching annotations to join points in a crosscutting manner

Language extension to support metadata in AspectJ should be fully backwards compatible and flow well with the current language. This requires following two principles:

  • Introduce no new keywords, if possible.
  • Align with the existing pointcuts and APIs as much as possible.

The proposed syntax can be divided into four parts: extending the signature syntax to allow specifying annotations, adding new pointcuts to allow selecting join points based on annotation associated with them as well as capturing annotation instances as pointcut context, syntax to supply annotations, and modification to reflection APIs to access annotations.

Extending the signature syntax

Adrian Coyler, the AspectJ team lead, has written a design note to extend signature patterns in AspectJ to select join points based on metadata. I have taken Adrian’s idea and extended it to cover more complex cases such as matching based on annotation properties:

Let’s dive directly into a few examples to illustrate the new syntax.

Pointcut Join points matched
execution(@Idempotent public * *.*(..))
Execution of methods that carry an @Idempotent annotation (such as @Idempotent public long getId())
execution(public * *.*(@ReadOnly *, ..))
Execution of methods whose first argument carries a @ReadOnly annotation
set(* @ContainerSupplied dataSource)
Write-access to all fields named dataSource marked with a @ContainerSupplied annotation
within(@Secure *)
All join points inside any type with a @Secure annotation
within(@Secure * && @Persistent *)
All join points within lexical scope of all types that have a @Secure and a @Persistent annotation
within(@Secure *..*)
All join points within lexical scope of all types that have a direct or indirect parent package with a @Secure annotation
within(@Secure *.@Sensitive *..*)
All join points within lexical scope of all types that have a direct or indirect parent package with a @Secure annotation whose child package carries a @Sensitive annotation

I think within() with just annotation type as type pattern such as within(@Secure) should be a compile-time error, since it does not make sense to capture anything in the annotation type itself -- compiler generated code for property access in annotation types is irrelevant from user perspective.

Next, we need syntax for matching join points based on annotation property values and not just annotation types. Note that while the proposal to capture annotation instances using pointcuts can achieve a similar effect, those pointcuts will probably fall into dynamically determinable pointcut category. Here are a few examples that capture join points based on annotation properties.

The following pointcut matches all methods that carry a @Transactional annotation:

execution(@Transactional *.*(..))

The following pointcut restricts the selection to only methods that have the kind property set to Required:

execution(@Transactional(kind==Required) *.*(..))

The following pointcut restricts the selection to only methods that have the kind property set to either Required or RequiredNew:

execution(@Transactional(kind==Required || kind==RequiredNew) *.*(..))

In short, one may specify any Java boolean expression that uses properties and constants to filter the join point selection. Here is a pointcut indicative of complex pointcuts one may write :-):

pointcut longRunningTransactionalRequiredOps()
    : execution(@Transactional(kind==Required || kind==RequiredNew)
                @Timing((min > 30 || max > 100 || ((max-min) > 50))
                        || (avg > 40 && variance > 0.35)) *.*(..))

For wildcard treatment, annotation types should be considered just like other types. Note that since the Java language does not permit inheritance with annotation types, the ‘+’ wildcard won’t make sense (I think it should be a compile-time error to specify annotation type with the ‘+’ wildcard).

New pointcuts

New proposed pointcuts allow select join points based on annotations as well as capturing annotation instances as pointcut context by extending the current this(), target(), and args() pointcuts.

  • @interface(AnnotationTypeOrIdentifier) to capture annotations attached to the program element corresponding to the captured join points themselves. (Note to those attended/viewed my JavaOne presentation: I used annotation(AnnotationTypeOrIdentifier) in that presentation. After more thought, it seems a good idea to avoid a new keyword. While annotation() seems clearer, once Java developers get used to defining annotation types using @interface syntax, the @interface() pointcut may feel more natural.)
  • @this(AnnotationTypeOrIdentifier) to capture annotations attached to the this object associated with the captured join point.
  • @target(AnnotationTypeOrIdentifier) to capture annotation attached to the target object associated with the captured join point.
  • @args(AnnotationTypeOrIdentifier1, AnnotationTypeOrIdentifier2, …) to capture annotation attached to the arguments associated with the captured join point.

Like this(), target(), and args() pointcuts, the proposed pointcuts take two forms: one that specifies a type and another that specifies an identifier. The first form is used to make join point selection only, whereas the second form is used to also collect the annotation instance as a context. For example, the following pointcut matches all the join points with @Transactional annotation:

pointcut transactedOps() : @interface(Transactional);

whereas the following also collects the annotation instance to use inside advice (just like any other context)

pointcut transactedOps(Transactional tx)
    : @interface(tx);

The context collected in this fashion may be used in an if() pointcut. For example, the following pointcut captures only join points that have @Transactional annotation with the kind property set to Required.

pointcut transactedRequiredNewOps(Transactional tx)
    : @interface(tx) && if(tx.kind() == RequiredNew);

Of course, an advice may use collected annotation instances just like any other collected context:

Object around(Transactional tx) : transactedOps(tx) {
    TransactionalKind txKind = tx.value();
    ...
}

Since an element may carry more than one type of annotations (but never two annotations of the same type), the proposed syntax allows using @interface, @this, @target, and @args multiple times in the same pointcut. The following pointcut captures join point with both @Transactional and @Idempotent annotations:

pointcut idempotentTransactional()
    : @interface(Transactional)
      && @interface(Idempotent)

New declare annotations syntax

This new syntax allows attaching annotations to program elements in crosscutting fashion. This syntax resembles the existing declare soft syntax. Adrian Coyler has blogged about this syntax. I have also presented the same idea in JavaOne ‘04. The only difference in the syntax proposed here is switching places between the annotation type and the pointcut to better match the declare soft syntax:

declare annotations: <AnnotationInstance> : <pointcut expression>;

As an example, the following declaration associated a new @Transactional annotation with property kind set to Required to the Account.debit() method:

declare annotations: @Transactional(kind=Required)
    : execution(public void Account.debit(..))
      || execution(public void Account.credit(..));

It should be probably a compile-time error if declare annotations statement would result in multiple annotations of same type for a program elements (much in line with declare parents producing errors if multiple inheritance of classes would result).

Modifications to reflection API

Add new methods in org.aspectj.lang.JoinPoint interface to access annotation instance reflectively.

public interface JoinPoint {

    ... current methods ...

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
    public Annotation[] getAnnotations();

    public <A extends Annotation> A getThisAnnotation(Class<A> annotationClass);
    public Annotation[] getThisAnnotations();

    public <A extends Annotation> A getTargetAnnotation(Class<A> annotationClass);
    public Annotation[] getTargetAnnotations();

    public <A extends Annotation> A[] getArgsAnnotation(Class<A> annotationClass);
    public Annotation[][] getArgsAnnotations();
}

That’s it for now.


Hello World!

Category:

Finally, I started to blog! More to come soon...

Powered by WordPress

Copyright © 2004 Ramnivas Laddad. All rights reserved.

CodeCamp at FootHill College.  Click Here for Details and Registration