Thursday, April 14, 2005

EJB 3 and AOP: the EJB interceptor dilemna

(imported from http://blogs.codehaus.org/people/avasseur, read comments there)


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
href="http://www.oracle.com/technology/tech/java/ejb30.html">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
href="http://www.aip.org/history/heisenberg/p08.htm">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
href="http://www.theserverside.com/articles/article.tss?l=AspectWerkzP1">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
    href="http://docs.codehaus.org/display/AW/AOP+Benchmark">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
href="http://www.theserverside.com/articles/article.tss?l=AspectWerkzP2">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
href="http://backport175.codehaus.org/">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
href="http://cvs.aspectwerkz.codehaus.org/viewrep/aspectwerkz/aspectwerkz4/src/compiler-extensions/ejb3">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 ..