Ok, here is the same example with some more details/comments. There was a mistake in my original explanation and I apoligize for confusion.

BeanA allocates a connection
- TxConnectionManager.getManagedConnection allocates
new managed connection mc1 of type
org.jboss.resource.adapter.jdbc.xa.XAManagedConnection which
is a wrapper for (oracle) xa connection xaconn1.
- TxConnectionManager.registerConnectionEventListener(mc1)
creates new TxConnectionEventListener and calls
mc1.addConnectionEventListener
- mc1 gets enlisted into the transaction,
TxConnectionEventListener.enlist is called and mc1 is added
into txToManagedConnectionMap
- mc1.getConnection() is called to get sql connection, it
gets conn1 through xaconn1.getConnection()
BeanA calls BeanB
- nothing special here
BeanB allocates a connection and gets instance conn2 from the same xaconn1
- TxConnectionManager.getManagedConnection gets mc1 from
txToManagedConnectionMap
- mc1.getConnection() is called to get sql connection, it
gets conn2 through xaconn1.getConnection() and implicitly
invalidates conn1 held by BeanA (see quote below)
BeanB closes the connection and returns
- nothing special
BeanA tries to use the connection
- SQLException("Logical handle no longer valid").

A quote from JDBC 2.0 stdext, section 6.2.3 "Retrieving a Connection":

<quote>
An individual PooledConnection object will usually be asked to produce a series of Connection objects during its lifetime. Only one of these Connection objects may be open at a particular point in time. The connection pooling implementation must call PooledConnection.getConnection() each time a new reference to a pooledConnection is handed out to the JDBC application layer. Each call to PooledConnection.getConnection() must return a newly constructed Connection object that exhibits the default Connection behavior. Only the most recent Connection object produced from a particular PooledConnection is open. An existing Connection object is automatically closed, if the getConnection() method of its associated Pooled-
Connection is called again, before it has been explicitly closed by the application. This gives the application server a way to ‘take away’ a Connection from the application if it wishes, and give it out to someone else. This capability will not likely be used frequently in practice.
</quote>

Attached is a proposed fix. Hope this makes the problem clearer.

David Jencks wrote:
I don't understand.  Can you repeat your explanation of the problem clearly
indicating what is a ManagedConnection and what is a Connection?  From your
description it looks like perhaps the problem is that we are reusing the
java.sql.Connection obtained from a XAConnection?  This would not be a
problem with the trackConnectionByTx logic but with the xa wrapper.

thanks
david jencks


On 2002.11.01 08:12:05 -0500 Igor Fedorenko wrote:

Hi,

There is a problem with trackConnectionByTx in the current CVS version
of JBoss 3.2 which occurs when two beans access the same datasource
from the same transaction. I am not 100% sure about original implementation intends and would like to give others an opportunity to complain before I check in updated trackConnectionByTx logic.
Consider the following example

- BeanA allocates a connection and gets an instance conn1
- BeanA calls BeanB
- BeanB allocates a connection and gets the same instance conn1
- BeanB closes the connection and returns
- BeanA tries to use the connection and gets SQLException("Logical
handle is closed").

Updated trackConnectionByTx logic will return the
same connection only if the connection has not been allocated yet
(condition
"BaseConnectionManager2.ConnectionListener.isManagedConnectionFree()");
otherwise allocate and return new connection.

Any objections?
--
Igor Fedorenko
Think smart. Think automated. Think Dynamics.
www.thinkdynamics.com
Index: TxConnectionManager.java
===================================================================
RCS file: 
/cvsroot/jboss/jbosscx/src/main/org/jboss/resource/connectionmanager/TxConnectionManager.java,v
retrieving revision 1.2.2.1
diff -u -r1.2.2.1 TxConnectionManager.java
--- TxConnectionManager.java    12 Sep 2002 01:23:46 -0000      1.2.2.1
+++ TxConnectionManager.java    1 Nov 2002 16:06:11 -0000
@@ -13,13 +13,13 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.management.ObjectName;
+
 import javax.naming.InitialContext;
 import javax.resource.ResourceException;
 import javax.resource.spi.ConnectionEvent;
-import javax.resource.spi.ConnectionEventListener;
 import javax.resource.spi.ConnectionRequestInfo;
 import javax.resource.spi.ManagedConnection;
 import javax.resource.spi.ManagedConnectionFactory;
@@ -34,8 +34,6 @@
 import javax.transaction.xa.XAResource;
 import javax.transaction.xa.Xid;
 
-import org.jboss.system.Registry;
-
 import org.jboss.logging.Logger;
 
 /**
@@ -129,6 +127,7 @@
 
    private boolean localTransactions;
 
+   // maps tx => List of connections
    private final Map txToManagedConnectionMap = new HashMap();
 
    /**
@@ -261,8 +260,25 @@
             ManagedConnection mc = null;
             synchronized (txToManagedConnectionMap)
             {
-               mc = (ManagedConnection)txToManagedConnectionMap.get(tx);
+               List conns = (List) txToManagedConnectionMap.get(tx);
+               if( conns != null )
+               {
+                  Iterator i = conns.iterator();
+                  while( i.hasNext() )
+                  {
+                     ManagedConnection conn = (ManagedConnection) i.next();
+                     ConnectionListener listener = getConnectionEventListener(conn);
+                     if( listener != null && listener instanceof 
+TxConnectionEventListener
+                         && ((TxConnectionEventListener) 
+listener)._isManagedConnectionFree() )
+                     {
+                        mc = conn;
+                        break;
+                     }
+                  }
+               }
+//               mc = (ManagedConnection)txToManagedConnectionMap.get(tx);
             }
+            // make sure that connection is not allocated by the client
             if (mc != null) 
             {
                if (log.isTraceEnabled()) 
@@ -443,7 +459,13 @@
                //Here we actually track the cx by tx.
                synchronized(txToManagedConnectionMap)
                {
-                  txToManagedConnectionMap.put(currentTx, 
this.getManagedConnection());
+                  List conns = (List) txToManagedConnectionMap.get(currentTx);
+                  if( conns == null )
+                  {
+                     conns = new ArrayList();
+                     txToManagedConnectionMap.put(currentTx, conns);
+                  }
+                  conns.add(this.getManagedConnection());
                }
                
             } // end of if ()
@@ -576,7 +598,15 @@
          {
             synchronized(txToManagedConnectionMap)
             {
-               txToManagedConnectionMap.remove(currentTx);
+               List conns = (List) txToManagedConnectionMap.get(currentTx);
+               if( conns != null )
+               {
+                  conns.remove(ce.getConnectionHandle());
+                  if( conns.isEmpty() )
+                  {
+                     txToManagedConnectionMap.remove(currentTx);
+                  }
+               }
             }
          }
          currentTx = null;
@@ -595,6 +625,10 @@
          {
             return false;
          } // end of if ()
+         return super.isManagedConnectionFree();
+      }
+      boolean _isManagedConnectionFree()
+      {
          return super.isManagedConnectionFree();
       }
 

Reply via email to