Issue Details (XML | Word | Printable)

Key: JMS_SPEC-126
Type: Improvement Improvement
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: Nigel Deakin
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
jms-spec

API to allow app servers to implement JMSContext without needing an additional connection pool

Created: 20/May/13 01:45 PM   Updated: 11/Oct/13 04:45 PM
Component/s: None
Affects Version/s: 2.0
Fix Version/s: None

Time Tracking:
Not Specified

Tags: jms20-bug
Participants: clebertsuconic, John D. Ament and Nigel Deakin


 Description  « Hide

The problem

In JMS 2.0, chapter 11 "JMS application server facilities" defines some optional API which may be used by application servers to allow them to support any compliant JMS provider. Some application servers use this API directly whilst some use this API to implement a portable resource adapter.

Application servers or resource adapters that use this API typically maintain a pool of the JMS provider's Connection objects. To support this, they wrap the JMS provider's ConnectionFactory object so that a call to createConnection does not create a new Connection object but instead fetches one from a pool. They also wrap the JMS provider's Connection object so that a call to the close method does not close the connection but instead simply returns it to the pool.

The introduction of the JMSContext object in JMS 2.0 has complicated matters. Since a JMSContext contains a connection then an obvious approach is to maintain a separate pool of JMSContext objects. In such an implementation, the application server or resource adapter would wrap the JMS provider's ConnectionFactory object so that a call to createContext does not create a new JMSContext object but instead fetches one from a pool. It also would wrap the JMS provider's JMSContext object so that a call to the close method does not close the JMSContext but instead simply returns it to the pool.

However this approach requires the application server or resource adapter to maintain two separate pools of objects: a pool of Connection objects and a separate pool of JMSContext objects. This means that for every JMS application, in addition to configuring a Connection pool the user would also need to configure a JMSContext pool. This would be more complicated to administer. It would also be inefficient since a Connection that is released to the pool by a call to connection.close() could only be reused by an application that subsequently calls createConnection to create a Connection. It could not be reused by an application that subsequently called createContext to create a JMSContext.

The solution

This complication can be avoided by defining a new method which allows a JMSContext to be created using a connection that was fetched from the normal connection pool. As with the the existing methods for application server integration, implementing this method would be optional.

New method Connection#createContextForEE(Session s)

JMS providers would be required to implement a new method on Connection with the following signature:
JMSContext createContextForEE(Session session);

This method would simply create a new JMSContext using the specified connection and session.

This method would be for application server or resource adapter use only. The name createContextForEE is intended to discourage naive users from using it accidentally. The application server or resource adapter's implementation of the createContextForEE method on Connection would be encouraged to always throw an exception.

This new method would be used by the application server or resource adapter's ConnectionFactory implementation of createContext as follows:

1. Calls this.createConnection() to create a RA-implemented Connection which wraps a provider-implemented Connection. This would typically fetch the provider-implemented Connection from a pool.

2. Calls the RA-implemented Connection's createSession(sessionMode) method to create a RA-implemented Session which wraps a provider-implemented Session. If the RA-implemented Connection's createSession(sessionMode) method enforced the requirement that you can only create one session on a connection in a Java EE web or EJB container, then this check would be applied here. This also allows the RA-implemented Connection's createSession method to cache the provider-implemented Session if it wants to

3. Calls the provider-implemented Connection's createContext(connection,session) method (the new method we're defining here) to create the provider-implemented {{JMSContext }}object

4. Wraps the provider-implemented JMSContext object in a RA-implemented JMSContext

5. Returns the RA-implemented JMSContext to the application

The RA's JMSContext implementation would be a proxy for the JMS provider's JMSContext implementation and could delegate almost all behaviour to it. The exception to this would be the close method. This needs to be overridden so that instead of closing the session and connection it does whatever behaviour is already implemented when a wrapped session and connection is closed. So the RA's JMSContext implementation would override close as follows:

1. Calls the provider-implemented JMSContext's closeForEE() method. This is a new method (described below) offering the same behaviour as the close method but without closing the the underlying session and connection.

2. Calls the RA-implemented Session's close method. This can do whatever it does now, such as leaving the session open and cached with the connection.

2. Calls the RA-implemented Connection's close method. This can do whatever it does now, such as leaving the connection open and returning it to a pool.

New method JMSContext#closeForEE()

This method offers the same behaviour as the JMSContext close method but without closing the underlying session and connection.

It is intended to be called from an application server or resource adapter's implementation of the close method,
and so must provide the same behaviour to the application as is defined for the JMSContext close method.

This includes waiting for any receive call or message listener callback to return,
and for any incomplete asynchronous send operations for this JMSContext to be completed, including waiting for any CompletionListener callbacks to return.

It also includes ensuring that any subsequent call to acknowledge on a message received using this JMSContext causes an IllegalStateException to be thrown.

Again this method would be for application server or resource adapter use only. The name closeForEE is intended to discourage naive users from using it accidentally. The application server or resource adapter's implementation of the closeForEE method on JMSContext would be encouraged to always throw an exception.

It should be seen that this method does more than simply clean up state. It actually need to behave just the same as calling JMSContext#close since that's the method that the application will have called. The same issue applies for application servers and resource adapters that cache the JMS provider's session and connection objects in a pool and provide the application with wrappers around them. These need to be able to implement close without actually closing the object. This suggests that methods similar to closeForEE are needed on Session and Connection as well:

New method Session#closeForEE()

