In the JMS 1.1 specification, the following method on a javax.jms.Connection is used to create a javax.jms.Session:
Session createSession(boolean transacted, int acknowledgeMode) throws JMSException
where transacted may be set to true or false and
acknowledgeMode may be set to Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and Session.DUPS_OK_ACKNOWLEDGE
(There are similar methods on javax.jms.QueueConnection and javax.jms.TopicConnection: this whole issue applies to all three.)
This is a rather confusing method for several reasons:
- It uses two arguments to define a single aspect of the session
- In a Java EE transaction, both arguments are ignored anyway
- In a Java EE unspecified transaction context, the meaning of the arguments is undefined and unclear
It uses two arguments to define the same thing
This method uses two arguments to define what is in practice a single aspect of the session with four possibilities: if transacted is set to false then the session is non-transacted and the acknowledgeMode argument defines which of three kinds of acknowledgement are used when receiving messages. If transacted is set to true then the acknowledgeMode argument is ignored.
This is inconsistent with the method Session.getAcknowledgeMode() which returns one of four values: Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, or Session.DUPS_OK_ACKNOWLEDGE if the session is not transacted and Session.SESSION_TRANSACTED if the session is transacted.
This also leads to code which is potentially misleading, since if transacted is false the user still has to set acknowledgeMode to some value even if it is ignored, which leads to code such as
Session session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
Some developers like to use
Session session = connection.createSession(true,Session.SESSION_TRANSACTED);
though this is still misleading since since if transacted was set to true then the second argument is ignored anyway, and if transacted is false then setting acknowledgeMode to Session.SESSION_TRANSACTED would be an error.
In a Java EE transaction, both arguments are ignored anyway
In a Java EE transaction none of the four options listed above are permitted. Instead, both arguments to connection.createSession are ignored and a global transaction is used.
The EJB 3.1 Specification, section 13.3.5 "use of JMS APIs in Transactions" states that, in a container-managed or bean-managed transaction,
Because the container manages the transactional enlistment of JMS sessions on behalf of a bean, the parameters of the createSession(boolean transacted, int acknowledgeMode) method createQueueSession... are ignored.
This also applies to web applications. The Java EE platform spec, Section EE.6.7 "Java Message Service (JMS) 1.1 Requirements" specifies that
The behavior of a JMS provider should be the same in both the EJB container and the web container." It continues "The EJB specification describes restrictions on the use of JMS in an EJB container, as well as the interaction of JMS with transactions in an EJB container. Applications running in the web container should follow the same restrictions.
Instead, the receiving and sending of messages must be part of the container-managed or bean-managed transaction, and the transaction will be committed or rolled back in the way that container-managed or bean-managed transactions are committed or rolled back.
-
Container-managed transactions are either committed when the appropriate business method completes or are rolled back using EJBContext.setRollbackOnly.
-
Bean-managed transactions are either committed using UserTransaction.commit or rolled back using UserTransaction.rollback.
How explicit is the EJB specification about all this? In addition to specifying that in a transactional context the arguments to Connection.createSession are ignored, it also states the following:
-
Section 13.3.5 states that "within a transaction" the bean should not use the acknowledge method. This therefore covers both container-managed and bean-managed transactions.
-
Section 13.3.3 states that in the case of bean-managed transactions, the bean must not invoke the commit or rollback methods on the javax.jms.Session interface.
-
Section 13.3.4 states that in the case of container-managed transactions, the bean must not "use any resource-managed specific transaction management methods that would interfere with the container's demarcation of transaction boundaries" and again must not invoke the commit or rollback methods on the javax.jms.Session interface.
-
Section 13.1.1 states that in the case of bean-managed transactions, "all resource manager accesses between the UserTransaction.begin and UserTransaction.commit calls are part of a transaction", thereby apparently ruling out the use of non-transacted sessions using auto-acknowledgement and dups-ok-acknowledgement as well as those using client-acknowledgement.
-
Section 13.1.1 also states in a container-managed transaction the transaction demarcation depends on the transaction attributes of the bean method. It doesn't explicitly state that all resource manager accesses should be part of this transaction, but this is implied.
In a Java EE unspecified transaction context, the meaning of the arguments undefined and unclear
The previous section only relates to the use of the JMS API "in transactions". It does not cover how the JMS API should behave when there is no current container-managed or bean-managed transaction. That is, when there is an unspecified transaction context.
The EJB 3.1 Specification, section 13.6.5 "Handling of Methods that run with an unspecified transaction context" defines an "unspecified transaction context" as covering "the cases in which the EJB architecture does not fully define the transaction semantics of an enterprise bean method execution".
Section 13.6.5 goes on to give a list of examples of when an "unspecified transaction context" may arise. All the cases given are for container-managed transactions, leaving an ambiguity about what an "unspecified transaction context" means when using bean-managed transactions. An obvious interpretation is that that if a bean is configured to use bean-managed transactions, then business methods or onMessage() code executed before a call to userTransaction.begin(), or after a call to UserTransaction.commit or UserTransaction.rollback, is executed in an unspecified transaction context, as is any code executed in the four bean lifecycle callback methods listed in 13.6.5 (PostConstruct,PreDestroy, PostActivate, or PrePassivate). However this is not explicitly stated in the EJB spec.
So, what does the EJB spec say should happen in an "unspecified transaction context"?
Section 13.6.5 is written with all resource managers (not just JMS) in mind, and states that
"The EJB specification does not prescribe how the container should manage the execution of a method with an unspecified transaction context—the transaction semantics are left to the container implementation.
It goes on to give some options, which include treating "each call... to a resource manager as a single transaction", merging multiple calls into a single transaction, or accessing the resource manager "without a transaction context".
Now in the case of JMS the application has a way to give the container a hint as to what behaviour they desire: the arguments to {[createSession}}. So it would seem reasonable to follow these.
However the EJB 3.1 specification, section 13.3.5 does explicitly state that "The Bean Provider should not use the JMS acknowledge method either within a transaction or within an unspecified transaction context. Message acknowledgment in an unspecified transaction context is handled by the container.
It is curious that although Session.acknowledge is prohibited in a unspecified transaction context, Session.commit is not, even though both perform message acknowledgement. If the former is invalid, then the latter must be as well.
This means that within an unspecified transaction context:
- a non-transacted session using client acknowledgement is explicitly prohibited
- a (local) transacted session is implicitly prohibited
- a non-transacted session is permitted, with both client-acknowledgement and dups-ok-acknowledgement (which is an optimised version of auto-acknowledgement) allowed.
Summary
So in Connection.createSession (and QueueConnection.createQueueSession and TopicConnection.createTopicSession we have a method which offers different options depending on the context in which it is used:
-
In a Java EE transaction there are no options: the session is part of a transaction managed by the container and the application has no choice on the matter.
-
In a Java EE unspecified transaction context there are two options:
- non-transacted session with auto-acknowledgement
- non-transacted session with dups-ok-acknowledgement
-
In a Java SE environment there are four options:
- non-transacted session with auto-acknowledgement
- non-transacted session with dups-ok-acknowledgement
- non-transacted session with client-acknowledgement
- transacted session
So, in the light of all this, what are the problems?
-
the special behaviour of this method in a Java EE transaction is not mentioned anywhere in the JMS specification or in the javadocs, which means that users are surprised when they discover that the arguments to createSession are ignored. Fortunately, however, the required behaviour is clearly defined in the EJB specification.
-
the special behaviour in a Java EE unspecified transaction context is also not mentioned anywhere in the JMS specification or in the javadocs. Unfortunately, the required behaviour is not explicitly described in the EJB specification but has to be pieced together from various different sections, as in the analysis above. This needs to be confirmed and stated explicitly.
-
the actual API for createSession, with its two arguments, not only does not reflect the four options available in the normal Java SE case, it does not reflect the zero options available in the Java EE transaction case or the two options available in the Java EE unspecified transaction context case. However when compared the the two preceding issues this is perhaps not such a major issue.
Proposals
It is proposed that
-
The JMS specification and javadocs be updated to describe how createSession behaves in a Java EE applicaiton, both in a transaction and in an unspecified transaction context. The former case will be a restatement of the existing EJB spec, the latter case will be intended to remove any ambiguities in the EJB spec along the lines of the analysis above.
-
A new method be provided in a javax.jms.Connection
Session createSession(int sessionMode) throws JMSException
In a normal Java SE environment sessionMode may be set to
Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, Session.DUPS_OK_ACKNOWLEDGE
or Session.TRANSACTED
In a Java EE transaction, sessionMode is ignored.
In a Java EE undefined transaction context, sessionMode may be set to
Session.AUTO_ACKNOWLEDGE or Session.DUPS_OK_ACKNOWLEDGE only.
- A further new method be provided in a javax.jms.Connection
Session createSession() throws JMSException
This method is particularly intended for use in a Java EE environment,
though it may also be used in a normal Java SE environment.
In a normal Java SE environmentm this is equivalent to calling createSession(Session.AUTO_ACKNOWLEDGE)
In a Java EE transaction, the session will be part of a transaction managed by the container.
In a Java EE undefined transaction context, the session will have a sessionMode of Session.AUTO_ACKNOWLEDGE.
- The existing method createSession(boolean transacted,int acknowledgeMode will remain with a note added to encourage applications to use the other two methods. In accordance with Java EE policy it will remain in the API indefinitely, and will not be formally marked as @deprecated.