jsr343-experts@jms-spec.java.net

[jsr343-experts] (JMS_SPEC-45) Clarify and improve Connection.createSession

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Tue, 09 Aug 2011 11:35:06 +0100

I have logged the following JIRA issue:
http://java.net/jira/browse/JMS_SPEC-45

This is quite a long description and it may be easier to read the formatted version in the JIRA issue rather than the
text version below. In any case, I would appreciate your comments.

Nigel


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,
{quote}
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.
{quote}

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.

h4.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

where sessionMode may be set to Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, Session.DUPS_OK_ACKNOWLEDGE or
Session.AUTO_TRANSACTED

* The existing createSession will remain with a note that it may be removed from a future release of the API.