Search code examples
javajakarta-eeglassfishjmsmessage-driven-bean

JMS TransactionRolledbackLocalException leads to GlassFish breakdown


My basic problem:
the MessageDriven bean throws an Exception somewhere, which leads to a rollback in the JMS Queue until it just blows up and the server breaks down. I can't even look for the Exceptions to solve the problem because the server-logs grow to a few hundred MB in only a few Minutes.

What I am looking for:
a way to first clear the JMS Queue and then stop the rollbacks once and for all.

This is what I do:


   the object         stateless bean to         Message-Driven Bean     here it throws
 I want to send     send the ObjectMessage     to receive the message   the Exception
  +-----------+      +-----------------+        +-----------------+     +----------+
  |  MyObject |----->| MessageProducer |------->| MessageReceiver |---->| do stuff |
  +-----------+      +-----------------+        +-----------------+     +----------+  

The object I send is a simple serializable POJO.

The MessageProducer looks like this:

@Stateless
public class MessageProducer{

    @Resource(mappedName = "jms/JMSConnectionFactory")
    private ConnectionFactory connectionFactory;

    @Resource(mappedName = "jms/MessageReceiver")
    Queue messageReceiver;

    public void sendMessage(PostContainer postContainer){          
        MessageProducer messageProducer;
        // try with resources to close everything on Exception
        try(Connection connection = connectionFactory.createConnection(); 
               Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)){
            messageProducer = session.createProducer(messageReceiver);

            ObjectMessage objectMessage = session.createObjectMessage();
            objectMessage.setObject(myObject);
            objectMessage.setJMSRedelivered(false);  // this doesn't seem to have any effect  
            messageProducer.send(objectMessage);
            messageProducer.close();
        }catch(Exception ex){
            System.out.err("error when sendingMessage .. ignoring"); // when "ignoring" the Exception I thought to at least save me the enormous log messages
        }
    }
}

The message bean looks like this:

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/MessageReceiver")
})
public class MessageReceiver implements MessageListener{

    public MessageReceiver(){
    }

    @Override
    public void onMessage(Message message){
        try{
            if(message instanceof ObjectMessage){
                ObjectMessage objectMessage = (ObjectMessage) message;
                MyObject myObject = (MyObject) objectMessage.getObject();
                doStuff(myObject); // the method that handles the incoming object - here the Exception gets thrown
            }
        }catch(Exception ex){
           System.err.println("message could not be received: " + ex.getMessage());  // still trying to ignore exceptions here
        }
    }
}

Some part of the Exception

javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean
    at com.sun.ejb.containers.EJBContainerTransactionManager.checkExceptionClientTx(EJBContainerTransactionManager.java:662)
    at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:507)
    at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4566)
    ......... (and so on....)
    at com.me.MessageBeans.MessageReceiver.doStuff(MessageReceiver.java:86)
    at com.me.MessageBeans.MessageReceiver.onMessage(MessageReceiver.java:61)
    at sun.reflect.GeneratedMethodAccessor313.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
    at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
    at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4786)
    at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:656)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:608)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
    at org.jboss.weld.ejb.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:52)
    at sun.reflect.GeneratedMethodAccessor310.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
    ......... (and so on....)
Caused by: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.

 // I try to persist myObject with JPA into the database (that's what's not working right now.. but it's beside the point here
    ......... (and so on....)

Now my actual question

:)
How can I stop my JMS Queue to redeliver messages (that cause any Exceptions or could not be delivered successfully for any other reason) ? (e.g catching the exceptions, setting some parameters when sending the message...)

Is there a parameter/option I have to set in GlassFish when setting up the DestinationResources or ConnectionFactories ?

And how could I clear the Queue manually, because right now it's full with "redeliver messages" and every time I deploy my application I get flooded with log messages within minutes.

Thank you all for your time and for reading :)


Solution

  • Transaction management in MDBs is tricky.

    What is happening is that a java.lang.RuntimeException is being thrown from your JPA Facade service method and this automatically marks the current transaction for rollback. Therefore it's already too late by the time it reaches the catch clause in your MDB. JMS will retry failed transactions.

    Any java.lang.RuntimeException escaping from your service method will mark the MDB's transaction for rollback. You prevent this by catching it in the service method.

    You may need to annotate the service method in your JPA Facade with @ TransactionAttribute(REQUIRES_NEW) in case JPA marks the Tx (not sure about that) or another EJB call from within the service method fails.

    You can follow the instructions at To Purge Messages From a Physical Destination to clear your queue.