-
Bug
-
Resolution: Done
-
Major
-
8.0.0.Final
-
None
Section 12.4.4 of the JMS 2.0 spec says:
If a method is called on an injected JMSContext when there is a JTA
transaction (either bean-managed or container-managed), the scope of
the JMSContext will be @TransactionScoped.
This is not currently true in WildFly. As a test case, suppose you have the following two beans:
import javax.annotation.Resource; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.jms.JMSContext; import javax.jms.Topic; import javax.transaction.Transactional; @ApplicationScoped @Transactional public class AppScopedBean { @Inject private JMSContext jmsCtx; //private Instance<JMSContext> jmsCtx; @Resource(lookup = "jms/topic/test") private Topic topic; public void sendMessage() { jmsCtx.createProducer().setDisableMessageID(true).setDisableMessageTimestamp(true).send(topic, "a message"); //jmsCtx.get().createProducer().setDisableMessageID(true).setDisableMessageTimestamp(true).send(topic, "a message"); } }
import javax.annotation.Resource; import javax.enterprise.concurrent.ManagedThreadFactory; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Dependent; import javax.enterprise.context.Initialized; import javax.enterprise.event.Observes; import javax.inject.Inject; @Dependent public class ThreadLauncher { @Resource private ManagedThreadFactory threadFactory; @Inject private AppScopedBean bean; void start(@Observes @Initialized(ApplicationScoped.class) Object event) { System.out.println("starting threads"); for (int i = 0; i < 5; i++) { threadFactory.newThread(new SendRunnable()).start(); } System.out.println("start finished"); } private class SendRunnable implements Runnable { public void run() { System.out.println("starting to send"); for (int i = 0; i < 100; i++) { bean.sendMessage(); } System.out.println("done sending"); } } }
If you deploy and run the above code, you will see a lot of this:
2014-05-12 16:46:02,936 WARN [org.hornetq.core.client] (EE-ManagedThreadFactory-default-Thread-4) HQ212051: Invalid concurrent session usage. Sessions are not supposed to be used by more than one thread concurrently.: java.lang.Exception: trace
That should not be happening if the JMSContext is really @TransactionScoped. The problem appears to be in org.jboss.as.messaging.deployment.JMSContextProducer, specifically the JMSContextWrapper.getDelegate() method. If two threads call it at the same time, the first will create a delegate but the second will receive the same delegate as the first, because the delegate doesn't get nulled until the transaction in the first thread completes.
This means that both threads are using the same session at the same time, which is not allowed. I don't know how HornetQ detects concurrent use, but depending on how that works, it's also possible that messages will end up being part of the wrong transaction.
A workaround is to inject Instance<JMSContext> instead of JMSContext directly (the two commented out lines of code). Since the JMSContext producer is @Dependent, each Instance.get() call will return a new instance, and only one thread will ever use any given instance.