commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pste...@apache.org
Subject svn commit: r553511 [1/2] - in /jakarta/commons/proper/dbcp/trunk: ./ src/java/org/apache/commons/dbcp/managed/ src/test/org/apache/commons/dbcp/ src/test/org/apache/commons/dbcp/managed/ xdocs/
Date Thu, 05 Jul 2007 14:12:49 GMT
Author: psteitz
Date: Thu Jul  5 07:12:47 2007
New Revision: 553511

URL: http://svn.apache.org/viewvc?view=rev&rev=553511
Log:
Added support for pooling managed connections.
JIRA: DBCP-228
Patch provided by Dain Sundstrom.

Added:
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/LocalXAConnectionFactory.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedConnection.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedDataSource.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContext.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContextListener.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java
    jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/XAConnectionFactory.java
    jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterCallableStatement.java
    jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/
    jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSource.java
    jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSourceInTx.java
Modified:
    jakarta/commons/proper/dbcp/trunk/build.properties.sample
    jakarta/commons/proper/dbcp/trunk/build.xml
    jakarta/commons/proper/dbcp/trunk/pom.xml
    jakarta/commons/proper/dbcp/trunk/project.properties
    jakarta/commons/proper/dbcp/trunk/project.xml
    jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterConnection.java
    jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml

Modified: jakarta/commons/proper/dbcp/trunk/build.properties.sample
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/build.properties.sample?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/build.properties.sample (original)
+++ jakarta/commons/proper/dbcp/trunk/build.properties.sample Thu Jul  5 07:12:47 2007
@@ -50,3 +50,7 @@
 # Commons logging - dependency of naming jars
 logging.home=${repository}/commons-logging/jars
 logging.jar=${logging.home}/commons-logging-1.0.4.jar
+
+# JTA - needed for managed connections
+jta-spec.jar=${repository}/org.apache.geronimo.specs/jars/geronimo-jta_1.1_spec-1.1.jar
+jta-impl.jar=${repository}/org.apache.geronimo.modules/jars/geronimo-transaction-1.2-beta.jar 

Modified: jakarta/commons/proper/dbcp/trunk/build.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/build.xml?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/build.xml (original)
+++ jakarta/commons/proper/dbcp/trunk/build.xml Thu Jul  5 07:12:47 2007
@@ -46,7 +46,7 @@
       <property name="classpath" 
        value="${cp}:${commons-pool.jar}:${jdbc20ext.jar}:${junit.jar}:
         ${naming-common.jar}:${naming-java.jar}:${commons-logging.jar}:
-      	${xerces.jar}:${xml-apis.jar}"/>
+      	${xerces.jar}:${xml-apis.jar}:${jta-spec.jar}:${jta-impl.jar}"/>
 
       <property name="name" value="commons-dbcp"/>
       <property name="title" value="Jakarta Commons Database Pooling Package"/>

Modified: jakarta/commons/proper/dbcp/trunk/pom.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/pom.xml?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/pom.xml (original)
+++ jakarta/commons/proper/dbcp/trunk/pom.xml Thu Jul  5 07:12:47 2007
@@ -122,6 +122,10 @@
     <contributor>
       <name>Wayne Woodfield</name>
     </contributor>
+    <contributor>
+      <name>Dain Sundstrom</name>
+      <email>dain@apache.org</email>
+    </contributor>
   </contributors>
 
   <dependencies>
@@ -138,6 +142,14 @@
       <scope>test</scope>
     </dependency>
 
+    <!-- For managed connections -->
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jta_1.1_spec</artifactId>
+       <version>1.1</version>
+       <optional>true</optional>
+    </dependency>
+
     <!-- tomcat naming jars for jndi reference tests -->
     <dependency>
       <groupId>tomcat</groupId>
@@ -160,7 +172,15 @@
        <version>1.0.4</version>
        <scope>test</scope>
     </dependency>
-    
+
+    <!-- for testing of managed connections -->
+    <dependency>
+      <groupId>org.apache.geronimo.modules</groupId>
+      <artifactId>geronimo-transaction</artifactId>
+       <version>1.2-beta</version>
+       <scope>test</scope>
+    </dependency>
+
   </dependencies>
 
   <build>
@@ -215,6 +235,9 @@
                 <include>org/apache/commons/dbcp/datasources/TestFactory.java</include>
                 <include>org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java</include>
                 <include>org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java</include>
+
+                <include>org/apache/commons/dbcp/managed/TestManagedDataSource.java</include>
+                <include>org/apache/commons/dbcp/managed/TestManagedDataSourceInTx.java</include>
               </includes>
           </configuration>
         </plugin>

Modified: jakarta/commons/proper/dbcp/trunk/project.properties
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/project.properties?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/project.properties (original)
+++ jakarta/commons/proper/dbcp/trunk/project.properties Thu Jul  5 07:12:47 2007
@@ -23,16 +23,15 @@
 maven.changes.issue.template=http://issues.apache.org/jira/browse/%ISSUE%
 
 maven.javadoc.author=false
-maven.javadoc.links=http://java.sun.com/j2se/1.5.0/docs/api,http://jakarta.apache.org/commons/pool/api-1.3
-
+maven.javadoc.links=http://java.sun.com/j2se/1.5.0/docs/api,http://jakarta.apache.org/commons/pool/api-1.3,http://java.sun.com/j2ee/sdk_1.3/techdocs/api
 maven.xdoc.date=bottom
 maven.xdoc.poweredby.image=maven-feather.png
 maven.xdoc.version=${pom.currentVersion}
 maven.xdoc.developmentProcessUrl=http://jakarta.apache.org/commons/charter.html
 
 # JDK level
-maven.compile.source=1.3
-maven.compile.target=1.3
+maven.compile.source=1.4
+maven.compile.target=1.4
 
 # Merge in a file containing just Built-By attribute
 maven.jar.manifest=${basedir}/manifestMods.txt

Modified: jakarta/commons/proper/dbcp/trunk/project.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/project.xml?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/project.xml (original)
+++ jakarta/commons/proper/dbcp/trunk/project.xml Thu Jul  5 07:12:47 2007
@@ -192,6 +192,24 @@
         <scope>test</scope>
       </properties>
     </dependency>
