Thursday, August 6, 2009

Message interceptors with JMS

Interceptors are a way to add behavior to a system without directly invoking this behavior in from code. The typical anti-example is that if you would want to log the entry and exit of methods on a class, you could do that of course by adding log statements in each and every method. The downside is that you would have to change each and every method, and that all these methods now have repetitive code in there. With the interceptor approach on the other hand, you could do that by adding an interceptor that is invoked before and after the method is invoked. In the code of the interceptor you would then add the log statement. The advantage is that the existing methods are not changed, that repetitive code is avoided, and that the logging concern is concentrated in one part of the code base rather than spread all over.

I think that this logging example is pretty dumb, but I guess it drives home the message of adding behavior non-invasively, and a separation of concerns. I'll discuss some more interesting examples in a minute.

Interceptors in EE

Interceptors have long been the domain of AOP (Aspect Oriented Programming) packages that add interceptors through byte code manipulation, either at runtime or as a post-compile step. For EE developers, things changed with the advent of EE5 in which interceptors were added to EJBs.

An interceptor can be added to all the methods or individual methods of an EJB by adding an annotation to either the class or to individual methods. In the following example, the NoConcurrency interceptor is invoked when the getUnclaimedAccounts() method is called.

@Interceptors(NoConcurrency.class)
public void getUnclaimedAccounts(String category) {...

The interceptor class is a simple Java class that has to have a method with the following signature:

@AroundInvoke
Object <METHOD>(javax.interceptor.InvocationContext) throws Exception

The interceptor method is called when a client calls an EJB method. The interceptor typically calls InvocationContext.proceed(). This will call into the EJB method (or the next interceptor, should there be one).

Interceptor classes are typically packaged as part of an EAR file: they are part of the application.

Interceptors are referenced in an EJB using the @Interceptors annotation. They can also be referenced from the EJB's deployment descriptor. In either case, the application needs to be modified to specify interceptors.

Another limitation of EJB interceptors is that they only work on EJB methods. When common behavior needs to be added to other interactions, for instance if an EJB invokes something like an outbound resource adapter, a different mechanism must be used.

Interceptors in CAPS

A Java CAPS customer had an interesting case for using interceptors. The customer had hundreds of integration applications that all received and sent JMS messages. If a message cannot be processed, the message is sent to an error handling system using JMSJCA's dead letter queue facility. An operator will monitor the dead letter queue, and may resubmit the message. If a resubmitted message is processed successfully, the error handling system needs to be notified of that fact. One way of adding this behavior would be to add a chunk of code to all MDBs. That would of course lead to a lot of duplicated code. It also leads to mixing this system level behavior with the business logic in the integration applications. This is clearly undesirable.

Because Repository based projects in Java CAPS generate all the MDB code and assembles the applications, it is difficult to make use of EJB interceptors. What is needed is to specify the use of interceptors on a global basis, that is outside of the applications, and preferably for all applications at the same time.

Another requirement is that the customer wanted to add common behavior not just when messages are received, but also when messages were sent from an EJB. Upon sending a message, payload validation should happen. Since EJB interceptors can only be used for EJB methods, this was another reason that EJB interceptors could not be used.

The solution was to add a new feature to JMSJCA to support interceptors.

Interceptors in JMSCA

Interceptors in JMSJCA are invoked not only before / after a message is delivered to the MDB, but also any time a message is sent.

Rather than introducing a new Java interface, which would bring with it the complication of compile time dependencies on JMSJCA jars, the JMSJCA interceptors use the same annotations as in EJB3: any class with a method with the right signature and the @AroundInvoke annotation can serve as an interceptor.

Which classes will JMSJCA consider as interceptors? For this, it uses the service locator mechanism from JDK 6. This is done without introducing dependencies on JDK 6 by the way. With the service locator mechanism, a jar that holds the interceptor should have a special manifest file that lists the class names of the interceptors. The name of this manifest should be META-INF/services/myinterceptor where myinterceptor is the name of the interceptor. This name needs to be specified in the JMSJCA configuration. If no name is specified, the default name jmsjca.interceptor is used. This means that interceptors using the service name jmsjca.interceptor are automatically loaded and invoked if not overridden in the JMSJCA configuration.

With this, it's easy to add interceptors to all applications running in the application server, whenever they send or receive messages through JMSJCA, without having to change any application.

For more information, see http://jmsjca.dev.java.net/ .

Post a Comment