Thursday, 23 June 2011

Applying the @Interceptors Annotation to EJB3 Beans

One of the new features of EJB3 is the ability to apply simplistic Aspect Oriented Programming (AOP) to your beans. This is achieved using the @Interceptors and @AroundInvoke annotations. I’ve called this ‘simplistic AOP’ not to deride it, but to compliment it. AOP can be a very complex topic, and if you only want to implement some simple functionality, like logging for example, then you need a simple tool to do it.

There are two steps to implementing interceptors, the first is to write an interceptor class and the second is to apply that class to your EJB3 bean.

I’ve pinched my Interceptor code from 'Beginning EJB 3 Application Development: From Novice to Professional ' by Ragu R. Kodali and Jonathon Wetherbee. This isn’t my favourite book on EJB 3, I prefer ‘EJB 3 in Action’ by Debu Panda et al. as I find its explanations clearer. However, the interceptor code below serves my purposes here. Looking at the code, you’ll see that it’s a simple method profiler class that measures and prints the time taken for a business call.

public class TimerLog {

 
/**
   * Measure and print the time take for any method call
   *
   *
@param ctx
   *            The invocation context - contains the class info we're interested in.
   *
@return The result of the wrapped method call.
   */
 
@AroundInvoke
 
public Object measure(InvocationContext ctx) throws Exception {
   
String beanClassName = ctx.getClass().getSimpleName();
    String businessMethodName = ctx.getMethod
().getName();
    String target = beanClassName +
"." + businessMethodName;
   
long startTime = System.currentTimeMillis();
    System.out.println
("Invoking " + target);
   
try {
     
return ctx.proceed();
   
} finally {
     
System.out.println("Exiting " + target);
     
long totalTime = System.currentTimeMillis() - startTime;
      System.out.println
("Business method " + businessMethodName + "in " + beanClassName
          +
"takes " + totalTime + "ms to execute");
   
}
  }

}

Unlike Kodali and Wetherbee, who shoe horn this code in with their business code, I’ve put the interceptor code into its own class. The reason being that I want to uphold the Single Responsibility Principle (a class shouldn't’t profile itself and do business logic) and I can therefore re-use this code on other EJBs.

From the code above, you can see that there are two steps to implementing our interceptor. Firstly, you need to write a method with the appropriate signature and secondly, you need to apply the @AroundInvoke annotation to that method. The method signature must be:

  public Object methodName (InvocationContext ctx) throws Exception {

Another point to note is that you must add the following line to your code:

      return ctx.proceed();

This allows the interceptor to call your code.

Having created an interceptor class, the next step is to apply it to your business code and I’m going to apply it to my WineSearch EJB that I’ve used in a previous blog. The interceptor can be applied on both a class an method level. Applying this interceptor to an entire class means that you’ll be profiling all methods in that class. Applying it to a single method (or number of methods) allows you to home in on potential problems in those individual methods.

The code below demonstrates how to apply an interceptor to an entire class:

@Interceptors({ TimerLog.class })
@Stateless(name = "WineSearchFacade", mappedName = "WineSearch")
public class WineSearchFacadeBean implements WineSearchFacade, WineSearchFacadeLocal {

The code below demonstrates how to apply your interceptor to an individual method.

  @Interceptors({ TimerLog.class })
 
@Override
 
public WineList wineSearch(WineCountry country) {

   
return wineService.getWineByCountry(country);
 
}

...and that’s really all there is to it. Whether or not you like AOP or think that it’s a solution looking for a problem is a different matter and will probably feature in this blog on some future date...

No comments: