Adam Bien's Weblog
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.
Posted at 02:39PM Aug 07, 2008 by Adam Bien in Java EE 5 Architectures And Idioms | Kommentare[11]
[This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]



Hi Adam,
Thanks for the good blog.
I have been following your post and they are very very good.
Regards,
Marcelo Alcantara
System Architect
Credit Suisse Hedging Griffo Brazil
Gesendet von Marcelo Alcantara am August 07, 2008 at 03:26 PM CEST #
Hi, Adam; the EJB specification says that you can't perform EntityManager operations from within an interceptor. But that's what I want to do--I'm looking for a way to do trigger-like operations from within an interceptor.
Do you know of some way to update a record that has just been saved in an interceptor that is spec-compliant?
Thanks,
Laird
Gesendet von Laird Nelson am August 07, 2008 at 04:41 PM CEST #
Marcelo,
thank you very much for your comment. I really appreciate it. I use actually my weblog as a scratchpad and "idea storage". I write stuff down in my dead time - often in Starbucks drinking a coffee, with no preparation. I link to the posts from articles, conference talks so it is convenient to me.
Some posts emerge to articles, some to even books.
Have fun with Java and especially EE 5/6!,
regards,
adam
Gesendet von Adam Bien am August 07, 2008 at 04:50 PM CEST #
Laird,
you could encapsulate the access in an EJB. Almost no overhead - should work...
regards,
adam
Gesendet von Adam Bien am August 07, 2008 at 04:51 PM CEST #
Hi Adam,
First, let me say that that is a great example. Clear, concise, and informational.
Do you know of any way to intercept entity bean calls as well? I'm looking for something similar to the aop interceptor in jboss that works with glassfish v2. Any help would be greatly appreciated.
Thanks,
chad
Gesendet von Chad Whelan am August 08, 2008 at 04:48 PM CEST #
Chad,
thanks for the nice comment. For Entities you can use EntityListeners. They work differently - so it is not possible to intercept the getters/setters, rather than the lifecycle (merging, storing, deleting etc.)
EntityListeners, however, are absolutely portable (I tested them with JBoss, Glassfish and Weblogic),
regards,
adam
Gesendet von Adam Bien am August 08, 2008 at 04:54 PM CEST #
Duh; yes, of course. Thank you!
Best,
Laird
Gesendet von Laird Nelson am August 08, 2008 at 10:45 PM CEST #
Hi, do you know if client-side interceptors will someday be standardized? JBoss EJB2.x had a great and easy way to add interceptors (per app) at the client side for, say, remote session beans. Since JBoss implemented EJB3 with jboss-aop, it's not so easy to configure those interceptors as ejb3-interceptors-aop.xml is configured for the whole server rather than for a particular ear/jar. I often use client-interceptors to pass context/metadata along my remote invocations without polluting my interface's methods with parameters which have no business meaning.
thanks,
Xavier
Gesendet von Xavier am August 10, 2008 at 01:39 PM CEST #
Realy cool! Just required something like that to validate the user on every bean method.
Was just in time! Many thanks
Gesendet von stritt am August 11, 2008 at 05:15 PM CEST #
Xavier,
perhaps this post could be helpful: http://www.adam-bien.com/roller/abien/entry/lightweight_aop_with_plain_java
It should work with EJB 3 as well,
regards,
adam
Gesendet von Adam Bien am August 11, 2008 at 05:58 PM CEST #
Thanks for the link but I don't think regular proxies could do it because I don't get access to InvocationContext#contextMetaData (which is where I would like to put my contextual data). I tried enlarging the array of arguments to pass additional data at the client side and consume it (and reduce the array) at the server side but then I get an exception telling me the number of arguments is incorrect.
regards,
Xavier
Gesendet von Xavier am August 12, 2008 at 09:16 AM CEST #