adam bien's blog

Interceptors (EJB 3) For Absolute Beginners - Or Pragmatic AOP in 2 Minutes (without XML :-)) 📎

After a lightning introduction of EJB 3, JPA and DI, I would like to explain the essence of interception and so realization of cross-cutting aspects.

 Requirements:

  1. Installled JDK 1.5 (better 1.6) 
  2. An IDE of your choice e.g. vi, emacs, netbeans 6.1/6.5 (SE or EE), Eclipse Ganymede (SE or EE)
  3. @Stateless, @Local Annotations in classpath
  4. An Java EE 5 capable application server of your choice. It will work with Glassfish v1+ (better v2), JBoss 4.2+, WLS 10+ and probably Geronimo (not tried yet)

What is to do:

  1. Create and deploy a simple Stateless or Stateful Session Bean.
  2. Create a simple Java class with one method with the following signature:  public Object <any name you like>(InvocationContext context) throws Exception:

    import javax.interceptor.AroundInvoke;
    import javax.interceptor.InvocationContext;
    public class TracingInterceptor {
      
        @AroundInvoke
        public Object logCall(InvocationContext context) throws Exception{
            System.out.println("Invoking method: " + context.getMethod());
            return context.proceed();
        }
    }
  3. The method has to be annotated with  @AroundInvoke. It is the one and only available annotation.
  4. Inside the method you can "decorate" existing functionality. The invocation context.proceed() invokes the actual method and returns the value. An interceptor wraps the method completely.
  5. Apply the interceptor on any Session Bean you like e.g.:

    @Interceptors(TracingInterceptor.class)
    @Stateless
    public class HelloWorldBean implements HelloWorld {
       
         public void sayHello() {
            System.out.println("Hello!");
        }
    }
  6. The annotation @Interceptors can be applied for the whole class, or chosen methods. You can even exclude interceptors with @ExcludeClassInterceptors - but it is rarely needed.
  7. Compile everything and JAR the output (in Netbeans just "build", in Eclipse "Export -> JAR")
  8. Copy the JAR into the autodeploy folder of WLS 10 (bea10\user_projects\domains\YOUR_DOMAIN\autodeploy), or glassfish\domains\domain1\autodeploy in the case of Glassfish v2, or jboss-4.2.2.GA\server\default\deploy in case of JBoss
  9. Inspect the log files, you are done :-)

What you have gained:

  1. There is no XML needed - its DRY.
  2. Its robust - compiler checks for the existence of the Interceptor and checks the right spelling of the annotation etc.
  3. Cross cutting functionality can be easily factored out into reusable interceptors.
  4. DI works in interceptors. You can easily inject resources or other beans into an interceptor.
  5. The whole method is wrapped - you have full access to the parameters and return values. You can even reexcute the method or not do it at all (for caching purposes).
  6. It's self documented: there is no surprise - the annotation is visible in code.
  7. They are portable and run on every Java EE 5 compliant application server.
  8. No additional frameworks, libraries etc. are need. This is good for maintenance.
  9. Its flexible - if you prefer XML - no problem just configure the decoration in a XML-descriptor:
    <ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
             version = "3.0"
             xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
        <interceptors>
            <interceptor>
                <interceptor-class>com.abien.logging.interceptor.TracingInterceptor</interceptor-class>
            </interceptor>
        </interceptors>
        <assembly-descriptor>
            <interceptor-binding>
                <ejb-name>
    HelloWorldBean</ejb-name>
                <interceptor-order>
                    <interceptor-class>com.abien.logging.interceptor.TracingInterceptor</interceptor-class>
                </interceptor-order>
            </interceptor-binding>
        </assembly-descriptor>
        </ejb-jar>

You will find a more useful example (ThreadTracker) here. It is almost Java EE compiant (changing thread names is actually not allowed) :-).

Interceptors and EJBs seem to be controversial. During a workshop at Sun Tech Days '07 someoune complained about the of flexibility. My answer was: find a use case, for which Interceptors are not sufficient - and you will get a t-shirt. He wasn't able to find a single use case - but I gave him the t-shirt at the end of the conference :-). Interceptors are absolutely sufficient for the most use cases.

[I described in the book "Real World Java EE Patterns" Interceptors, Dependency Injection etc. more deeply]