EJB 3 and AOP: the EJB interceptor dilemna
[ avasseur ] 16:50, Thursday, 14 April 2005

In this post I present an implementation a subset of the EJB 3 specification (JSR-220) : the EJB interceptors. As of today no EJB 3 preview brings an implementation that seamlessly integrates with AOP, despite the concept similarities.
The proposed implementation is fully runnable out of any container, and introduce a specific extension to allow use of pointcut in EJB interceptors, thanks to AspectWerkz extensible AOP container.
Wants to know more about EJB 3 interceptors, and how they are closed to AOP or different from AOP ? Read more.

Should we consider EJB interceptor as AOP ?

As you may know, the EJB 3 specification (JSR-220) defines Interceptors for EJBs (stateless, statefull and message-driven). If it might be a good idea to spread an answer for the need of addressing cross-cutting concerns in J2EE and EJBs in particular, it might be a bad idea to do it for those of us (more and more numerous) familiar with AOP implementations like AspectJ and AspectWerkz, especially because huge limitations of what the specification allows us to do with those interceptors - at first sight and excluding any vendor specific extension.

The most anti-AOP concept in the specification is that the EJB that wants interceptor have to declare it explicitly using a @javax.ejb.Interceptor or @javax.ejb.Interceptors class level annotation - which breaks the obliviousness of aspects. That in favor of making things explicit, which might be good for J2EE users.

