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] 


NEW: Java EE 7 Testing and Quality Workshop

A book about rethinking Java EE Patterns

Comments:

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

Posted by Marcelo Alcantara on August 07, 2008 at 05: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

Posted by Laird Nelson on August 07, 2008 at 06: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

Posted by Adam Bien on August 07, 2008 at 06:50 PM CEST #

Laird,

you could encapsulate the access in an EJB. Almost no overhead - should work...

regards,

adam

Posted by Adam Bien on August 07, 2008 at 06: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

Posted by Chad Whelan on August 08, 2008 at 06: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

Posted by Adam Bien on August 08, 2008 at 06:54 PM CEST #

Duh; yes, of course. Thank you!

Best,
Laird

Posted by Laird Nelson on August 09, 2008 at 12:45 AM 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

Posted by Xavier on August 10, 2008 at 03:39 PM CEST #

Realy cool! Just required something like that to validate the user on every bean method.
Was just in time! Many thanks

Posted by stritt on August 11, 2008 at 07: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

Posted by Adam Bien on August 11, 2008 at 07: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

Posted by Xavier on August 12, 2008 at 11:16 AM CEST #

Quite a good blog!
Thanks!

Posted by Chandra Mohan Bhatt on December 20, 2008 at 03: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

Posted by Murali on February 20, 2009 at 05:56 PM CET #

Hi Adam,

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

Detlef

Posted by Detlef Folger on May 03, 2009 at 09: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

Posted by Alan Rubin on July 07, 2009 at 01:09 PM CEST #

Hi Adam,

Thanks for this great article.
Regards,
Sabino Mendes

Posted by Sabino Mendes on October 27, 2009 at 06:17 AM CET #

I am using an interceptor for logging in my application and the problem is a log entry is made every time the my session bean methods are called and if the method fails, by throwing an exception for instance, there is no way of logging this and to that end l don't think these interceptors are sufficient for logging. If there is a way around this please let me know otherwise you owe me a t-shirt

Posted by Christian on March 29, 2011 at 06:37 PM CEST #

@Christian,

whats about that:

public class LoggingInterceptor {

@AroundInvoke
public Object logInvocation(InvocationContext context) throws Exception{
long start = System.currentTimeMillis();
try{
return context.proceed();
}finally{
System.out.println(context.getMethod() + "---Invoked in " + (System.currentTimeMillis()-start));
}
}
}

Now I expect a t-shirt :-)

Works?

enjoy hacking,

adam

Posted by adam-bien.com on March 30, 2011 at 01:15 PM CEST #

@Adam

I don't think you understood my scenario let me re explain:

I have the following interceptor:
public class LoggingInterceptor {
@AroundInvoke
public Object log(InvocationContext context) throws Exception{
System.out.println("Executing method:"+context.getMethod().getName());
return context.proceed();
}
}

and the following session bean:
@Stateless

public class TestEjb implements TestEjbRemote {

@Interceptors(LoggingInterceptor.class)
public void myTestMethod() throws Exception{
throw new Exception();

}

}

The thing is I don't want the interceptor to log anything when 'myTestMethod" fails by throwing an exception how can I achieve that.

Posted by Christian on March 30, 2011 at 03:51 PM CEST #

@Christian,

do you want something like that?:

@AroundInvoke
public Object log(InvocationContext context) throws Exception{
try{
return context.proceed();
System.out.println("Executing method:"+context.getMethod().getName());
}catch(Exception e){
throw e;
}
}

Do you need my address for the t-shirt? :-)

adam

Posted by adam-bien.com on April 01, 2011 at 01:53 AM CEST #

I wonder the following: if you have a bean class which implements both a remote and a local interface, and you set an interceptor on the bean class: how to figure out inside the interceptor code if a local bean or a remote bean is being called (usecase: for a remote bean I want to do logging in case of an error, for a local not).

Related to this (would also solve the issue): is there any way to specify the interceptor on the remote or local interface (instead of on the bean impl class).

