Connection Enlistment Strategy in GlassFish

THIS IS STILL A DRAFT. SOME CONTENT MAY BE CHANGED

Transaction Scenarios.

In a Java EE application, transactions can multiple components. The connections used
in the transaction must be automatically enlisted in the transaction by the containers.
The good practice for getting using a connection (be it a JDBC, JMS or a connector connection)
is to obtain the connection as late as possible and return the connection to the pool as
early as possible. In the pseudo code, it will translate to.

ut1.begin();

Connection con1 = ds.getConnection()
con1.createStatement().execute();

Connection con2 = ds.getConnection();
con2.createStatement().execute();

ut1.commit();

The datasource used in the above code snippet is a wrapper above a JDBC driver. So, whenever
a getConnection happens, appserver runtime know that it need to be enlisted in the transaction. Note that when con2 is obtained, if ds is wrapping a non-XA datasource, the physical connection used by con1 and con2 will be shared by them.

Now, lets take a little more complex scenario.

Connection con1 = ds.getConnection()
Connection con2 = ds.getConnection();

ut2.begin();

con1.createStatement().execute();
con2.createStatement().execute();

ut2.commit();

In ut2, the connections are obtained outside the transaction boundary and is used within the transaction.
This case can happen, typically, in a stateful session bean where connections can be obtained in the ejbCreate/ejbActivate method and they are used in business methods.

Now, for enlisting such connections, glassfish server runtime does the following.

Whenever a connection is obtained, it will be added to a housekeeping datastructure. There is one such datastructure per component. I.e a stateful session bean will have such a datastructure. Whenever transaction starts in the component, the container (EJB container) will make sure that all the connections
in the component's resource list is enlisted in the transaction.

Component Sharing and its resource list.

The object instance (actual EJB bean) doesnt get shared between two transactions. This is because

  • Enterprise Java Bean Instances cannot be shared between the transactions. There will be only one thread accessing an EJB instance at a time.
  • Servlets need to make sure that the connection need to be obtained within doPost or doGet methods as per the Java EE spec (section 4.2.3). So, the resource list is maintained per component per thread. i.e resource list is not shared between the threads or transactions. This means that the connection obtained in the init() method of the servlet cannot be used in a transaction started later on, even if the access to the connection is synchronized. So, for servlets, the resource list is not maintained per component.

So, what is the problem?

If a session bean A that started the transaction uses a CMP bean. If the CMP bean (The persistence manager behind the bean) obtain a connection in the beforeCompletion method of the transaction synchronization listener, in the absence of __pm, the connection will be associated with the session bean as the beforeCompletion will get executed after the end of CMP method. If the persistence manager does not close the connection in that transaction and reuse it later on for some other CMP, the connection will be available both to the CMP and to the session bean. This can create interesting concurrency issues.

Allow-NonComponent-Callers

If your code need to handle the above case, then two things are required.

1. Make sure that you always get the connection within the transaction and return the connection as soon as you do the database/message queue operation.

2. Set the Allow-NonComponent-Callers property in the pool.

What does it do?

Appserver containers wouldnt keep the connection in the component's resource list, if the connection is obtained from a pool where this property is set. This property is equivalent to the __pm datasource used in older versions of sun application server.

Since the connection is not in the component's resource list, there is no concurrency issue. However you cannot expect the connection obtained outside the transaction to be enlisted.

Implementation details

When ever a ConnectionFactory (DataSource) is looked up, the JNDI objectfactory will create a new object. The name of the datasource is available to the ObjectFactory. This ObjectFactory creates an appropriate ConnectionManager and saves the name of the datasource in that. From then onwards, whenever ConnectionManager.allocateConnection is executed, the JNDI name is available to the ConnectionManager. The JNDI name is used by the ConnectionManager to get the default resource principal configured in the deployment descriptor, obtain the resource sharing scope etc. It then passes the JNDI name to PoolManager.

Important classes.

The PoolManagerImpl
when it communicate with the transaction manager for tx related activities, use either ResourceManagerImpl or SystemResourceManagerImpl depending on whether the getConnection request is from the JNDI name suffixed with __pm or not. SystemResourceManagerImpl skip everything for component association/dissociation during enlistment/delistment. EJB and webcontainers use InvocationManagerImpl
for registering/deregistering the component invocation information before and after each and every invocation. Look at StatefulSessionContainer for an example.

Could the implementation have taken another path?

If a resource adapter supports lazy connection association and lazy connection enlistment, then the connection can be enlisted in the transaction when it is actually used. This will really remove the need for component's resource list. To achieve this, JDBC and JMS resource adapters used in appserver need to support this feature as well. For JDBC resource adapter it will be required to wrap a number of JDBC objects like connection, statement etc. This is something for the later versions of appserver.

TODO: Look at how other appserver handle this issue.