Further on, the EJB bean itself can have a method that intercept its own business method ie the bean is the aspect (that sounds like marketing isn'it ?). Well. That's a specification and is interesting in the sense that it democratize a technology. So what can we do to democratize AOP as part of the EJB 3 specification ?

@javax.ejb.Stateless
@javax.ejb.Interceptor("test.ejb3.MyInterceptor")
public class MyEJBIsTheAspect {
 
     // business method
     public int businessSum(int i, int j) {
          return i + j;
      }
 
     // interceptor method within the bean (the bean is the aspect)
     @javax.ejb.AroundInvoke
     public Object interceptMySelf(InvocationContext ctx) throws Exception {
          System.out.println("--> MyEJBIsTheAspect.interceptMySelf");
          System.out.println("  method: " + ctx.getMethod());
          for (int i = 0; i < ctx.getParameters().length; i++) {
               Object o = ctx.getParameters()[i];
               System.out.println("  args["+i+"]: " + o);
           }
          return ctx.proceed();
      }
 
}

State of the art (not that much..) in JBoss and Oracle AS EJB 3 preview

After having a look at JBoss EJB 3 and OracleAS EJB 3 previews, I was even more disapointed. Both of them are using a reflective based approach to invoke the interceptors. This means that the performance of the interceptor will be bad, and that a Heisenberg effect will be inevitable and actually fairly big (no wonder that ones will use interceptor to gather performance metrics and thus as soon as you observe the bean, you are observing a different things than what actually happens).

Ones may say this is a microscopic view. It is. But when thinking about EJB 2 stories in the past a sound idea would be to make sure we don't waste resources where we can avoid it. And thinking about JBoss history around AOP, that's rather suprising that the EJB 3 interceptors are not cleanly integrated in their AOP framework. I personnaly consider that we have enough technology around to make it far better, and far more consistent with AOP. Given the impact that AOP will continue to have, ones would better figuring out how to do that now with EJB 3 - assuming that EJB 3 succeeds.

AspectWerkz extensible container value proposal to EJB 3 interceptors

I decided to give it a try with the AspectWerkz extensible container. As Jonas Bonér described it in a TSS article, AspectWerkz can be considered as a generic AOP runtime platform, in which ones can hook in any kind of AOP/AOP like programming model. AspectWerkz aspects are one, AspectJ aspects are another, and we have implementations for Spring AOP and AOP Alliance aspects. An AspectModel answers three essential properties: what is the life cycle of the aspect, how to invoke it and how the programming model exposes the closure with wich the user proceed.

It happens that the EJB 3 interceptor can be seen as a very simple AOP programming model in two ways:

  • ones can define interceptor class. An interceptor (advice) is thus a @javax.ejb.AroundInvoke annotated method with a specific signature (no interface, no mandatory method name) within a class - the interceptor class (aspect).
  • ones can define a @javax.ejb.AroundInvoke annotated method (advice) in the EJB itself (aspect): the bean is the aspect !
  • the closure is defined with the interface javax.ejb.InvocationContext

public interface javax.ejb.InvocationContext {
 	public Object getBean();
 	public Method getMethod();
 	public Object[] getParameters();
 	public void setParameters(Object[]);
 	public Context getEJBContext();
 	public java.util.Map getContextData();
 
 	public Object proceed() throws Exception;
}

The value proposal I am bringing here with the AspectWerkz extensible container is the following:

  • no reflection at all. See performance figure f.e at our benchmark site
  • seamless integration of the interceptors with the aspects. They are made "aspect" thanks to AspectWerkz and the other aspect model (AspectWerkz aspect, AspectJ aspect, whatever aspect model registered in the runtime) coexists nicely
  • easy points for vendor specific extension:
    • real pointcut to narrow the matching of an interceptor method to a very precise set of bean methods
    • life cycle control for the interceptor class
    • supporting cflow(), args() and alike AOP semantics
    • introducing high performant hot deployment and undeployment of EJB interceptors
  • "out of container" runtime available
  • rich integration patterns: application preparation (like ejbc / appc for faster deployment) or seamless deployment

AspectWerkz extensible container details

To better understand how things will looks like, you need some background in AspectWerkz:

The first key part is "org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel". We will provide two implementations of it. One for interceptor class (the interceptor class is the aspect), and one for bean as interceptor (the bean is the aspect). The second key part is in integrating the EJB interceptor closure "InvocationContext" that defines the "proceed()" method with the more general idea of "Joinpoint.proceed()" of the runtime. In short, this means that the interceptor will be entirely part of the aspect chain, and if there is a transaction aspect to handle EJB transaction boundaries, they will share the very same chain - as ones expects - thus making it easy for the implementation to organize precedence rules (as define in section 3.5.1 of JSR 220 for example). As we already wrote this transaction aspect for EJB 3 in a previous AspectWerkz TSS tutorial, that's rather a simple idea and basic requirement.

The runtime will take care of aggregating the models depending on which aspect apply to which join point, each aspect having its specific AspectModel and each AspectModel implementation being responsible for generating what it needs:

// a closure to deal with the join points
// ie models org.codehaus.aspectwerkz.joinpoint.JoinPoint interface
// to fit with the transaction aspect, or any other aspect
// and javax.ejb.InvocationContext to fit EJB3 interceptors
 class jitEJB3Closure implements JoinPoint, InvocationContex  
 {
     
     // the closure hosts state, optimization makes it more complex than that
 
     // the target of the join point ie the intercepted ejb
     // which is also third aspect: remember the bean is the aspect !
     private MyEJBIsTheAspect target;
 
     // first aspect in the chain, statically compiled
     private TXAspect aspect_0;
 
     // second aspect: the interceptor class
     private MyInterceptor aspect_1;           
   
     // details skipped that makes those fields initalized as they should.
 
     // as defined in javax.ejb.InvocationContext
     public Method getMethod() {...}             
 
     // as defined in JoinPoint
     public Class getTargetClass {...}           
 
     // other methods as per each aspect model affecting the join point
     ...
 
     // generic proceed() method as defined in JoinPoint and InvocationContext
     public Object proceed() throws Throwable    
     {
          // sort of a loop to proceed with the advice chain
          
          // case first round: TX aspect
          // TX aspect
          // as defined in the "AspectWerkzAspectModel implements AspectModel"
  	aspect_0.manageTX(this) 
          // "this" is considered as the JoinPoint instance
  
          ...
  
          // case second round: interceptor class
          // as defined in the yet to be written
          // "EJBInterceptorModel  implements AspectModel"
  	aspect_1.someNameOfYourChoice(this)        
          // "this" is considered as the InvocationContext instance
          
          ...
  
          // case third round: the bean is the aspect
          // as defined in the yet to be written
          //  "EJBIsTheAspectModel implements AspectModel"
  	target.interceptMyself(this)
          // "this" is considered as the InvocationContext instance
          ...
       }
 
    }

That may sound a bit of gory details, but that's actually simple once you get the idea of this closure acting for multiple AspectModel in mind. The code is actually 15O lines for the interceptor class model (EJBInterceptorModel) and 25 lines for the bean is the aspect model (EJBIsTheAspectModel) thanks to some inheritance. It mainly deals with

  • generating the InvocationContext methods (like getMethod(), getParameters())
  • dealing with the EJB 3 aspect life cycle ie
    • push the EJB instance on stack for the EJBIsTheAspectModel
    • create an interceptor class instance, bookeep it and push it on stack for the EJBInterceptorModel (as the spec does not specifies the life cycle, I will use a singleton model).

What about the deployment ?

I presented the runtime, but there is an interesting topic as well: the deployment. AspectWerkz pioneered the aop.xml to define which aspects are affecting the system. Obviously, we don't want that for EJB interceptor. We need to use the AspectWerkz API to register what we need in the system. The interesting concept here is that we will transform what the EJB specification defines thanks to annotation (@javax.ejb.Interceptor, @javax.ejb.Interceptors, @javax.ejb.AroundInvoke) in real AOP pointcut that the AOP container understand. That is where a vendor may easily hook in an extension (ie introduce a new annotation for example) to refine the interceptor class life cycle, or to introduce a pointcut.

I am defining a class that will expose an API that will hide the AOP registration from the user or from the other parts of our spec. implementation.

public class EJBInterceptorDeployer {
 
    public static void deploy(String ejbClassName, ClassLoader loader) {
        // step 1 - interceptor class
        
        // read the ejbClass @Interceptor and @Interceptors class level annotation
        // for each interceptor class name found as value of those annotation
        //     get the @AroundInvoke method information
        //     deploy an aspect
        //         using the EJBInterceptorModel
        //         to the pointcut : "execution(!@javax.ejb.AroundInvoke !static * " + ejbClassName + ".*(..))"
        //
        
        // step 2 - @AroundInvoke method of the bean itself
        // find the @AroundInvoke method if any
        // deploy an aspect
        // 	using the EJBIsTheAspectModel
        //	to the same pointcut
        
     }
 
  // method making use of AspectWerkz aspect deployment API
 
}

The thing to note about the deployer is that it is not using reflection. It is f.e. using our BackPort175 implementation to read the EJB annotations from the bytecode. If we were not doing so, we would trigger the EJB class loading while our system is not yet defined, and the weaver would not be able to do the job when using load-time weaving approaches.

What about running the system ?

In the sample application, I am deploying the EJB using the EJBInterceptorDeployer presented in the previous section. In a full blown container I would hook the call to it when the application or EJB deployer would register the EJB in the system. The nice thing is that I can run my EJB and still have the interceptors when running as a standalone application by using AspectWerkz load time weaving, and a simple static block to declare which class is an EJB.

The sample application can be run with (for ones familiar with AspectWerkz, there is no aop.xml..)

java -javaagent:lib\aspectwerkz-jdk5-2.0.jar test.ejb3.Sample
public class Sample {
 
     static {
          EJBInterceptorDeployer.deploy("test.ejb3.MyEJBIsTheAspect", Sample.class.getClassLoader());
      }
 
     public static void main(String args[]) throws Throwable {
          // some one would do a lookup or inject for that when in the container
          MyEJBIsTheAspect myEjb = new MyEJBIsTheAspect();
  
          System.out.println("Calling the EJB");
          int i = myEjb.businessSum(1, 2);
          System.out.println("got : " + i);
      }
}
AW EJB3 - Deploying test.ejb3.MyInterceptor for test.ejb3.MyEJBIsTheAspect
AW EJB3 - Deploying test.ejb3.MyEJBIsTheAspect for test.ejb3.MyEJBIsTheAspect
Calling the EJB
--> MyInterceptor.interceptStandalone
  method: public int test.ejb3.MyEJBIsTheAspect.businessSum(int,int)
  args[0]: 1
  args[1]: 2
--> MyEJBIsTheAspect.interceptMySelf
  method: public int test.ejb3.MyEJBIsTheAspect.businessSum(int,int)
  args[0]: 1
  args[1]: 2
got : 3

Conclusion

Though at first glance I found the interceptor part of the EJB 3 specification fairly odd, and was a bit sad that some important AOP concepts like precedence and aspect life cycle, as well as expressiveness of the pointcut language are completely sacrified, I must admit that it might help ones familiarize with AOP concepts.

It took 1h to implement the EJB 3 interceptor spec and have it perfectly integrated with AOP - unlike so far exposed EJB 3 previews by JBoss and Oracle (though those are actual preview, while this article is more a technology focus and positionning). It took 15 minutes more to implement a pointcut extension thru a new @org.codehaus.aspectwerkz.ejb3.AroundInvokeAOP annotation, that allows me to reuse the expressiveness of the pointcuts ie a vendor extension:

public class MyInterceptor {
 
     @AroundInvoke
     @AroundInvokeAOP("execution(* *.businessSum(..))")
     public Object interceptStandalone(InvocationContext ctx) throws Exception {
          System.out.println("--> MyInterceptor.interceptStandalone");
          System.out.println("  method: " + ctx.getMethod());
          for (int i = 0; i < ctx.getParameters().length; i++) {
               Object o = ctx.getParameters()[i];
               System.out.println("  args["+i+"]: " + o);
           }
          return ctx.proceed();
      }
}

Some more time would allow me to have further features, like f.e. supporting cflow and args pointcut, so that an interceptor may look like an advice as it looks like in AspectWerkz aspects, with static access to intercepted method arguments etc (ie no boxing in an Object[] array).

A next iteration would be to integrate with the AspectWerkz hot deployment feature, and this would bring in hot deployment of EJB interceptors at no cost - while still having everything statically compiled for the runtime to perform at its best.

This implementation may seems at first glance full of details, and perhaps like a hammer to address a simple part of the spec. I don't think so. I think it address the very crucial point that so far everyone has skept - including JBoss folks (that have a foot in the AOP trench): it integrates seamlessly with the aspects and AOP concepts, and actually allow advanced user to apply more advanced AOP concepts as well ie bridging the gap between a commercial concept and needs (EJB interception) and a sound concept backed by years of research (AOP and cross-cutting).

So which vendor will be the first to have its EJB play well with AOP : like applying an interceptor thru a real pointcut, defining programmatic precedence etc, dealing with cflow and hotdeployment of interceptors, and all that with an implementation that can scale ?

Want the source code ? This one is part of AspectWerkz CVS. It can be browsed from here.



Side note: my feedback on the specication:

There are some odd things in the 3.5 section of the JSR-220 that I have spot:

  • - (sect. 3.5.1) an interceptor class or EJB can only have one single @AroundInvoke method (advice) in it, and its signature is (sect. 3.5.4) "@AroundInvoke Object someNameOfYourChoice(InvocationContext ctx) throws Exception". Why limitating that to having one single method in the interceptor class or EJB (sect. 3.5.1). My guess is that it is tied to the fact that precedence between advice would then be harder to define in the spec without new semantics. Then one might wonder why an interceptor is not defined as an interface with one single method "intercept(...)" that the EJB could implement as well. Having this interface would suppress the need for @AroundInvoke which sounds like an annotation overuse (unless it is a door open for vendor extension as I will do below..). I'll be interested in EG feedback on that topic, especially in regards of the first limitation.
  • - interceptor components life cycle is unspecified but stateless. That sounds like a very important and powerfull concept of AOP (especially AspectJ) that has been left aside. It is probably an interesting door to provide vendor specific extension ie thread safe interceptor, per bean class interceptor, per bean instance interceptor, per application interceptor etc (but then interceptor component may not be stateless anymore.)
  • - (3.5) an interceptor intercepts all business method (or MessageListener methods for MDB). This means that every interceptor will be poluted with a code snip like "if (invocationContext.getMethod().getName().equals("doSomething")) ..." ie ones will have to write sort of a pointcut in a very loosy way while AOP allow us to write that in a neat way (and further, a way that tools can easy understand to spot which method is intercepted by what).
  • - javax.ejb.InvocationContext is tied to EJB. What will happen when interceptors will end up somewhere else ?

Fortunately, the implementation exposed in this article addresses those issues nicely from a vendor specific extension perspective.

Note: These are my own thoughts and not of my employer ..


Comments

I think you should have focused on the coolness of the AW extensible AOP container rather than taking a swipe at EJB 3. Its sad that you have to trash another piece of technology to make your own product look better.

Anyways...

Interceptors are currently applied through the spec via the @Interceptors annotation on the bean class, however, the EG has not yet defined the XML mappings yet. Obviously interceptor attachment is better done through an oblivious means. True, there will not be the full power of a pointcut language behind them, but in all honesty, the majority of users probably cannot grok pointcut expressions anyways.

You have another totally lame-ass comment about performance and the reflective nature of both JBoss EJB 3.0 and Oracle's impl. The costs of reflection are totally irrelevant compared to any of the other operations a Session or MDB EJB is required to accomplish. I find it sad that the AspectWerkz team continually puts importance on the relevance of microbenchmarks. I think you should actually try and write an EJB container before making such irrelevant claims.

Finally, JBoss EJB 3.0 is built on top of JBoss AOP and thus you have the full power of pointcut expressions built-into our EJB 3.0 implementation. This allows you to define your own pluggable annotations and aspects that are usuable not only with your EJB container, but can be weaved anywhere into any class.

--Bill Burke, April 14, 2005 09:17 PM

Hi Bill,
Always the first shooter ;-)

I do not want to blame the spec. to push for AOP. You are mistaken. I precisely wanted to explore what could be done to brige the gap than one has at first when a bit familiar with AOP (I wrote: "the spec. is interesting in the sense that it democratizes a technology (interception / cross-cutting). So what can we do to democratize AOP as part of the EJB 3 specification ? ")

As done in the sample implementation, I am bringing the pointcut expressiveness to the EJB interceptors thru a "vendor extension", and write that the same can be done for life cycles hence bridging the gap between the EJB interceptor model and more "complete" AOP programming models.
I definitely think there will be value in doing so and it looks like you are heading this way in your own vendor extension.

As per micro-benchmark, I think I am staying humble. I contacted you privately before to ask you about that impl. details and obviously don't blame your implementation for that. The value of the presented implementation details here is in the closure, that really achieve to have one single chain, while in JBoss EJB 3 impl f.e. there are 2 chains (one for the JBoss aspect proceed() and one other for the EJB interceptors proceed()). I don't think it is reasonable to argue that this last design is better or more natural, no matter how it scales.

Alex

--Alex, April 14, 2005 09:34 PM
Post a comment









Remember personal info?