The only thing I can think of now, is a copy/paste :-(

Posted by jpwinne on May 10, 2011 at 01:17 PM CEST #

@JPwinne,

you can only apply interceptors on the Bean-Impl. They are called on every @Local, @Remote, @Schedule - local or REST call. It is impossible know through which "channel" your bean was called...

sorry :-( && thanks!,

adam

Posted by Adam Bien on May 11, 2011 at 12:19 AM CEST #

Hi Adam

I have an sales EJB project which controls the inventory, in and out of stock.

I want to add extra functionality to the EJB, such as payment and billing.

For this I want to use Interceptor in Session Bean class. I want to implement the new functionality as a component, decoupled from the current implementation.

But I have not access to the current session bean or Xml (ejb-jar), so I can not put the @ Interceptor in the class or method! how I can solve my problem?

Can I add @ Interceptor from another location, class or session? There is another way of doing?

Thanks for your help.

Posted by Marcos Jara on May 16, 2011 at 07:40 AM CEST #

> you can only apply interceptors on the Bean-Impl.
> They are called on every @Local, @Remote, @Schedule - local or REST call.
> It is impossible know through which "channel" your bean was called...

How about doing something like this:

@Resource
private SessionContext ctx;

...

private boolean isRemoteCall(){
Class businessInterface = sessionContext.getInvokedBusinessInterface();
return MyReflectionUtil.hasRemoteAnnotation(businessInterface);
}

To write the utility method is quite straight forward: Just check if the Interface class or any of its superclasses has the @Remote annotation.

Posted by Carsten Erker on July 06, 2011 at 10:47 PM CEST #

Thanks for the excellent example, Adam. I followed it and everything was right.
Satisfied, i modified it making a JSF 2.0 page ask for a @Model bean (like a managed bean) invoke the HelloWorldBean by his remote interface. The interceptor captured the message, passed to the SB and this modified another atribute of the same instance do @Model bean and the JSF, finally, closed the circuit, saying:
"The answer returned fine from all the circuit. Everything works".
I had tryed to follow all yours NetBeans articles.
Thank you very much.
For a beginner in CDI, it was nice to see the Weld using the same instance of the @Model bean binded to the JSF page show the modified atribute at the JSF page, closing all the flow of messages.

Posted by Paulo Vicente on July 10, 2011 at 01:38 AM CEST #

Thanks a lot for this tutorial on Interceptors.

Posted by Incrediblogger on August 10, 2011 at 03:49 PM CEST #

A question about interceptors and JAX-WS EJB implementation.

@Webservice(endpointInterface="xx.Businness", serviceName="WSBean")
@Stateless
@Interceptors(Interceptor.class)
@Remote(Remote.class)
public class WSBean implements Remote, Ws {
public String test1(Object h, String arg) {
return "test1";
}

@WebMethod
public String test2(String arg) {
return "test2";
}
}

@Remote
public interface Remote {
public String test1(Object h, String arg);
}

@WebService
public interface Ws
{
public String test2( String arg ) ;
}

When callin test1 on the remote interface by RMI the Interceptor class is called.

On the contrary, when test2 is being called by a webservice client, Interceptor is not triggered.

Are there any way to get Interceptor triggered even on the webservice methods on an EJB based JAX-WS implementation?
Or is the only alternative to implement handlers?

Thanks for any help
/Tamas

Posted by Tamas Szabo on December 21, 2011 at 02:03 PM CET #

A question about interceptors and JAX-WS EJB implementation.

@Webservice(endpointInterface="xx.Businness", serviceName="WSBean")
@Stateless
@Interceptors(Interceptor.class)
@Remote(Remote.class)
public class WSBean implements Remote, Ws {
public String test1(Object h, String arg) {
return "test1";
}

@WebMethod
public String test2(String arg) {
return "test2";
}
}

@Remote
public interface Remote {
public String test1(Object h, String arg);
}

@WebService
public interface Ws
{
public String test2( String arg ) ;
}

When calling test1 on the remote interface by RMI the Interceptor class is called.

On the contrary, when test2 is being called by a webservice client, Interceptor is not triggered.

Are there any way to get Interceptor triggered even on the webservice methods on an EJB based JAX-WS implementation?
Or is the only alternative to implement handlers?

Thanks for any help
/Tamas

Posted by Tamas Szabo on December 21, 2011 at 03:21 PM CET #

Hi Adam,
Can interceptor annotations be disabled at runtime ?
If the interceptor is used only for logging then how could be avoided the class/method introspection for calling the interceptor when it is not needed, for production environment.

Thanks,
Dany

Posted by Dany on February 07, 2012 at 02:48 AM CET #

1. Define an annotation by default to print the log.

2. If you don't want the log for one class, you can add that annotation to stop logging.

3. If you don't want the log for any class, i think the log level is already controlling that.

4. In the runtime if you want to control for one class, just use any system or db based property to decide printing the log.

Posted by Muthusamy Jayaraman on February 19, 2012 at 10:04 AM CET #

Hi Adam,
I had to write an interceptor to investigate some production problem. Your post helped me build it in a matter of minutes. It explains precisely what a beginner needs. Thanks. and keep blogging :)

Thanks,
Rahul

Posted by Rahul on December 18, 2012 at 06:39 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
...the last 150 posts
...the last 10 comments
Links
License