How To Unify Errorhandling in EJB 3 Message Driven Beans

Asynchronous loose coupling requires you to deal with additional type checking in the application code. Just look at the type checking sections:


public void onMessage(Message msg) {
if(msg instanceof ObjectMessage){
try {
	ObjectMessage objectMessage = (ObjectMessage) msg;
	Serializable payload = objectMessage.getObject();

//processing
} catch (JMSException ex) {
//what you are doing here?
}
}else{
//what if it is not an ObjectMessage?
}

Some projects just ignore the second else and log the arrival of a JMS-message. This cause the transaction to complete, commit and the JMS message will just disappear - it gets lost. In other cases the exception will be thrown, what in turn causes the JMS-Provider to resend the message immediately. Dependent on the the configuration of the JMS-server, it could even cause endless looping.

Since the availability of EJB 3.0, you can easily intercept Message Driven Bean - so you can check the type and even the content of a JMS message, before it actually gets delivered at the MDB. This can be done uniquely - in an errorhandling Interceptor.


@Interceptors(MessageTypeCheckInterceptor.class)
public class BookMessageConsumerBean implements MessageListener {
    
    @EJB
    private BookOrderingServiceLocal bookOrderingServiceLocal;

    @ExpectedMessageType(ObjectMessage.class)
    public void onMessage(Message message) {
    //casting, message processing
     }

 

The MessageTypeCheckInterceptor checks the type and re-routes the JMS-Message to e.g. a Dead Letter Queue outside the application code. It searches for the ExpectedMessageType and uses the type inside to compare it with the actual Message type

public class MessageTypeCheckInterceptor {
    
    @AroundInvoke
    public Object audit(InvocationContext invocationContext) throws Exception{
        Method method = invocationContext.getMethod();
        if("onMessage".equals(method.getName())){
            ExpectedMessageType messageType = method.getAnnotation(ExpectedMessageType.class);
            Class expectedType = messageType.value();
            Object messageParameter = messageParameter(invocationContext);
            if(!expectedType.isAssignableFrom(messageParameter.getClass())){
                escalateError(expectedType,messageParameter);
            }
        }
                return invocationContext.proceed();
    }

    private void escalateError(Class expectedType, Object messageParameter) {
 	//handle error uniformly here

   }
    
    private Object messageParameter(InvocationContext context){
        return context.getParameters()[0];
    }

}


You could go even further and cast already the message to the appropriate type inside the interceptor and invoke a type-safe method inside the MDB. Will cover that approach in some of the upcoming posts.

The whole sample was checked-in into: http://kenai.com/projects/javaee-patterns/ (ServiceFacade Pattern, tested with Glassfish v2.1 and Netbeans)

[See the SOA Facade strategy at the Page 62 in the "Real World Java EE Patterns" book for deeper explanation]

Comments:

Do you have any best practices or advices how to test interceptors?

Are Integration-Tests with an embeddable container the way to go? Or is there a more fine-granular testing strategy?

Posted by Jonas Bandi on July 09, 2009 at 11:33 AM CEST #

The redelivery of the JMS message is a good thing. If you had concurrent modification exceptions (optimistic locking) or a DB outage the redelivery mechanism can help to solve the situation.

Coming from the JBoss side I can't believe there are servers out there that do not have a DLQ-mechanism on by default.

If you run into a JMSException you should retry to process that message since you just could have had a non-transparent failover in a clustered environment. JBossMQ does non-transparent failovers. JBossMessaging has transparent failovers AFAIK. On WebLogic the configuration was a big mess, we didn't get it to work.

Posted by Tobias on July 09, 2009 at 12:28 PM CEST #

I don't think giving the JEE "interceptor" solution more publicity is a good idea. Crosscutting concerns should be handling in a crosscutting way and that's something else.

For this problem, a custom annotation (declaring the expected message type as its parameter) and some real interception with aspectj using the annotation as the pointcut would be an elegant solution - strictly IMHO :)

cheers,
Kristof

Posted by Kristof Jozsa on July 09, 2009 at 06:17 PM CEST #

@Kristof,

aspectj would work as well. Interceptor was just good enough for my purposes. It took about 0.5h to write and test the solution. It works now without any additional frameworks...

thanks for your comment!,

adam

Posted by Adam Bien on July 09, 2009 at 10:10 PM CEST #

@Tobias,

Every server I know has DLQ. It is a good thing but the correct configuration highly depends on the operations/administration.

With interceptor you could even re-route the JMS messages directly to a Tivoli emergency management system, opennms, hyperiq etc. DI into interceptors works perfectly as well. You are not limited to DLQ.

thanks!,

adam

Posted by Adam Bien on July 09, 2009 at 10:15 PM CEST #

@Jonas,

its pretty easy, but the comment-field is too short - I will cover it in twitter :-).

...just kidding - in the watch some subsequent posts,

regards,

adam

Posted by Adam Bien on July 09, 2009 at 10:18 PM CEST #

expectedType.isAssignableFrom(messageParameter.getClass())

should be written as

expectedType.isInstance(messageParameter)

Posted by bob on August 16, 2011 at 03:14 AM CEST #

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