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:
- Installled JDK 1.5 (better 1.6)
- An IDE of your choice e.g. vi, emacs, netbeans 6.1/6.5 (SE or EE), Eclipse Ganymede (SE or EE)
- @Stateless, @Local Annotations in classpath
- 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:
- Create and deploy a simple Stateless or Stateful Session Bean.
- 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();
}
} - The method has to be annotated with @AroundInvoke. It is the one and only available annotation.
- 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.
- 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!");
}
} - 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.
- Compile everything and JAR the output (in Netbeans just "build", in Eclipse "Export -> JAR")
- 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
- Inspect the log files, you are done :-)
What you have gained:
- There is no XML needed - its DRY.
- Its robust - compiler checks for the existence of the Interceptor and checks the right spelling of the annotation etc.
- Cross cutting functionality can be easily factored out into reusable interceptors.
- DI works in interceptors. You can easily inject resources or other beans into an interceptor.
- 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).
- It's self documented: there is no surprise - the annotation is visible in code.
- They are portable and run on every Java EE 5 compliant application server.
- No additional frameworks, libraries etc. are need. This is good for maintenance.
- 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]