JMS request/response pattern in transactional environment

自古美人都是妖i 提交于 2019-12-11 09:04:20

问题


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 one begin, and commit (and rollback). 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 first commit.

  • 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!