This method offers the same behaviour as the Session close method but without closing the session.
It is intended to be called from an application server or resource adapter's implementation of the close method,
and so must provide the same behaviour to the application as is defined for the Session close method.

This includes waiting for any receive call or message listener callback to return, and for any incomplete asynchronous send operations for this Session to be completed including waiting for any CompletionListener callbacks to return.

It also includes ensuring that any subsequent call to acknowledge on a message received using this Session causes an IllegalStateException to be thrown.

Again this method would be for application server or resource adapter use only. The name closeForEE is intended to discourage naive users from using it accidentally. The application server or resource adapter's implementation of the closeForEE method on JMSContext would be encouraged to always throw an exception.

New method Connection#closeForEE()

This method offers the same behaviour as the Connection close method but without closing the connection.
It is intended to be called from an application server or resource adapter's implementation of the close method,
and so must provide the same behaviour to the application as is defined for the Connection close method.

This includes waiting for any receive call or message listener callback to return, and for any incomplete asynchronous send operations for this Connection to be completed including waiting for any CompletionListener callbacks to return.

It also includes ensuring that any subsequent call to acknowledge on a message received using this Connection causes an IllegalStateException to be thrown.

Again this method would be for application server or resource adapter use only. The name closeForEE is intended to discourage naive users from using it accidentally. The application server or resource adapter's implementation of the closeForEE method on JMSContext would be encouraged to always throw an exception.

Naming considerations

An alternative name for closeForEE could be closeForPooling.

Alternatively some new interfaces could be defined which contain these new methods. This probably makes things too complicated but is included here for discussion.

A new interface PoolableConnection could be defined with two methods createContext(Session s) and closeForPooling. There would be a new method Connection#getPoolableConnection to obtain an instance of this interface from the Connection.

A new interface PoolableJMSContext could be defined with one method {closeForPooling}}. There would be a new method JMSContext#getPoolableJMSContext to obtain an instance of this interface from the JMSContext.

A new interface PoolableSession could be defined with one method {closeForPooling}}. There would be a new method JMSContext#getPoolableSession to obtain an instance of this interface from the Session.



Nigel Deakin added a comment - 20/May/13 02:57 PM - edited

Here's another idea: instead of Connection#createContextForEE(Session s) we could have Session#createContextForEE(). Both give the application server or resource adapter control over the creation of the connection and session.


clebertsuconic added a comment - 12/Sep/13 05:41 PM - edited

Is this really needed? all we care so for is pool the connection. a JMSContext instance should be pretty cheap to just be GCed... the JMSContext is always getting a Connection from the pool, in which case it should either reuse a previous connection or create a new one when needed.

I understand there could be an implementation mistake where this would end up being created every time. But I think it can be fixed using the current spec pretty easily without adding API changes.


clebertsuconic added a comment - 12/Sep/13 05:43 PM

closeForEE.. I don't think that's needed as well? just reference counting and other implementation details can fix that. It seems to be fixable with implementation details rather than with API changes. maybe I'm missing something there?


clebertsuconic added a comment - 12/Sep/13 05:56 PM

Also: with EE you only have one JMSContext. Someone could easily cache the instance on the Connection and just get it from there (if you really want to cache it).

Besides: I feel like javax.resource.spi.ManagedConnectionFactory and proper metadata would be enough to manage this.

I really feel this should be an implementation detail without requiring API changes.


Nigel Deakin added a comment - 11/Oct/13 01:03 PM

These methods are needed to allow an application server to create JMSContext objects from existing pooled Connection (and Session) objects. Currently there is no standard API to do this.

Application servers can achieve this with their own JMS provider by using provider-specific API (that's what GlassFish does now). However if we want an application server to be able to achieve this with an arbitrary third-part JMS provider there needs to be a standard API for this.

There is definitely scope to debate the exact methods needed, but I think two new methods are needed (1) to create a JMSContext from a Connection and Session and (2) to do the reverse.


John D. Ament added a comment - 11/Oct/13 01:09 PM

I personally don't see the point in doing this. Are you trying to say it should be possible for a third party to construct a JMSContext, on behalf of the application server?


Nigel Deakin added a comment - 11/Oct/13 04:45 PM

This feature is proposed to for use in the following use cases:

(1) application servers which support third-party JMS providers directly (i.e. without using a resource adapter provided by the third party)

(2) resource adapters which support third-party JMS providers (i.e. JMS resource adapters which are not tied to a particular JMS provider but work with any JMS provider)

In these cases, when a Java EE 6 application calls connectionFactory.createConnection(), the application server or resource adapter currently intercepts this call and plucks a Connection out of the connection pool.

When a Java EE 7 application calls connectionFactory.createContext(), the application server or resource adapter will need to intercept this call and pluck a JMSContext out of the JMSContext pool. However this means that the application server will need to maintain a pool of JMSContext objects separate from the pool of Connection objects, which is potentially less efficient and gives the user something extra to configure.

This proposal is intended to avoid the need to create a second pool and allows the existing pool of Connection objects to be used both when the application calls createConnection() and when it calls createContext().

When a Java EE application calls connectionFactory.createContext(), the application server or resource adapter should be able to intercept this call, pluck a Connection out of the Connection pool, and then use the API proposed above to create a JMSContext from that Connection. There is no need to maintain a separate pool of JMSContext objects.

(Note that there'll be an opportunity to discuss this more thoroughly at the appropriate time; the comments on this issue make it clear that when that happens I'll need to explain carefully why I think this is a desirable feature).