I have an EJB module that has to synchronously exchange messages with a MDB in another module via TemporaryQueue. The EJB container (in my case it's Glassfish 4.0) assumes transactional environment, do I have to use BEAN-managed transaction and denote start and end of transactions using UserTransaction object.
The outline of my code is like this:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CommunicationJMSUtils {
@Resource
private UserTransaction ut;
@Inject
private JMSContext context;
@Resource(lookup = "jms/DestinationQueue")
private Queue destination;
private static final long JMS_COMMUNICATION_TIMEOUT = 5000;
public Map<String, String> getClientordersData(String id) throws JMSException {
try {
MapMessage mm = context.createMapMessage();
ut.begin();
TemporaryQueue replyQueue = context.createTemporaryQueue();
mm.setJMSReplyTo(replyQueue);
mm.setStringProperty(<...>);
<...>
mm.setString(<...>);
<...>
context.createProducer().send(destination, mm);
ut.commit();
ut.begin();
ObjectMessage om = (ObjectMessage) context.createConsumer(replyQueue).receive(JMS_COMMUNICATION_TIMEOUT);
ut.commit();
if (om != null) {
return om.getBody(Map.class);
} else {
throw new JMSException("Failed to receive reply within " + JMS_COMMUNICATION_TIMEOUT);
}
} catch (NotSupportedException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SecurityException | IllegalStateException | SystemException ex) {
<...>
}
}
}
The first problem is that from time to time this code (receive part) fails with exception
MQJMSRA_DS4001: _checkDestination:Temporary Destination not owned by parent connectionId=1760697170479431168
although evidently TemporaryQueue is created with same JMSContext.
And the second problem is the "fragility" of this code. If I put context.createMapMessage() inside the first transaction or move TemporaryQueue creation out of the first transaction this snippet will definately fail.
Unfortunately, JMS tutorials/documentation do not really cover that particular use-case. What is the correct way to implement the JMS request/response pattern with JMS 2.0 in Java EE?
Synchronous behaviour and (asynchronous) messaging using JMS conflict somehow, as messaging is asynchronous by nature. Instead of using JMS, just call the method, for example on a local or remote EJB: A stateless session bean and an MDB can call the same functionality (service class), but this way they provide two different types of interfaces (sync & async).
You are even creating a temporary queue for calling a method, this does add complexity (and this is causing trouble).
The
UserTransaction
should span all operations. There should be only onebegin
, andcommit
(androllback
). I cannot see a real transaction boundary, resources from the first TX are used in the 2nd TX (replyQueue
). I would expect that the temporary queue was deleted during the firstcommit
.Possibly related: An EJB with
TransactionManagementType.BEAN
acts as a "transaction barrier". It will possibly suspend a current transaction: Why do EJB beans with bean-managed transactions act as a “transacation barrier”? hereSo instead of fixing the transaction handling I suggest adding a stateless session bean which offers the same functionality, and call this one (synchronously). In that case I do not see a reason why this should not work with container managed transactions, and thus it will be really transactional across bean boundaries.