+    
+    <!-- JTA spec jar for managed connections -->
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jta_1.1_spec</artifactId>
+      <version>1.1</version>
+    </dependency> 
+    
+    <!-- For testing managed connections-->
+    <dependency>
+      <groupId>org.apache.geronimo.modules</groupId>
+      <artifactId>geronimo-transaction</artifactId>
+      <version>1.2-beta</version>
+      <properties>
+        <scope>test</scope>
+        <comment>Only required for managed connections tests</comment>
+      </properties>
+    </dependency>
 
     <!-- tomcat naming jars for jndi reference tests -->
     <dependency>

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,69 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import javax.sql.XAConnection;
+import javax.sql.XADataSource;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class DataSourceXAConnectionFactory implements XAConnectionFactory {
+    protected TransactionRegistry transactionRegistry;
+    protected XADataSource xaDataSource;
+
+    /**
+     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database
+     * connections.  The connections are enlisted into transactions using the specified transaction manager.
+     *
+     * @param transactionManager the transaction manager in which connections will be enlisted
+     * @param xaDataSource the data source from which connections will be retrieved
+     */
+    public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource) {
+        if (transactionManager == null) throw new NullPointerException("transactionManager is null");
+        if (xaDataSource == null) throw new NullPointerException("xaDataSource is null");
+
+        this.transactionRegistry = new TransactionRegistry(transactionManager);
+        this.xaDataSource = xaDataSource;
+    }
+
+    public TransactionRegistry getTransactionRegistry() {
+        return transactionRegistry;
+    }
+
+    public Connection createConnection() throws SQLException {
+        // create a new XAConection
+        XAConnection xaConnection = xaDataSource.getXAConnection();
+
+        // get the real connection and XAResource from the connection
+        Connection connection = xaConnection.getConnection();
+        XAResource xaResource = xaConnection.getXAResource();
+
+        // register the xa resource for the connection
+        transactionRegistry.registerConnection(connection, xaResource);
+
+        return connection;
+    }
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/LocalXAConnectionFactory.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/LocalXAConnectionFactory.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/LocalXAConnectionFactory.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/LocalXAConnectionFactory.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,298 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import org.apache.commons.dbcp.ConnectionFactory;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * An implementation of XAConnectionFactory which manages non-XA connections in XA transactions.  A non-XA connection
+ * commits and rolls back as part of the XA transaction, but is not recoverable since the connection does not implement
+ * the 2-phase protocol.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class LocalXAConnectionFactory implements XAConnectionFactory {
+    protected TransactionRegistry transactionRegistry;
+    protected ConnectionFactory connectionFactory;
+
+    /**
+     * Creates an LocalXAConnectionFactory which uses the specified connection factory to create database
+     * connections.  The connections are enlisted into transactions using the specified transaction manager.
+     *
+     * @param transactionManager the transaction manager in which connections will be enlisted
+     * @param connectionFactory  the connection factory from which connections will be retrieved
+     */
+    public LocalXAConnectionFactory(TransactionManager transactionManager, ConnectionFactory connectionFactory) {
+        if (transactionManager == null) throw new NullPointerException("transactionManager is null");
+        if (connectionFactory == null) throw new NullPointerException("connectionFactory is null");
+
+        this.transactionRegistry = new TransactionRegistry(transactionManager);
+        this.connectionFactory = connectionFactory;
+    }
+
+    public TransactionRegistry getTransactionRegistry() {
+        return transactionRegistry;
+    }
+
+    public Connection createConnection() throws SQLException {
+        // create a new conection
+        Connection connection = connectionFactory.createConnection();
+
+        // create a XAResource to manage the connection during XA transacitons
+        XAResource xaResource = new LocalXAResource(connection);
+
+        // register the xa resource for the connection
+        transactionRegistry.registerConnection(connection, xaResource);
+
+        return connection;
+    }
+
+    /**
+     * LocalXAResource is a fake XAResource for non-XA connections.  When a transaction is started
+     * the connection auto-commit is turned off.  When the connection is committed or rolled back,
+     * the commit or rollback method is called on the connection and then the original auto-commit
+     * value is restored.
+     * </p>
+     * The LocalXAResource also respects the connection read-only setting.  If the connection is
+     * read-only the commit method will not be called, and the prepare method returns the XA_RDONLY.
+     * </p>
+     * It is assumed that the wrapper around a managed connection disables the setAutoCommit(),
+     * commit(), rollback() and setReadOnly() methods while a transaction is in progress.
+     */
+    protected static class LocalXAResource implements XAResource {
+        private final Connection connection;
+        private Xid xid;
+        private boolean originalAutoCommit;
+
+        public LocalXAResource(Connection localTransaction) {
+            this.connection = localTransaction;
+        }
+
+        /**
+         * Gets the current xid of the transaction branch associated with this XAResource.
+         *
+         * @return the current xid of the transaction branch associated with this XAResource.
+         */
+        public synchronized Xid getXid() {
+            return xid;
+        }
+
+        /**
+         * Signals that a the connection has been enrolled in a transaction.  This method saves off the
+         * current auto commit flag, and then disables auto commit.  The original auto commit setting is
+         * restored when the transaction completes.
+         *
+         * @param xid  the id of the transaction branch for this connection
+         * @param flag either XAResource.TMNOFLAGS or XAResource.TMRESUME
+         * @throws XAException if the connection is already elisted in another tranction, or if auto-commit
+         *                     could not be disabled
+         */
+        public synchronized void start(Xid xid, int flag) throws XAException {
+            if (flag == XAResource.TMNOFLAGS) {
+                // first time in this transaction
+
+                // make sure we aren't already in another tx
+                if (this.xid != null) {
+                    throw new XAException("Already enlisted in another transaction with xid " + xid);
+                }
+
+                // save off the current auto commit flag so it can be restored after the transaction completes
+                try {
+                    originalAutoCommit = connection.getAutoCommit();
+                } catch (SQLException ignored) {
+                    // no big deal, just assume it was off
+                    originalAutoCommit = true;
+                }
+
+                // update the auto commit flag
+                try {
+                    connection.setAutoCommit(false);
+                } catch (SQLException e) {
+                    throw (XAException) new XAException("Count not turn off auto commit for a XA transaction").initCause(e);
+                }
+
+                this.xid = xid;
+            } else if (flag == XAResource.TMRESUME) {
+                if (xid != this.xid) {
+                    throw new XAException("Attempting to resume in different transaction: expected " + this.xid + ", but was " + xid);
+                }
+            } else {
+                throw new XAException("Unknown start flag " + flag);
+            }
+        }
+
+        /**
+         * This method does nothing.
+         *
+         * @param xid  the id of the transaction branch for this connection
+         * @param flag ignored
+         * @throws XAException if the connection is already elisted in another tranction
+         */
+        public synchronized void end(Xid xid, int flag) throws XAException {
+            if (xid == null) throw new NullPointerException("xid is null");
+            if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
+
+            // This notification tells us that the application server is done using this
+            // connection for the time being.  The connection is still associated with an
+            // open transaction, so we must still wait for the commit or rollback method
+        }
+
+        /**
+         * This method does nothing since the LocalXAConnection does not support two-phase-commit.  This method
+         * will return XAResource.XA_RDONLY if the connection isReadOnly().  This assumes that the physical
+         * connection is wrapped with a proxy that prevents an application from changing the read-only flag
+         * while enrolled in a transaction.
+         *
+         * @param xid the id of the transaction branch for this connection
+         * @return XAResource.XA_RDONLY if the connection.isReadOnly(); XAResource.XA_OK otherwise
+         */
+        public synchronized int prepare(Xid xid) {
+            // if the connection is read-only, then the resource is read-only
+            // NOTE: this assumes that the outer proxy throws an exception when application code
+            // attempts to set this in a transaction
+            try {
+                if (connection.isReadOnly()) {
+                    // update the auto commit flag
+                    connection.setAutoCommit(originalAutoCommit);
+
+                    // tell the transaction manager we are read only
+                    return XAResource.XA_RDONLY;
+                }
+            } catch (SQLException ignored) {
+                // no big deal
+            }
+
+            // this is a local (one phase) only connectiion, so we can't prepare
+            return XAResource.XA_OK;
+        }
+
+        /**
+         * Commits the transaction and restores the original auto commit setting.
+         *
+         * @param xid  the id of the transaction branch for this connection
+         * @param flag ignored
+         * @throws XAException if connection.commit() throws a SQLException
+         */
+        public synchronized void commit(Xid xid, boolean flag) throws XAException {
+            if (xid == null) throw new NullPointerException("xid is null");
+            if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
+
+            try {
+                // make sure the connection isn't already closed
+                if (connection.isClosed()) {
+                    throw new XAException("Conection is closed");
+                }
+
+                // A read only connection should not be committed
+                if (!connection.isReadOnly()) {
+                    connection.commit();
+                }
+            } catch (SQLException e) {
+                throw (XAException) new XAException().initCause(e);
+            } finally {
+                try {
+                    connection.setAutoCommit(originalAutoCommit);
+                } catch (SQLException e) {
+                }
+                this.xid = null;
+            }
+        }
+
+        /**
+         * Rollsback the transaction and restores the original auto commit setting.
+         *
+         * @param xid the id of the transaction branch for this connection
+         * @throws XAException if connection.rollback() throws a SQLException
+         */
+        public synchronized void rollback(Xid xid) throws XAException {
+            if (xid == null) throw new NullPointerException("xid is null");
+            if (!this.xid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.xid + ", but was " + xid);
+
+            try {
+                connection.rollback();
+            } catch (SQLException e) {
+                throw (XAException) new XAException().initCause(e);
+            } finally {
+                try {
+                    connection.setAutoCommit(originalAutoCommit);
+                } catch (SQLException e) {
+                }
+                this.xid = null;
+            }
+        }
+
+        /**
+         * Returns true if the specified XAResource == this XAResource.
+         *
+         * @param xaResource the XAResource to test
+         * @return true if the specified XAResource == this XAResource; false otherwise
+         */
+        public boolean isSameRM(XAResource xaResource) {
+            return this == xaResource;
+        }
+
+        /**
+         * Clears the currently associated transaction if it is the specified xid.
+         *
+         * @param xid the id of the transaction to forget
+         */
+        public synchronized void forget(Xid xid) {
+            if (xid != null && this.xid.equals(xid)) {
+                this.xid = null;
+            }
+        }
+
+        /**
+         * Always returns a zero length Xid array.  The LocalXAConnectionFactory can not support recovery, so no xids will ever be found.
+         *
+         * @param flag ignored since recoverty is not supported
+         * @return always a zero length Xid array.
+         */
+        public Xid[] recover(int flag) {
+            return new Xid[0];
+        }
+
+        /**
+         * Always returns 0 since we have no way to set a transaction timeout on a JDBC connection.
+         *
+         * @return always 0
+         */
+        public int getTransactionTimeout() {
+            return 0;
+        }
+
+        /**
+         * Always returns false since we have no way to set a transaction timeout on a JDBC connection.
+         *
+         * @param transactionTimeout ignored since we have no way to set a transaction timeout on a JDBC connection
+         * @return always false
+         */
+        public boolean setTransactionTimeout(int transactionTimeout) {
+            return false;
+        }
+    }
+
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedConnection.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedConnection.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedConnection.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedConnection.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,271 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import org.apache.commons.dbcp.DelegatingConnection;
+import org.apache.commons.pool.ObjectPool;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * ManagedConnection is responsible for managing a database connection in a transactional environment
+ * (typically called "Container Managed").  A managed connection opperates like any other connection
+ * when no gloabal transaction (a.k.a. XA transaction or JTA Transaction) is in progress.  When a
+ * global transaction is active a single physical connection to the database is used by all
+ * ManagedConnections accessed in the scope of the transaction.  Connection sharing means that all
+ * data access during a transaction has a consistent view of the database.  When the global transaction
+ * is committed or rolled back the enlisted connections are committed or rolled back.  Typically upon
+ * transaction completion, a connection returns to the auto commit setting in effect before being
+ * elisted in the transaction, but some vendors do not propertly implement this.
+ *
+ * When enslisted in a transaction the setAutoCommit(), commit(), rollback(), and setReadOnly() methods
+ * throw a SQLException.  This is necessary to assure that the transaction completes as a single unit.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class ManagedConnection extends DelegatingConnection {
+    private final ObjectPool pool;
+    private final TransactionRegistry transactionRegistry;
+    private final boolean accessToUnderlyingConnectionAllowed;
+    private TransactionContext transactionContext;
+    private boolean isSharedConnection;
+
+    public ManagedConnection(ObjectPool pool, TransactionRegistry transactionRegistry, boolean accessToUnderlyingConnectionAllowed) throws SQLException {
+        super(null);
+        this.pool = pool;
+        this.transactionRegistry = transactionRegistry;
+        this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
+        updateTransactionStatus();
+    }
+
+    protected void checkOpen() throws SQLException {
+        super.checkOpen();
+        updateTransactionStatus();
+    }
+
+    private void updateTransactionStatus() throws SQLException {
+        // if there is a is an active transaction context, assure the transaction context hasn't changed
+        if (transactionContext != null) {
+            if (transactionContext.isActive()) {
+                if (transactionContext != transactionRegistry.getActiveTransactionContext()) {
+                    throw new SQLException("Connection can not be used while enlisted in another transaction");
+                }
+                return;
+            } else {
+                // transaction should have been cleared up by TransactionContextListener, but in
+                // rare cases another lister could have registered which uses the connection before
+                // our listener is called.  In that rare case, trigger the transaction complete call now
+                transactionComplete();
+            }
+        }
+
+        // the existing transction context ended (or we didn't have one), get the active transaction context
+        transactionContext = transactionRegistry.getActiveTransactionContext();
+
+        // if there is an active transaction context and it already has a shared connection, use it
+        if (transactionContext != null && transactionContext.getSharedConnection() != null) {
+            // A connection for the connection factory has already been enrolled
+            // in the transaction, replace our delegate with the enrolled connection
+
+            // return current connection to the pool
+            Connection connection = getDelegateInternal();
+            setDelegate(null);
+            if (connection != null) {
+                try {
+                    pool.returnObject(connection);
+                } catch (Exception ignored) {
+                    // whatever... try to invalidat the connection
+                    try {
+                        pool.invalidateObject(connection);
+                    } catch (Exception ignore) {
+                        // no big deal
+                    }
+                }
+            }
+
+            // add a listener to the transaction context
+            transactionContext.addTransactionContextListener(new CompletionListener());
+
+            // set our delegate to the shared connection
+            setDelegate(transactionContext.getSharedConnection());
+
+            // remember that we are using a shared connection so it can be cleared after the
+            // transaction completes
+            isSharedConnection = true;
+        } else {
+            // if our delegate is null, create one
+            if (getDelegateInternal() == null) {
+                try {
+                    // borrow a new connection from the pool
+                    Connection connection = (Connection) pool.borrowObject();
+                    setDelegate(connection);
+                } catch (Exception e) {
+                    throw (SQLException) new SQLException("Unable to acquire a new connection from the pool").initCause(e);
+                }
+            }
+
+            // if we have a transaction, out delegate becomes the shared delegate
+            if (transactionContext != null) {
+                // add a listener to the transaction context
+                transactionContext.addTransactionContextListener(new CompletionListener());
+
+                // register our connection as the shared connection
+                try {
+                    transactionContext.setSharedConnection(getDelegateInternal());
+                } catch (SQLException e) {
+                    // transaction is hosed
+                    transactionContext = null;
+                    throw e;
+                }
+            }
+        }
+    }
+
+    public void close() throws SQLException {
+        // close can be called multiple times, but PoolableConnection improperly
+        // throws an exception when a connection is closed twice, so before calling
+        // close we aren't alreayd closed
+        if (!isClosed()) {
+
+            // don't use super.close() because it calls passivate() which marks the
+            // the connection as cloased without returning it to the pool
+            try {
+                // don't actually close the connection if in a transaction
+                // the connection will be closed by the transactionComplete method
+                if (transactionContext == null) {
+                    getDelegateInternal().close();
+                }
+            } finally {
+                _closed = true;
+            }
+        }
+    }
+
+    protected class CompletionListener implements TransactionContextListener {
+        public void afterCompletion(TransactionContext transactionContext, boolean commited) {
+            if (transactionContext == transactionContext) {
+                transactionComplete();
+            }
+        }
+    }
+
+    protected void transactionComplete() {
+        transactionContext = null;
+
+        // if we were using a shared connection, clear the reference now that the transaction has completed
+        if (isSharedConnection) {
+            // for now, just set the delegate to null, it will be created later if needed
+            setDelegate(null);
+            isSharedConnection = false;
+        }
+
+        // if this connection was closed during the transaction and there is still a delegate present close it
+        Connection delegate = getDelegateInternal();
+        if (_closed && delegate != null) {
+            try {
+                setDelegate(null);
+
+                // don't actually close the connection if in a transaction
+                if (!delegate.isClosed()) {
+                    // don't use super.close() because it calls passivate() which marks the
+                    // the connection as cloased without returning it to the pool
+                    delegate.close();
+                }
+            } catch (SQLException ignored) {
+                // not a whole lot we can do here as connection is closed
+                // and this is a transaction classback so there is no
+                // way to report the error
+            } finally {
+                _closed = true;
+            }
+        }
+
+    }
+
+    //
+    // The following methods can't be used while enlisted in a transaction
+    //
+
+    public void setAutoCommit(boolean autoCommit) throws SQLException {
+        if (transactionContext != null) {
+            throw new SQLException("Auto-commit can not be set while enrolled in a transaction");
+        }
+        super.setAutoCommit(autoCommit);
+    }
+
+
+    public void commit() throws SQLException {
+        if (transactionContext != null) {
+            throw new SQLException("Commit can not be set while enrolled in a transaction");
+        }
+        super.commit();
+    }
+
+    public void rollback() throws SQLException {
+        if (transactionContext != null) {
+            throw new SQLException("Commit can not be set while enrolled in a transaction");
+        }
+        super.rollback();
+    }
+
+
+    public void setReadOnly(boolean readOnly) throws SQLException {
+        if (transactionContext != null) {
+            throw new SQLException("Read-only can not be set while enrolled in a transaction");
+        }
+        super.setReadOnly(readOnly);
+    }
+
+    //
+    // Methods for accessing the delegate connection
+    //
+
+    /**
+     * If false, getDelegate() and getInnermostDelegate() will return null.
+     * @return if false, getDelegate() and getInnermostDelegate() will return null
+     */
+    public boolean isAccessToUnderlyingConnectionAllowed() {
+        return accessToUnderlyingConnectionAllowed;
+    }
+
+    public Connection getDelegate() {
+        if (isAccessToUnderlyingConnectionAllowed()) {
+            return getDelegateInternal();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the actual delegate without checking the isAccessToUnderlyingConnectionAllowed() flag.  This method is for internal use only.
+     * @return the delegate of this connection
+     */
+    protected Connection getDelegateInternal() {
+        return super.getDelegate();
+    }
+
+    public Connection getInnermostDelegate() {
+        if (isAccessToUnderlyingConnectionAllowed()) {
+            return super.getInnermostDelegate();
+        } else {
+            return null;
+        }
+    }
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedDataSource.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedDataSource.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedDataSource.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/ManagedDataSource.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,80 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.dbcp.PoolingDataSource;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * The ManagedDataSource is a PoolingDataSource that creates ManagedConnections.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class ManagedDataSource extends PoolingDataSource {
+    private TransactionRegistry transactionRegistry;
+
+    /**
+     * Creates an uninitialized datasource.  Before this data source can be used a pool and
+     * transaction registry must be set.
+     */
+    public ManagedDataSource() {
+    }
+
+    /**
+     * Creates a ManagedDataSource which obtains connections from the specified pool and
+     * manages them using the specified transaction registry.  The TransactionRegistry must
+     * be the transaction registry obtained from the XAConnectionFactory used to create
+     * the connection pool.  If not an error will occure when attempting to use the connection
+     * in a global transaction because the XAResource object associated with the connection
+     * will be unavailable.
+     *
+     * @param pool the connection pool
+     * @param transactionRegistry the transaction registry obtained from the
+     * XAConnectionFactory used to create the connection pool object factory
+     */
+    public ManagedDataSource(ObjectPool pool, TransactionRegistry transactionRegistry) {
+        super(pool);
+        this.transactionRegistry = transactionRegistry;
+    }
+
+    /**
+     * Sets the transaction registry from the XAConnectionFactory used to create the pool.
+     * The transaction registry can only be set once using either a connector or this setter
+     * method.
+     * @param transactionRegistry the transaction registry acquired from the XAConnectionFactory
+     * used to create the pool
+     */
+    public void setTransactionRegistry(TransactionRegistry transactionRegistry) {
+        if(this.transactionRegistry != null) throw new IllegalStateException("TransactionRegistry already set");
+        if(transactionRegistry == null) throw new NullPointerException("TransactionRegistry is null");
+
+        this.transactionRegistry = transactionRegistry;
+    }
+
+    public Connection getConnection() throws SQLException {
+        if (_pool == null) throw new IllegalStateException("Pool has not been set");
+        if (transactionRegistry == null) throw new IllegalStateException("TransactionRegistry has not been set");
+
+        Connection connection = new ManagedConnection(_pool, transactionRegistry, isAccessToUnderlyingConnectionAllowed());
+        return connection;
+    }
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContext.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContext.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContext.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContext.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,131 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * TransactionContext represents the association between a single XAConnectionFactory and a Transaction.
+ * This context contains a single shared connection which should be used by all ManagedConnections for
+ * the XAConnectionFactory, the ability to listen for the transaction completion event, and a method
+ * to check the status of the transaction.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class TransactionContext {
+    private final TransactionRegistry transactionRegistry;
+    private final Transaction transaction;
+    private Connection sharedConnection;
+
+    /**
+     * Creates a TransactionContext for the specified Transaction and TransactionRegistry.  The
+     * TransactionRegistry is used to obtain the XAResource for the shared connection when it is
+     * enlisted in the transaction.
+     *
+     * @param transactionRegistry the TransactionRegistry used to obtain the XAResource for the
+     * shared connection
+     * @param transaction the transaction
+     */
+    public TransactionContext(TransactionRegistry transactionRegistry, Transaction transaction) {
+        if (transactionRegistry == null) throw new NullPointerException("transactionRegistry is null");
+        if (transaction == null) throw new NullPointerException("transaction is null");
+        this.transactionRegistry = transactionRegistry;
+        this.transaction = transaction;
+    }
+
+    /**
+     * Gets the connection shared by all ManagedConnections in the transaction.  Specifically,
+     * connection using the same XAConnectionFactory from which the TransactionRegistry was
+     * obtained.
+     * @return the shared connection for this transaction
+     */
+    public Connection getSharedConnection() {
+        return sharedConnection;
+    }
+
+    /**
+     * Sets the shared connection for this transaction.  The shared connection is enlisted
+     * in the transaction.
+     *
+     * @param sharedConnection the shared connection
+     * @throws SQLException if a shared connection is already set, if XAResource for the connection
+     * could not be found in the transaction registry, or if there was a problem enlisting the
+     * connection in the transaction
+     */
+    public void setSharedConnection(Connection sharedConnection) throws SQLException {
+        if (this.sharedConnection != null) {
+            throw new IllegalStateException("A shared connection is alredy set");
+        }
+
+        // This is the first use of the connection in this transaction, so we must
+        // enlist it in the transaction
+        try {
+            XAResource xaResource = transactionRegistry.getXAResource(sharedConnection);
+            transaction.enlistResource(xaResource);
+        } catch (RollbackException e) {
+            // transaction was rolled back... proceed as if there never was a transaction
+        } catch (SystemException e) {
+            throw (SQLException) new SQLException("Unable to enlist connection the transaction").initCause(e);
+        }
+
+        this.sharedConnection = sharedConnection;
+    }
+
+    /**
+     * Adds a listener for transaction completion events.
+     *
+     * @param listener the listener to add
+     * @throws SQLException if a problem occurs adding the listener to the transaction
+     */
+    public void addTransactionContextListener(final TransactionContextListener listener) throws SQLException {
+        try {
+            transaction.registerSynchronization(new Synchronization() {
+                public void beforeCompletion() {
+                }
+
+                public void afterCompletion(int status) {
+                    listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
+                }
+            });
+        } catch (Exception e) {
+            throw (SQLException) new SQLException("Unable to register transaction context listener").initCause(e);
+        }
+    }
+
+    /**
+     * True if the transaction is active or marked for rollback only.
+     * @return true if the transaction is active or marked for rollback only; false otherwise
+     * @throws SQLException if a problem occurs obtaining the transaction status
+     */
+    public boolean isActive() throws SQLException {
+        try {
+            int status = transaction.getStatus();
+            return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
+        } catch (SystemException e) {
+            throw (SQLException) new SQLException("Unable to get transaction status").initCause(e);
+        }
+    }
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContextListener.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContextListener.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContextListener.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionContextListener.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,33 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+/**
+ * A listener for transaction completion events.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public interface TransactionContextListener {
+    /**
+     * Occurs after the transaction commits or rolls back.
+     * @param transactionContext the transaction context that completed
+     * @param commited true if the transaction committed; false otherwise
+     */
+    void afterCompletion(TransactionContext transactionContext, boolean commited);
+}

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,116 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import javax.transaction.xa.XAResource;
+import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+import javax.transaction.SystemException;
+import javax.transaction.Status;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
+ * </p>
+ * The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and gives
+ * the ManagedConnection a way to enlist connections in a tranaction, allowing for the maximal resue of DBCP.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class TransactionRegistry {
+    private TransactionManager transactionManager;
+    private Map caches = new WeakHashMap();
+    private Map xaResources = new WeakHashMap();
+
+    /**
+     * Creates a TransactionRegistry for the specified transaction manager.
+     * @param transactionManager the transaction manager used to enlist connections
+     */
+    public TransactionRegistry(TransactionManager transactionManager) {
+        this.transactionManager = transactionManager;
+    }
+
+    /**
+     * Registers the association between a Connection and a XAResource.  When a conection
+     * is enlisted in a transaction, it is acutally the XAResource that is given to the transaction
+     * manager.
+     *
+     * @param connection the JDBC connection
+     * @param xaResource the XAResource which managed the connection within a transaction
+     */
+    public synchronized void registerConnection(Connection connection, XAResource xaResource) {
+        if (connection == null) throw new NullPointerException("connection is null");
+        if (xaResource == null) throw new NullPointerException("xaResource is null");
+        xaResources.put(connection, xaResource);
+    }
+
+    /**
+     * Gets the XAResource registered for the connection.
+     * @param connection the connection
+     * @return the XAResource registered for the connection; never null
+     * @throws SQLException if the connection does not have a registered XAResource
+     */
+    public synchronized XAResource getXAResource(Connection connection) throws SQLException {
+        if (connection == null) throw new NullPointerException("connection is null");
+        XAResource xaResource = (XAResource) xaResources.get(connection);
+        if (xaResource == null) {
+            throw new SQLException("Connection does not have a registered XAResource " + connection);
+        }
+        return xaResource;
+    }
+
+    /**
+     * Gets the active TransactionContext or null if not Transaction is active.
+     * @return the active TransactionContext or null if not Transaction is active
+     * @throws SQLException if an error occurs while fetching the transaction
+     */
+    public TransactionContext getActiveTransactionContext() throws SQLException {
+        Transaction transaction = null;
+        try {
+            transaction = transactionManager.getTransaction();
+
+            // was there a transaction?
+            if (transaction == null) {
+                return null;
+            }
+
+            // is it active
+            int status = transaction.getStatus();
+            if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) {
+                return null;
+            }
+        } catch (SystemException e) {
+            throw (SQLException) new SQLException("Unable to determine current transaction ").initCause(e);
+        }
+
+        // register the the context (or create a new one)
+        synchronized (this) {
+            TransactionContext cache = (TransactionContext) caches.get(transaction);
+            if (cache == null) {
+                cache = new TransactionContext(this, transaction);
+                caches.put(transaction, cache);
+            }
+            return cache;
+        }
+    }
+}
+

Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/XAConnectionFactory.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/XAConnectionFactory.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/XAConnectionFactory.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/XAConnectionFactory.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,57 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import org.apache.commons.dbcp.ConnectionFactory;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * XAConnectionFactory is an extension of ConnectionFactory used to create connections
+ * in a transaction managed environment.  The XAConnectionFactory opperates like a normal
+ * ConnectionFactory except an TransactionRegistry is provided from which the XAResource
+ * for a connection can be obtained.  This allows the existing DBCP pool code to work with
+ * XAConnections and gives a the ManagedConnection a way to enlist a connection in the
+ * the transaction.
+ *
+ * @author Dain Sundstrom
+ * @author Rodney Waldhoff
+ * @version $Revision$
+ */
+public interface XAConnectionFactory extends ConnectionFactory {
+    /**
+     * Gets the TransactionRegistry for this connection factory which contains a the
+     * XAResource for every connection created by this factory.
+     *
+     * @return the transaction registry for this connection factory
+     */
+    TransactionRegistry getTransactionRegistry();
+
+    /**
+     * Create a new {@link java.sql.Connection} in an implementation specific fashion.
+     * </p>
+     * An implementation can assume that the caller of this will wrap the connection in
+     * a proxy that protects access to the setAutoCommit, commit and rollback when
+     * enrolled in a XA transaction.
+     *
+     * @return a new {@link java.sql.Connection}
+     * @throws java.sql.SQLException if a database error occurs creating the connection
+     */
+    Connection createConnection() throws SQLException;
+}

Added: jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterCallableStatement.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterCallableStatement.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterCallableStatement.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterCallableStatement.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,341 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ * Trivial implementation of a CallableStatement to avoid null pointer exceptions in tests.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class TesterCallableStatement extends TesterPreparedStatement implements CallableStatement {
+
+    public TesterCallableStatement(Connection conn) {
+        super(conn);
+    }
+
+    public TesterCallableStatement(Connection conn, String sql) {
+        super(conn, sql);
+    }
+
+    public TesterCallableStatement(Connection conn, String sql, int resultSetType, int resultSetConcurrency) {
+        super(conn, sql, resultSetType, resultSetConcurrency);
+    }
+
+    public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException {
+    }
+
+    public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException {
+    }
+
+    public boolean wasNull() throws SQLException {
+        return false;
+    }
+
+    public String getString(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public boolean getBoolean(int parameterIndex) throws SQLException {
+        return false;
+    }
+
+    public byte getByte(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public short getShort(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public int getInt(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public long getLong(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public float getFloat(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public double getDouble(int parameterIndex) throws SQLException {
+        return 0;
+    }
+
+    public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException {
+        return null;
+    }
+
+    public byte[] getBytes(int parameterIndex) throws SQLException {
+        return new byte[0];
+    }
+
+    public Date getDate(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public Time getTime(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public Timestamp getTimestamp(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public Object getObject(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public Object getObject(int i, Map map) throws SQLException {
+        return null;
+    }
+
+    public Ref getRef(int i) throws SQLException {
+        return null;
+    }
+
+    public Blob getBlob(int i) throws SQLException {
+        return null;
+    }
+
+    public Clob getClob(int i) throws SQLException {
+        return null;
+    }
+
+    public Array getArray(int i) throws SQLException {
+        return null;
+    }
+
+    public Date getDate(int parameterIndex, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public Time getTime(int parameterIndex, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public void registerOutParameter(int paramIndex, int sqlType, String typeName) throws SQLException {
+    }
+
+    public void registerOutParameter(String parameterName, int sqlType) throws SQLException {
+    }
+
+    public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException {
+    }
+
+    public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException {
+    }
+
+    public URL getURL(int parameterIndex) throws SQLException {
+        return null;
+    }
+
+    public void setURL(String parameterName, URL val) throws SQLException {
+    }
+
+    public void setNull(String parameterName, int sqlType) throws SQLException {
+    }
+
+    public void setBoolean(String parameterName, boolean x) throws SQLException {
+    }
+
+    public void setByte(String parameterName, byte x) throws SQLException {
+    }
+
+    public void setShort(String parameterName, short x) throws SQLException {
+    }
+
+    public void setInt(String parameterName, int x) throws SQLException {
+    }
+
+    public void setLong(String parameterName, long x) throws SQLException {
+    }
+
+    public void setFloat(String parameterName, float x) throws SQLException {
+    }
+
+    public void setDouble(String parameterName, double x) throws SQLException {
+    }
+
+    public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException {
+    }
+
+    public void setString(String parameterName, String x) throws SQLException {
+    }
+
+    public void setBytes(String parameterName, byte x[]) throws SQLException {
+    }
+
+    public void setDate(String parameterName, Date x) throws SQLException {
+    }
+
+    public void setTime(String parameterName, Time x) throws SQLException {
+    }
+
+    public void setTimestamp(String parameterName, Timestamp x) throws SQLException {
+    }
+
+    public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException {
+    }
+
+    public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException {
+    }
+
+    public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException {
+    }
+
+    public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException {
+    }
+
+    public void setObject(String parameterName, Object x) throws SQLException {
+    }
+
+    public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException {
+    }
+
+    public void setDate(String parameterName, Date x, Calendar cal) throws SQLException {
+    }
+
+    public void setTime(String parameterName, Time x, Calendar cal) throws SQLException {
+    }
+
+    public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException {
+    }
+
+    public void setNull(String parameterName, int sqlType, String typeName) throws SQLException {
+    }
+
+    public String getString(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public boolean getBoolean(String parameterName) throws SQLException {
+        return false;
+    }
+
+    public byte getByte(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public short getShort(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public int getInt(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public long getLong(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public float getFloat(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public double getDouble(String parameterName) throws SQLException {
+        return 0;
+    }
+
+    public byte[] getBytes(String parameterName) throws SQLException {
+        return new byte[0];
+    }
+
+    public Date getDate(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Time getTime(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Timestamp getTimestamp(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Object getObject(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public BigDecimal getBigDecimal(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Object getObject(String parameterName, Map map) throws SQLException {
+        return null;
+    }
+
+    public Ref getRef(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Blob getBlob(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Clob getClob(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Array getArray(String parameterName) throws SQLException {
+        return null;
+    }
+
+    public Date getDate(String parameterName, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public Time getTime(String parameterName, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException {
+        return null;
+    }
+
+    public URL getURL(String parameterName) throws SQLException {
+        return null;
+    }
+}

Modified: jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterConnection.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterConnection.java?view=diff&rev=553511&r1=553510&r2=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterConnection.java (original)
+++ jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TesterConnection.java Thu Jul  5 07:12:47 2007
@@ -136,12 +136,12 @@
         if ("warning".equals(sql)) {
             setWarnings(new SQLWarning("warning in prepareCall"));
         }
-        return null;
+        return new TesterCallableStatement(this);
     }
 
     public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
         checkOpen();
-        return null;
+        return new TesterCallableStatement(this);
     }
 
     public PreparedStatement prepareStatement(String sql) throws SQLException {

Added: jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSource.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSource.java?view=auto&rev=553511
==============================================================================
--- jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSource.java (added)
+++ jakarta/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestManagedDataSource.java Thu Jul  5 07:12:47 2007
@@ -0,0 +1,246 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.commons.dbcp.managed;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.commons.dbcp.ConnectionFactory;
+import org.apache.commons.dbcp.DelegatingConnection;
+import org.apache.commons.dbcp.DriverConnectionFactory;
+import org.apache.commons.dbcp.PoolableConnectionFactory;
+import org.apache.commons.dbcp.PoolingDataSource;
+import org.apache.commons.dbcp.TestConnectionPool;
+import org.apache.commons.dbcp.TesterDriver;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
+
+import javax.transaction.TransactionManager;
+import java.sql.Connection;
+import java.util.Properties;
+
+/**
+ * TestSuite for ManagedDataSource without a transaction in progress.
+ *
+ * @author Dain Sundstrom
+ * @version $Revision$
+ */
+public class TestManagedDataSource extends TestConnectionPool {
+    public TestManagedDataSource(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestManagedDataSource.class);
+    }
+
+    protected Connection getConnection() throws Exception {
+        return ds.getConnection();
+    }
+
+    protected PoolingDataSource ds = null;
+    private GenericObjectPool pool = null;
+    protected TransactionManager transactionManager;
+
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // create a GeronimoTransactionManager for testing
+        transactionManager = new TransactionManagerImpl();
+
+        // create a driver connection factory
+        Properties properties = new Properties();
+        properties.setProperty("user", "username");
+        properties.setProperty("password", "password");
+        ConnectionFactory connectionFactory = new DriverConnectionFactory(new TesterDriver(), "jdbc:apache:commons:testdriver", properties);
+
+        // wrap it with a LocalXAConnectionFactory
+        XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(transactionManager, connectionFactory);
+
+        // create the pool
+        pool = new GenericObjectPool();
+        pool.setMaxActive(getMaxActive());
+        pool.setMaxWait(getMaxWait());
+
+        // create the pool object factory
+        PoolableConnectionFactory factory = new PoolableConnectionFactory(xaConnectionFactory, pool, null, "SELECT DUMMY FROM DUAL", true, true);
+        pool.setFactory(factory);
+
+        // finally create the datasource
+        ds = new ManagedDataSource(pool, xaConnectionFactory.getTransactionRegistry());
+        ds.setAccessToUnderlyingConnectionAllowed(true);
+    }
+
+    public void tearDown() throws Exception {
+        pool.close();
+        super.tearDown();
+    }
+
+    /**
+     * Verify the accessToUnderlyingConnectionAllowed propertly limits access to the physical connection.
+     */
+    public void testAccessToUnderlyingConnectionAllowed() throws Exception {
+        ds.setAccessToUnderlyingConnectionAllowed(true);
+        ManagedConnection connection = (ManagedConnection) newConnection();
+        assertTrue(connection.isAccessToUnderlyingConnectionAllowed());
+        assertNotNull(connection.getDelegate());
+        assertNotNull(connection.getInnermostDelegate());
+        connection.close();
+
+        ds.setAccessToUnderlyingConnectionAllowed(false);
+        connection = (ManagedConnection) newConnection();
+        assertFalse(connection.isAccessToUnderlyingConnectionAllowed());
+        assertNull(connection.getDelegate());
+        assertNull(connection.getInnermostDelegate());
+        connection.close();
+    }
+
+    /**
+     * Verify that conection sharing is working (or not working) as expected.
+     */
+    public void testSharedConnection() throws Exception {
+        DelegatingConnection connectionA = (DelegatingConnection) newConnection();
+        DelegatingConnection connectionB = (DelegatingConnection) newConnection();
+
+        assertFalse(connectionA.equals(connectionB));
+        assertFalse(connectionB.equals(connectionA));
+        assertFalse(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
+        assertFalse(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
+
+        connectionA.close();
+        connectionB.close();
+    }
+
+    public void testCantCloseConnectionTwice() throws Exception {
+        // this test is invalid... the JavaDoc and spec for the close method specifically
+        // state that the close method on an already closed connection is a no-op
+    }
+
+
+    /**
+     * Verify the close method can be called multiple times on a single connection without
+     * an exception being thrown.
+     */
+    public void testCanCloseConnectionTwice() throws Exception {
+        for (int i = 0; i < getMaxActive(); i++) { // loop to show we *can* close again once we've borrowed it from the pool again
+            Connection conn = newConnection();
+            assertTrue(null != conn);
+            assertTrue(!conn.isClosed());
+            conn.close();
+            assertTrue(conn.isClosed());
+            conn.close();
+            assertTrue(conn.isClosed());
+        }
+    }
+
+    public void testManagedConnectionEqualsSameDelegate() throws Exception {
+        // Get a maximal set of connections from the pool
+        Connection[] c = new Connection[getMaxActive()];
+        for (int i = 0; i < c.length; i++) {
+            c[i] = newConnection();
+        }
+        // Close the delegate of one wrapper in the pool
+        ((DelegatingConnection) c[0]).getDelegate().close();
+
+        // Grab a new connection - should get c[0]'s closed connection
+        // so should be delegate-equivalent, so equal
+        Connection con = newConnection();
+        assertTrue(c[0].equals(con));
+        assertTrue(con.equals(c[0]));
+        for (int i = 0; i < c.length; i++) {
+            c[i].close();
+        }
+    }
+
+    /*
+    * JIRA: DBCP-198
+    */
+    public void testManagedConnectionEqualsReflexive() throws Exception {
+        // Statndard setup - using DelegatingConnections
+        // returned from PoolableConnectionFactory
+        checkManagedConnectionEqualsReflexive();
+
+        // Force ManagedConnections to wrap non-Delegating connections
+        pool.close();
+        pool = new GenericObjectPool();
+        pool.setMaxActive(getMaxActive());
+        pool.setMaxWait(getMaxWait());
+        Properties props = new Properties();
+        props.setProperty("user", "username");
+        props.setProperty("password", "password");
+        NonDelegatingPoolableConnectionFactory factory = new NonDelegatingPoolableConnectionFactory(new DriverConnectionFactory(new TesterDriver(), "jdbc:apache:commons:testdriver", props), pool);
+        pool.setFactory(factory);
+        ds = new PoolingDataSource(pool);
+        checkManagedConnectionEqualsReflexive();
+    }
+
+    private void checkManagedConnectionEqualsReflexive() throws Exception {
+        Connection con = ds.getConnection();
+        Connection con2 = con;
+        assertTrue(con2.equals(con));
+        assertTrue(con.equals(con2));
+        con.close();
+    }
+
+    public void testManagedConnectionEqualsFail() throws Exception {
+        Connection con1 = ds.getConnection();
+        Connection con2 = ds.getConnection();
+        assertFalse(con1.equals(con2));
+        con1.close();
+        con2.close();
+    }
+
+    public void testManagedConnectionEqualsNull() throws Exception {
+        Connection con1 = ds.getConnection();
+        Connection con2 = null;
+        assertFalse(con1.equals(con2));
+        con1.close();
+    }
+
+    public void testManagedConnectionEqualsType() throws Exception {
+        Connection con1 = ds.getConnection();
+        Integer con2 = new Integer(0);
+        assertFalse(con1.equals(con2));
+        con1.close();
+    }
+
+    public void testManagedConnectionEqualInnermost() throws Exception {
+        ds.setAccessToUnderlyingConnectionAllowed(true);
+        DelegatingConnection con = (DelegatingConnection) ds.getConnection();
+        Connection inner = con.getInnermostDelegate();
+        ds.setAccessToUnderlyingConnectionAllowed(false);
+        DelegatingConnection con2 = new DelegatingConnection(inner);
+        assertTrue(con2.equals(con));
+        assertTrue(con.innermostDelegateEquals(con2.getInnermostDelegate()));
+        assertTrue(con2.innermostDelegateEquals(inner));
+        assertTrue(con.equals(con2));
+    }
+
+    /**
+     * Factory to return non-delegating connections for DBCP-198 test
+     */
+    private class NonDelegatingPoolableConnectionFactory extends PoolableConnectionFactory {
+        public NonDelegatingPoolableConnectionFactory(ConnectionFactory connFactory, ObjectPool pool) {
+            super(connFactory, pool, null, null, true, true);
+        }
+
+        synchronized public Object makeObject() throws Exception {
+            return _connFactory.createConnection();
+        }
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message