问题
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?
回答1:
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”? here
So 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.
来源:https://stackoverflow.com/questions/18464499/jms-request-response-pattern-in-transactional-environment