How To Unify Errorhandling in EJB 3 Message Driven Beans 📎
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]