adam bien's blog

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]