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:
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?
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:
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.
There are a few problems with this approach.
Luckily i also have a solution: The main issue here is controlling your transactions. For this scenario, i need 3 transactions:
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/