Search code examples
javaglassfishjmsopenmq

Problems with routing message to separate errorQueue


I have a MessageBean which reads from a Queue we'll name MainQ. If the execution of the onMessage code throws a user-based Exception with a type we'll name UserException I want to catch this and put this message on a separate Queue named UserErrorQ. If the exception is not of this type, the Exception is thrown on to be handled by the DMQ.

Here is my issue:

  • in my catch block I attempt, through a ErrorQueueHandler, to put this new message on the UserErrorQ. This results in an error when I attempt to connect to the connectionFactory to send the message to the UserErrorQ.
  • Apparently creating a new connection to a QueueConnectionFactory(javax.jms.ConnectionFactory) is causing problems

Error:

com.sun.messaging.jms.JMSException: MQRA:DCF:allocation failure:createConnection:Error in allocating a connection. Cause: javax.transaction.RollbackException 
at com.sun.messaging.jms.ra.DirectConnectionFactory._allocateConnection(DirectConnectionFactory.java:548)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:265)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:244)`

MessageBean:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
   try{
    .
    .
   }catch(Exception e){
       if(isUserExceptionWrappedInException(e){
           errorQueueHandler.sendToErrorQueue(message);
       }
   }
}

private boolean isUserExceptionWrappedInException(Throwable t) {
    if (t == null)
        return false;
    else if (t instanceof UserException)
        return true;
    else
        return isUserExceptionWrappedInException(t.getCause());
}

ErrorQueueHandler:

public void sendToErrorQueue(Message message) {
    try {
        createConnection();
        send((TextMessage)message);
    } finally {
        closeConnection();
    }
}

private void createConnection() throws Exception {
    try {
        connection = connectionfactory.createConnection();
        connection.start();
    } catch (JMSException e) {
        String msg = "Error while attempting to initialize connection to jms destination " + ERROR_QUEUE;
        throw new OperationalException(msg, e, OperationalExceptionType.APPLIKASJONSTJENER);
    }
}

As mentioned, the error occurs when attempting to make the connection. Anyone have a fix for this?


Solution

  • So, I have figured out the answer to my own question. The reason for the connectionException was that the ErrorQueueHandler was not an EJB, but rather injected via CDI. There are no new instantiations allowed within a rollback state because the container discards the bean instance, which is why it failed. My REQUIRES_NEW annotation was also ignored as this belongs to the javax api, which will not affect a CDI injected bean.

    Here are a few things to note:

    1. Make sure the EJB has either no constructors, or public ones. The modifiers are important as the container needs these to be correct for it to instantiate the EJB.

    2. There are a few problems with this approach.

      • As I am attempting to write the message to a separate error queue instead of the DMQ, I will have to consume the message and not throw the error on afterwards. Because the MDB is in a rollback state, the JMS spec clearly states that this will cause the message to be redelivered. What you will experience is that after writing to you custom errorQueue, the message will bounce right back to the queue and you now have an infinite loop.

    Luckily i also have a solution: The main issue here is controlling your transactions. For this scenario, i need 3 transactions:

    1. One transaction for the MDB so that it is able to acknowledge the message event though i have a RuntimeException.
    2. One transaction for the logic of the onMessage method so that i am able to do a rollback when i get an exception, but also still be able to write to the ErrorQueue.
    3. One transaction for connecting and writing to the ErrorQueue while in a rollback state.

    Code:

    MessageBean:

    @EJB
    QueueService queueService;
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void onMessage(Message message) {
      try{
        queueService.processMessageInNewTrasaction(message);
      }catch(Exception e){
        throw e;
      }
    }
    

    QueueService:

    import javax.jms.Message;
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    @Stateless
    public class QueueService {
    
      @EJB
      ErrorQueueHandler errorQueueHandler;
    
      public void processMessageInNewTransaction(Message message){
        try {
        .
        .
        } catch(Exception e) {
          if(isUserExceptionWrappedInException(e)
            errorQueueHandler.sendToErrorQueue(message);
        }
      }
    
      private boolean isUserExceptionWrappedInException(Throwable t) {
        if (t == null)
          return false;
        else if (t instanceof UserException)
          return true;
        else
          return isUserExceptionWrappedInException(t.getCause());
      }
    
    }
    

    ErrorQueueHandler:

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    @Stateless
    public class ErrorQueueHandler{
       public void sendToErrorQueue(Message message){
       .
       .
       }
    }
    

    useful resource: http://weblogic-wonders.com/weblogic/2011/01/10/working-with-jms-and-the-standard-issues-in-jms/