Combining Java Annotations and Aspects

Aspect-Oriented Programming

I had been looking for a while for some practical case to get my hands dirty with Aspect-Oriented Programming (AOP) in Java. This article is an account on how I did it.

It is not my goal here to give an overview of AOP or AspectJ (there are plenty good places for that), but to illustrate a way of using aspects for a practical purpose. I will combine the usage of aspects with Java annotations in order to provide some useful parameters to the aspects.

Those among you who do not know about AOP can get some detailed description in the wikipedia entry or in an introductory article by IBM rational. There is also the AspectJ programming guide for a more complete reference of AspectJ in particular, the tool that I used.

For those of you who have already dived into Python, you may find this technique leads to something that looks similar to decorators.

A Practical Use Case for Aspects

I was looking for ways to limit access to some specific functionality in my code depending on some acquired rights (these were in our case were represented by license files, stating which modules were allowed to run at a specific machine… but this is not specially relevant to the story). I wanted to do that in a clean way that did not mess the existing codebase. In fact, I liked the declarative nature of annotations and found it well suited:

@NeedLicense(module="sig")
private void generateTimeStamp(...) {
}

This approach also allows people not familiar with the AspectJ syntax to easily use the feature by only modifying the Java sources.

This is a middle-of-the-road solution, since in fact I will not keep everything related to aspects outside of my Java code (which is one of the interesting things of AOP in the first place…) but at least I will do it in a way that does not interfere so much with my code.

Let’s Get to It

Create an annotation

We’ll start by creating the annotation. You’ll probably have used annotations so far (e.g. some of Java’s own like @Override or provided by a framework such as Hibernate, e.g. @Entity) but it is not so common to have written your own.

My annotation will have one additional parameter stating which functional module we will require a license for.

Annotations are available since Java 1.5

The code needed to define our annotation is quite simple:

public @interface NeedLicense {
    // the name of the required module license (by default, "sig")
    String module() default "sig";
}

Like any public class or interface, we need to define this annotation in its own file NeedLicense.java

The @interface keyword defines that this is an annotation. Now, since we will be checking the annotation at runtime, we need to tell the compiler to retain the annotation (by default, annotations are for compile time). That will require use one of Java’s defined annotations (@Retention).

package es.isigma.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLicense {
    ...
}

We can now use this annotation for both methods or full classes whose usage we want to restrict:

// class example:

@NeedLicense(module="guru")
public class MyClassRestrictedForGurus {
   ...
}

// method example
@NeedLicense(module="coffee")
public void getCoffee() {
   ...
}

Of course that will only be a decoration until we introduce and weave our aspects into the java code.

Aspect definition

Now to our aspect. We will begin by defining our aspect, which I will name Restrictions, and create both a pointcut and an advice.

A pointcut defines the places in the code that this aspect refers to or cuts through (aspects are defined to address cross-cutting concerns since they are transversal to our object model). The pointcut syntax is typically used to define patterns based on class and method names, accessibility, and in my case the presence of a given annotation.

public aspect Restrictions {

    pointcut hasLicenseAnnotation():
        execution(@es.isigma.annotations.NeedLicense * *(..)) || 
        execution(* (@es.isigma.annotations.NeedLicense *).*(..));

    before(): hasLicenseAnnotation() {
        checkForLicense(thisJoinPoint);
    }

}

My pointcut is an or (||) combination of two such matching patterns:

  • the first one refers to any method annotated with @NeedLicense
  • the second one refers to methods contained within a class where the class itself is annotated. I will use this one as a quick shortcut to restrict access to all methods for one class.

Pointcuts result in a number of join points, each join point being one specific match of the rules that make up the pointcut.

Now we can link the pointcut (definition of places in the code) with an advice (what to do at these places).

The advice declaration states where related to the pointcut the advice will be executed (in my example before which is typical for security access, but it could be after – e.g. typical for logging – with fine-grained combinations such as after returning an object or after raising an exception, and others).

The body of the advice is standard java code, where we can use aspect-related objects like thisJoinPoint. This will represent the specific point where the advice is being executed, and we can extract from there some useful info… for example, the parameters for our annotation.

Checking the annotation through reflection

The method checkForLicense is where we will write the code we want to execute every time our program goes through the advice. In my example, a runtime error (not a checked exception) will be raised if conditions are not fulfilled:

    private void checkForLicense(JoinPoint jp) {
        Signature aSignature = jp.getSignature();
        if (aSignature instanceof MethodSignature) {
            // first check if whole class is limited
            Class<?> aClass = aSignature.getDeclaringType();
            errorIfNoLicense(aClass);
            Method aMethod = ((MethodSignature)aSignature).getMethod();
            errorIfNoLicense(aMethod);
        }
    }


    private void errorIfNoLicense(AnnotatedElement elem) {
        if (elem.isAnnotationPresent(es.isigma.annotations.NeedLicense.class)) {
            NeedLicense limited = 
                elem.getAnnotation(es.isigma.annotations.NeedLicense.class);
            String neededModule = limited.module();
            // here we would check whether such a license is enabled
            // (supply your own code for the check)
            if(!licenseAvailable(neededModule))
                throw new Error("no access to " + neededModule);
        }
    }

Through reflection we are looking at the Method for the pointcut, its containing Class, and the annotation for either the Method or class. If the annotation is present, we read its module parameter and check whether usage is allowed. Since we are doing this check at runtime, here you may better appreciate the need to set a RetentionPolicy.RUNTIME for our annotation.

About these ads

,

  1. #1 by Gabriel on 2010/12/17 - 11:00

    Hi,

    i’m just wondering if you are not introducing high overhead with this aspect, as everytime you are executing a method that match the pointcut, you need to check the license validity.

    Isn’t it better to have a temp caching mechanism to avoid license checking every time? Cause it’s less likely the license will change once it has been checked?

    Cheers

  2. #2 by Alexander Kriegisch on 2012/08/15 - 16:38

    @Gabriel: Who said that licenseAvailable(String module) does not do caching?

  3. #3 by okor (@okor5) on 2013/02/22 - 15:31

    Hi,

    Is it possible to do define the pointcut on non-annotated classes but just by a class name. e.g.: ‘java.sql.Connection’?

  4. #5 by pkWannaBe on 2014/02/26 - 05:57

    Really nice explanation !!
    I just want to know how easy it is to get the parameters passed to the method in its advice method ( in my case a before method ) ,
    if at all this is possible :) .

    • #6 by Carles Barrobés on 2014/02/26 - 20:23

      By what I remember (it’s been a long time since I wrote this, and haven’t used AOP or Java for years now) within your aspect code you can only get information of what’s going on using introspection or defining the context to be passed through. My way to parameterise aspect behaviour is by having parameters in the annotation.

      I’m only guessing about what you really need and want, it’s not really clear from your comment alone.

      Check whether this helps: http://www.eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html#d0e346

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: