Adam Bien's Weblog

Thursday Aug 07, 2008

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] 


[my tweets]  Rss My book: Real World Java EE - Rethinking Best Practices

Kommentare:

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 #

Quite a good blog!
Thanks!

Gesendet von Chandra Mohan Bhatt am December 20, 2008 at 02:53 PM CET #

I have question I am using ejb3.0
public Object log(InvocationContext invocationContext) throws Exception {

try {

myApp.info(this.getClass().getName(), " EJB Method Started "+invocationContext.getMethod().getName());
myApp.info(this.getClass().getName(), " EJB Method Started "+this.getClass().getName());

return invocationContext.proceed();

} catch (UsernameExistsException e) {
myApp.info(this.getClass().getName(), " EJB Method Exit "+invocationContext.getMethod().getName());
System.out.println("UsernameExistsException AroundInvoke cacth balcok in execute method ");

set("error",e.getMessage());
throw new ApplicantException(e.getMessage());

} finally {
myApp.info(this.getClass().getName(), " EJB Method Exit "+invocationContext.getMethod().getName());

}
can use try catch on this

Gesendet von Murali am February 20, 2009 at 04:56 PM CET #

Hi Adam,

I just added an interceptor to our project for instanciating logging. Thanks for the code snippet provided.

Detlef

Gesendet von Detlef Folger am May 03, 2009 at 07:36 PM CEST #

Hi Adam,

Very good post. One question: Can interceptors be used for plain java class or just for EJB ?

If no, still they cannot entirely replace the Java Proxies...

Thanks,
Alan

Gesendet von Alan Rubin am July 07, 2009 at 11:09 AM CEST #

Hi Adam,

Thanks for this great article.
Regards,
Sabino Mendes

Gesendet von Sabino Mendes am October 27, 2009 at 05:17 AM CET #

Senden Sie einen Kommentar:
  • HTML Syntax: Ausgeschaltet
Interviews/About
My Recent Book
Java One 2009
CommunityOne East N.Y.C
JavaONE 2008 Interview
Search
...the last 150 posts
...the last 10 comments
greenfire.dev.java.net
Links
my.netbeans.org
Visitors
License