Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java Tue Jun 19 09:04:08 2018 @@ -27,29 +27,41 @@ import javax.sql.PooledConnection; * @since 2.0 */ interface PooledConnectionManager { + /** - * Close the PooledConnection and remove it from the connection pool - * to which it belongs, adjusting pool counters. + * Closes the PooledConnection and remove it from the connection pool to which it belongs, adjusting pool counters. * - * @param pc PooledConnection to be invalidated - * @throws SQLException if an SQL error occurs closing the connection + * @param pc + * PooledConnection to be invalidated + * @throws SQLException + * if an SQL error occurs closing the connection */ void invalidate(PooledConnection pc) throws SQLException; + // /** + // * Sets the database password used when creating connections. + // * + // * @param password password used when authenticating to the database + // * @since 3.0.0 + // */ + // void setPassword(char[] password); + /** * Sets the database password used when creating connections. * - * @param password password used when authenticating to the database + * @param password + * password used when authenticating to the database */ void setPassword(String password); - /** * Closes the connection pool associated with the given user. * - * @param username user name - * @throws SQLException if an error occurs closing idle connections in the pool + * @param userName + * user name + * @throws SQLException + * if an error occurs closing idle connections in the pool */ - void closePool(String username) throws SQLException; + void closePool(String userName) throws SQLException; }
Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java Tue Jun 19 09:04:08 2018 @@ -32,20 +32,20 @@ import org.apache.tomcat.dbcp.pool2.impl import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig; /** - * <p>A pooling <code>DataSource</code> appropriate for deployment within - * J2EE environment. There are many configuration options, most of which are - * defined in the parent class. All users (based on username) share a single - * maximum number of Connections in this datasource.</p> + * <p> + * A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration + * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number + * of Connections in this data source. + * </p> * - * <p>User passwords can be changed without re-initializing the datasource. - * When a <code>getConnection(username, password)</code> request is processed - * with a password that is different from those used to create connections in the - * pool associated with <code>username</code>, an attempt is made to create a - * new connection using the supplied password and if this succeeds, idle connections - * created using the old password are destroyed and new connections are created - * using the new password.</p> + * <p> + * User passwords can be changed without re-initializing the data source. When a + * <code>getConnection(user name, password)</code> request is processed with a password that is different from those + * used to create connections in the pool associated with <code>user name</code>, an attempt is made to create a new + * connection using the supplied password and if this succeeds, idle connections created using the old password are + * destroyed and new connections are created using the new password. + * </p> * - * @author John D. McNally * @since 2.0 */ public class SharedPoolDataSource extends InstanceKeyDataSource { @@ -55,18 +55,18 @@ public class SharedPoolDataSource extend // Pool properties private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; - - private transient KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> pool = null; - private transient KeyedCPDSConnectionFactory factory = null; + private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool; + private transient KeyedCPDSConnectionFactory factory; /** - * Default no-arg constructor for Serialization + * Default no-argument constructor for Serialization */ public SharedPoolDataSource() { + // empty. } /** - * Close pool being maintained by this datasource. + * Closes pool being maintained by this data source. */ @Override public void close() throws Exception { @@ -76,11 +76,12 @@ public class SharedPoolDataSource extend InstanceKeyDataSourceFactory.removeInstance(getInstanceKey()); } - // ------------------------------------------------------------------- // Properties /** + * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. + * * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. */ public int getMaxTotal() { @@ -88,27 +89,32 @@ public class SharedPoolDataSource extend } /** - * Set {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. - * @param maxTotal The max total value + * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. + * + * @param maxTotal + * {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. */ public void setMaxTotal(final int maxTotal) { assertInitializationAllowed(); this.maxTotal = maxTotal; } - // ---------------------------------------------------------------------- // Instrumentation Methods /** - * @return the number of active connections in the pool. + * Gets the number of active connections in the pool. + * + * @return The number of active connections in the pool. */ public int getNumActive() { return pool == null ? 0 : pool.getNumActive(); } /** - * @return the number of idle connections in the pool. + * Gets the number of idle connections in the pool. + * + * @return The number of idle connections in the pool. */ public int getNumIdle() { return pool == null ? 0 : pool.getNumIdle(); @@ -118,14 +124,13 @@ public class SharedPoolDataSource extend // Inherited abstract methods @Override - protected PooledConnectionAndInfo - getPooledConnectionAndInfo(final String username, final String password) - throws SQLException { + protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword) + throws SQLException { - synchronized(this) { + synchronized (this) { if (pool == null) { try { - registerPool(username, password); + registerPool(userName, userPassword); } catch (final NamingException e) { throw new SQLException("RegisterPool failed", e); } @@ -134,47 +139,41 @@ public class SharedPoolDataSource extend PooledConnectionAndInfo info = null; - final UserPassKey key = new UserPassKey(username, password); + final UserPassKey key = new UserPassKey(userName, userPassword); try { info = pool.borrowObject(key); - } - catch (final Exception e) { - throw new SQLException( - "Could not retrieve connection info from pool", e); + } catch (final Exception e) { + throw new SQLException("Could not retrieve connection info from pool", e); } return info; } @Override - protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) { + protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) { return factory; } /** - * @return a <code>SharedPoolDataSource</code> {@link Reference}. - * @throws NamingException Should not occur + * Returns a <code>SharedPoolDataSource</code> {@link Reference}. */ @Override public Reference getReference() throws NamingException { - final Reference ref = new Reference(getClass().getName(), - SharedPoolDataSourceFactory.class.getName(), null); + final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null); ref.add(new StringRefAddr("instanceKey", getInstanceKey())); return ref; } - private void registerPool(final String username, final String password) - throws NamingException, SQLException { + private void registerPool(final String userName, final String password) throws NamingException, SQLException { - final ConnectionPoolDataSource cpds = testCPDS(username, password); + final ConnectionPoolDataSource cpds = testCPDS(userName, password); // Create an object pool to contain our PooledConnections - factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), - getValidationQueryTimeout(), isRollbackAfterValidation()); + factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeout(), + isRollbackAfterValidation()); factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); - final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = - new GenericKeyedObjectPoolConfig<>(); + final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig(); config.setBlockWhenExhausted(getDefaultBlockWhenExhausted()); config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName()); config.setLifo(getDefaultLifo()); @@ -182,66 +181,58 @@ public class SharedPoolDataSource extend config.setMaxTotal(getMaxTotal()); config.setMaxTotalPerKey(getDefaultMaxTotal()); config.setMaxWaitMillis(getDefaultMaxWaitMillis()); - config.setMinEvictableIdleTimeMillis( - getDefaultMinEvictableIdleTimeMillis()); + config.setMinEvictableIdleTimeMillis(getDefaultMinEvictableIdleTimeMillis()); config.setMinIdlePerKey(getDefaultMinIdle()); config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun()); - config.setSoftMinEvictableIdleTimeMillis( - getDefaultSoftMinEvictableIdleTimeMillis()); + config.setSoftMinEvictableIdleTimeMillis(getDefaultSoftMinEvictableIdleTimeMillis()); config.setTestOnCreate(getDefaultTestOnCreate()); config.setTestOnBorrow(getDefaultTestOnBorrow()); config.setTestOnReturn(getDefaultTestOnReturn()); config.setTestWhileIdle(getDefaultTestWhileIdle()); - config.setTimeBetweenEvictionRunsMillis( - getDefaultTimeBetweenEvictionRunsMillis()); + config.setTimeBetweenEvictionRunsMillis(getDefaultTimeBetweenEvictionRunsMillis()); - final KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> tmpPool = - new GenericKeyedObjectPool<>(factory, config); + final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, + config); factory.setPool(tmpPool); pool = tmpPool; } @Override - protected void setupDefaults(final Connection con, final String username) throws SQLException { + protected void setupDefaults(final Connection connection, final String userName) throws SQLException { final Boolean defaultAutoCommit = isDefaultAutoCommit(); - if (defaultAutoCommit != null && - con.getAutoCommit() != defaultAutoCommit.booleanValue()) { - con.setAutoCommit(defaultAutoCommit.booleanValue()); + if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit.booleanValue()) { + connection.setAutoCommit(defaultAutoCommit.booleanValue()); } final int defaultTransactionIsolation = getDefaultTransactionIsolation(); if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { - con.setTransactionIsolation(defaultTransactionIsolation); + connection.setTransactionIsolation(defaultTransactionIsolation); } final Boolean defaultReadOnly = isDefaultReadOnly(); - if (defaultReadOnly != null && - con.isReadOnly() != defaultReadOnly.booleanValue()) { - con.setReadOnly(defaultReadOnly.booleanValue()); + if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly.booleanValue()) { + connection.setReadOnly(defaultReadOnly.booleanValue()); } } /** * Supports Serialization interface. * - * @param in a <code>java.io.ObjectInputStream</code> value - * @throws IOException if an error occurs - * @throws ClassNotFoundException if an error occurs - */ - private void readObject(final ObjectInputStream in) - throws IOException, ClassNotFoundException { - try - { + * @param in + * a <code>java.io.ObjectInputStream</code> value + * @throws IOException + * if an error occurs + * @throws ClassNotFoundException + * if an error occurs + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + try { in.defaultReadObject(); - final SharedPoolDataSource oldDS = (SharedPoolDataSource) - new SharedPoolDataSourceFactory() + final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory() .getObjectInstance(getReference(), null, null, null); this.pool = oldDS.pool; - } - catch (final NamingException e) - { + } catch (final NamingException e) { throw new IOException("NamingException: " + e); } } } - Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java Tue Jun 19 09:04:08 2018 @@ -22,13 +22,11 @@ import javax.naming.Reference; /** * A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s + * * @since 2.0 */ -public class SharedPoolDataSourceFactory - extends InstanceKeyDataSourceFactory -{ - private static final String SHARED_POOL_CLASSNAME = - SharedPoolDataSource.class.getName(); +public class SharedPoolDataSourceFactory extends InstanceKeyDataSourceFactory { + private static final String SHARED_POOL_CLASSNAME = SharedPoolDataSource.class.getName(); @Override protected boolean isCorrectClass(final String className) { @@ -40,10 +38,8 @@ public class SharedPoolDataSourceFactory final SharedPoolDataSource spds = new SharedPoolDataSource(); final RefAddr ra = ref.get("maxTotal"); if (ra != null && ra.getContent() != null) { - spds.setMaxTotal( - Integer.parseInt(ra.getContent().toString())); + spds.setMaxTotal(Integer.parseInt(ra.getContent().toString())); } return spds; } } - Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java Tue Jun 19 09:04:08 2018 @@ -19,85 +19,115 @@ package org.apache.tomcat.dbcp.dbcp2.dat import java.io.Serializable; +import org.apache.tomcat.dbcp.dbcp2.Utils; + /** - * <p>Holds a username, password pair. Serves as a poolable object key for the KeyedObjectPool - * backing a SharedPoolDataSource. Two instances with the same username are considered equal. - * This ensures that there will be only one keyed pool for each user in the pool. The password - * is used (along with the username) by the KeyedCPDSConnectionFactory when creating new connections.</p> + * <p> + * Holds a user name and password pair. Serves as a poolable object key for the KeyedObjectPool backing a + * SharedPoolDataSource. Two instances with the same user name are considered equal. This ensures that there will be + * only one keyed pool for each user in the pool. The password is used (along with the user name) by the + * KeyedCPDSConnectionFactory when creating new connections. + * </p> * - * <p>{@link InstanceKeyDataSource#getConnection(String, String)} validates that the password used to create - * a connection matches the password provided by the client.</p> + * <p> + * {@link InstanceKeyDataSource#getConnection(String, String)} validates that the password used to create a connection + * matches the password provided by the client. + * </p> * * @since 2.0 */ class UserPassKey implements Serializable { private static final long serialVersionUID = 5142970911626584817L; - private final String password; - private final String username; - - UserPassKey(final String username, final String password) { - this.username = username; - this.password = password; - } + private final String userName; + private final char[] userPassword; /** - * Get the value of password. - * @return value of password. + * @since 2.4.0 */ - public String getPassword() { - return password; + UserPassKey(final String userName) { + this(userName, (char[]) null); } /** - * Get the value of username. - * @return value of username. + * @since 2.4.0 */ - public String getUsername() { - return username; + UserPassKey(final String userName, final char[] password) { + this.userName = userName; + this.userPassword = password; + } + + UserPassKey(final String userName, final String userPassword) { + this(userName, Utils.toCharArray(userPassword)); } /** - * @return <code>true</code> if the username fields for both - * objects are equal. Two instances with the same username - * but different passwords are considered equal. - * - * @see java.lang.Object#equals(java.lang.Object) + * Only takes the user name into account. */ @Override public boolean equals(final Object obj) { + if (this == obj) { + return true; + } if (obj == null) { return false; } - - if (obj == this) { - return true; + if (getClass() != obj.getClass()) { + return false; } - - if (!(obj instanceof UserPassKey)) { + final UserPassKey other = (UserPassKey) obj; + if (userName == null) { + if (other.userName != null) { + return false; + } + } else if (!userName.equals(other.userName)) { return false; } + return true; + } - final UserPassKey key = (UserPassKey) obj; + /** + * Gets the value of password. + * + * @return value of password. + */ + public String getPassword() { + return Utils.toString(userPassword); + } + + /** + * Gets the value of password. + * + * @return value of password. + */ + public char[] getPasswordCharArray() { + return userPassword; + } - return this.username == null ? - key.username == null : - this.username.equals(key.username); + /** + * Gets the value of user name. + * + * @return value of user name. + */ + public String getUsername() { + return userName; } /** - * Returns the hash of the username. + * Only takes the user name into account. */ @Override public int hashCode() { - return this.username != null ? - this.username.hashCode() : 0; + final int prime = 31; + int result = 1; + result = prime * result + ((userName == null) ? 0 : userName.hashCode()); + return result; } @Override public String toString() { final StringBuffer sb = new StringBuffer(50); sb.append("UserPassKey("); - sb.append(username).append(", ").append(password).append(')'); + sb.append(userName).append(", ").append(userPassword).append(')'); return sb.toString(); } } Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java Tue Jun 19 09:04:08 2018 @@ -30,22 +30,18 @@ import org.apache.tomcat.dbcp.dbcp2.Pool import org.apache.tomcat.dbcp.dbcp2.PoolingDataSource; /** - * <p>BasicManagedDataSource is an extension of BasicDataSource which - * creates ManagedConnections. This data source can create either - * full two-phase-commit XA connections or one-phase-commit - * local connections. Both types of connections are committed or - * rolled back as part of the global transaction (a.k.a. XA - * transaction or JTA Transaction), but only XA connections can be - * recovered in the case of a system crash. + * <p> + * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can + * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections + * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only + * XA connections can be recovered in the case of a system crash. * </p> - * <p>BasicManagedDataSource adds the TransactionManager and XADataSource - * properties. The TransactionManager property is required and is - * used to enlist connections in global transactions. The XADataSource - * is optional and if set is the class name of the XADataSource class - * for a two-phase-commit JDBC driver. If the XADataSource property - * is set, the driverClassName is ignored and a DataSourceXAConnectionFactory - * is created. Otherwise, a standard DriverConnectionFactory is created - * and wrapped with a LocalXAConnectionFactory. + * <p> + * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is + * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the + * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the + * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard + * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory. * </p> * * @see BasicDataSource @@ -53,13 +49,17 @@ import org.apache.tomcat.dbcp.dbcp2.Pool * @since 2.0 */ public class BasicManagedDataSource extends BasicDataSource { + /** Transaction Registry */ private TransactionRegistry transactionRegistry; + /** Transaction Manager */ private transient TransactionManager transactionManager; - /** XA datasource class name */ + + /** XA data source class name */ private String xaDataSource; - /** XA datasource instance */ + + /** XA data source instance */ private XADataSource xaDataSourceInstance; /** @@ -72,14 +72,17 @@ public class BasicManagedDataSource exte } /** - * <p>Sets the XADataSource instance used by the XAConnectionFactory.</p> * <p> - * Note: this method currently has no effect once the pool has been - * initialized. The pool is initialized the first time one of the - * following methods is invoked: <code>getConnection, setLogwriter, - * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> + * Sets the XADataSource instance used by the XAConnectionFactory. + * </p> + * <p> + * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first + * time one of the following methods is invoked: <code>getConnection, setLogwriter, + * setLoginTimeout, getLoginTimeout, getLogWriter.</code> + * </p> * - * @param xaDataSourceInstance XADataSource instance + * @param xaDataSourceInstance + * XADataSource instance */ public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) { this.xaDataSourceInstance = xaDataSourceInstance; @@ -88,6 +91,7 @@ public class BasicManagedDataSource exte /** * Gets the required transaction manager property. + * * @return the transaction manager used to enlist connections */ public TransactionManager getTransactionManager() { @@ -96,6 +100,7 @@ public class BasicManagedDataSource exte /** * Gets the transaction registry. + * * @return the transaction registry associating XAResources with managed connections */ protected synchronized TransactionRegistry getTransactionRegistry() { @@ -104,7 +109,9 @@ public class BasicManagedDataSource exte /** * Sets the required transaction manager property. - * @param transactionManager the transaction manager used to enlist connections + * + * @param transactionManager + * the transaction manager used to enlist connections */ public void setTransactionManager(final TransactionManager transactionManager) { this.transactionManager = transactionManager; @@ -112,6 +119,7 @@ public class BasicManagedDataSource exte /** * Gets the optional XADataSource class name. + * * @return the optional XADataSource class name */ public synchronized String getXADataSource() { @@ -120,7 +128,9 @@ public class BasicManagedDataSource exte /** * Sets the optional XADataSource class name. - * @param xaDataSource the optional XADataSource class name + * + * @param xaDataSource + * the optional XADataSource class name */ public synchronized void setXADataSource(final String xaDataSource) { this.xaDataSource = xaDataSource; @@ -132,10 +142,12 @@ public class BasicManagedDataSource exte throw new SQLException("Transaction manager must be set before a connection can be created"); } - // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory + // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a + // LocalXAConnectionFactory if (xaDataSource == null) { final ConnectionFactory connectionFactory = super.createConnectionFactory(); - final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), connectionFactory); + final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), + connectionFactory); transactionRegistry = xaConnectionFactory.getTransactionRegistry(); return xaConnectionFactory; } @@ -159,15 +171,16 @@ public class BasicManagedDataSource exte } // finally, create the XAConnectionFactory using the XA data source - final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), xaDataSourceInstance, getUsername(), getPassword()); + final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), + xaDataSourceInstance, getUsername(), getPassword()); transactionRegistry = xaConnectionFactory.getTransactionRegistry(); return xaConnectionFactory; } @Override protected DataSource createDataSourceInstance() throws SQLException { - final PoolingDataSource<PoolableConnection> pds = - new ManagedDataSource<>(getConnectionPool(), transactionRegistry); + final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(), + transactionRegistry); pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); return pds; } @@ -175,16 +188,18 @@ public class BasicManagedDataSource exte /** * Creates the PoolableConnectionFactory and attaches it to the connection pool. * - * @param driverConnectionFactory JDBC connection factory created by {@link #createConnectionFactory()} - * @throws SQLException if an error occurs creating the PoolableConnectionFactory + * @param driverConnectionFactory + * JDBC connection factory created by {@link #createConnectionFactory()} + * @throws SQLException + * if an error occurs creating the PoolableConnectionFactory */ @Override - protected PoolableConnectionFactory createPoolableConnectionFactory( - final ConnectionFactory driverConnectionFactory) throws SQLException { + protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory) + throws SQLException { PoolableConnectionFactory connectionFactory = null; try { - connectionFactory = new PoolableManagedConnectionFactory( - (XAConnectionFactory) driverConnectionFactory, getRegisteredJmxName()); + connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory, + getRegisteredJmxName()); connectionFactory.setValidationQuery(getValidationQuery()); connectionFactory.setValidationQueryTimeout(getValidationQueryTimeout()); connectionFactory.setConnectionInitSql(getConnectionInitSqls()); @@ -194,8 +209,7 @@ public class BasicManagedDataSource exte connectionFactory.setDefaultCatalog(getDefaultCatalog()); connectionFactory.setCacheState(getCacheState()); connectionFactory.setPoolStatements(isPoolPreparedStatements()); - connectionFactory.setMaxOpenPreparedStatements( - getMaxOpenPreparedStatements()); + connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements()); connectionFactory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); connectionFactory.setRollbackOnReturn(getRollbackOnReturn()); connectionFactory.setEnableAutoCommitOnReturn(getEnableAutoCommitOnReturn()); Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java Tue Jun 19 09:04:08 2018 @@ -19,6 +19,7 @@ package org.apache.tomcat.dbcp.dbcp2.man import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; @@ -28,74 +29,111 @@ import javax.sql.XADataSource; import javax.transaction.TransactionManager; import javax.transaction.xa.XAResource; +import org.apache.tomcat.dbcp.dbcp2.Utils; + /** * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources. * - * @author Dain Sundstrom * @since 2.0 */ public class DataSourceXAConnectionFactory implements XAConnectionFactory { private final TransactionRegistry transactionRegistry; private final XADataSource xaDataSource; - private String username; - private String password; + private String userName; + private char[] userPassword; /** - * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database - * connections. The connections are enlisted into transactions using the specified transaction manager. + * 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 + * @param transactionManager + * the transaction manager in which connections will be enlisted + * @param xaDataSource + * the data source from which connections will be retrieved */ public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) { - this(transactionManager, xaDataSource, null, null); + this(transactionManager, xaDataSource, null, (char[]) null); } /** - * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database - * connections. The connections are enlisted into transactions using the specified transaction manager. + * 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 - * @param username the username used for authenticating new connections or null for unauthenticated - * @param password the password used for authenticating new connections + * @param transactionManager + * the transaction manager in which connections will be enlisted + * @param xaDataSource + * the data source from which connections will be retrieved + * @param userName + * the user name used for authenticating new connections or null for unauthenticated + * @param userPassword + * the password used for authenticating new connections */ - public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final String username, final String password) { - if (transactionManager == null) { - throw new NullPointerException("transactionManager is null"); - } - if (xaDataSource == null) { - throw new NullPointerException("xaDataSource is null"); - } - + public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, + final String userName, final char[] userPassword) { + Objects.requireNonNull(transactionManager, "transactionManager is null"); + Objects.requireNonNull(xaDataSource, "xaDataSource is null"); this.transactionRegistry = new TransactionRegistry(transactionManager); this.xaDataSource = xaDataSource; - this.username = username; - this.password = password; + this.userName = userName; + this.userPassword = userPassword; + } + + /** + * 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 + * @param userName + * the user name used for authenticating new connections or null for unauthenticated + * @param userPassword + * the password used for authenticating new connections + */ + public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, + final String userName, final String userPassword) { + this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword)); } /** - * Gets the username used to authenticate new connections. + * Gets the user name used to authenticate new connections. + * * @return the user name or null if unauthenticated connections are used */ public String getUsername() { - return username; + return userName; } /** - * Sets the username used to authenticate new connections. - * @param username the username used for authenticating the connection or null for unauthenticated + * Sets the user name used to authenticate new connections. + * + * @param userName + * the user name used for authenticating the connection or null for unauthenticated */ - public void setUsername(final String username) { - this.username = username; + public void setUsername(final String userName) { + this.userName = userName; } /** * Sets the password used to authenticate new connections. - * @param password the password used for authenticating the connection or null for unauthenticated + * + * @param userPassword + * the password used for authenticating the connection or null for unauthenticated. + * @since 2.4.0 */ - public void setPassword(final String password) { - this.password = password; + public void setPassword(final char[] userPassword) { + this.userPassword = userPassword; + } + + /** + * Sets the password used to authenticate new connections. + * + * @param userPassword + * the password used for authenticating the connection or null for unauthenticated + */ + public void setPassword(final String userPassword) { + this.userPassword = Utils.toCharArray(userPassword); } @Override @@ -107,10 +145,10 @@ public class DataSourceXAConnectionFacto public Connection createConnection() throws SQLException { // create a new XAConnection XAConnection xaConnection; - if (username == null) { + if (userName == null) { xaConnection = xaDataSource.getXAConnection(); } else { - xaConnection = xaDataSource.getXAConnection(username, password); + xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); } // get the real connection and XAResource from the connection @@ -143,7 +181,6 @@ public class DataSourceXAConnectionFacto } }); - return connection; } } Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java Tue Jun 19 09:04:08 2018 @@ -19,6 +19,7 @@ package org.apache.tomcat.dbcp.dbcp2.man import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; import javax.transaction.TransactionManager; import javax.transaction.xa.XAException; @@ -28,11 +29,10 @@ import javax.transaction.xa.Xid; import org.apache.tomcat.dbcp.dbcp2.ConnectionFactory; /** - * An implementation of XAConnectionFactory which manages non-XA connections in XA transactions. A non-XA connection + * 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 * @since 2.0 */ public class LocalXAConnectionFactory implements XAConnectionFactory { @@ -40,20 +40,18 @@ public class LocalXAConnectionFactory im private final 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. + * 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 + * @param transactionManager + * the transaction manager in which connections will be enlisted + * @param connectionFactory + * the connection factory from which connections will be retrieved */ - public LocalXAConnectionFactory(final TransactionManager transactionManager, final ConnectionFactory connectionFactory) { - if (transactionManager == null) { - throw new NullPointerException("transactionManager is null"); - } - if (connectionFactory == null) { - throw new NullPointerException("connectionFactory is null"); - } - + public LocalXAConnectionFactory(final TransactionManager transactionManager, + final ConnectionFactory connectionFactory) { + Objects.requireNonNull(transactionManager, "transactionManager is null"); + Objects.requireNonNull(connectionFactory, "connectionFactory is null"); this.transactionRegistry = new TransactionRegistry(transactionManager); this.connectionFactory = connectionFactory; } @@ -78,16 +76,16 @@ public class LocalXAConnectionFactory im } /** - * 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. + * 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. + * 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. + * It is assumed that the wrapper around a managed connection disables the setAutoCommit(), commit(), rollback() and + * setReadOnly() methods while a transaction is in progress. + * * @since 2.0 */ protected static class LocalXAResource implements XAResource { @@ -109,14 +107,17 @@ public class LocalXAConnectionFactory im } /** - * 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 enlisted in another transaction, or if auto-commit - * could not be disabled + * 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 enlisted in another transaction, or if auto-commit could not be + * disabled */ @Override public synchronized void start(final Xid xid, final int flag) throws XAException { @@ -140,13 +141,15 @@ public class LocalXAConnectionFactory im try { connection.setAutoCommit(false); } catch (final SQLException e) { - throw (XAException) new XAException("Count not turn off auto commit for a XA transaction").initCause(e); + throw (XAException) new XAException("Count not turn off auto commit for a XA transaction") + .initCause(e); } this.currentXid = xid; } else if (flag == XAResource.TMRESUME) { if (!xid.equals(this.currentXid)) { - throw new XAException("Attempting to resume in different transaction: expected " + this.currentXid + ", but was " + xid); + throw new XAException("Attempting to resume in different transaction: expected " + this.currentXid + + ", but was " + xid); } } else { throw new XAException("Unknown start flag " + flag); @@ -156,31 +159,33 @@ public class LocalXAConnectionFactory im /** * 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 enlisted in another transaction + * @param xid + * the id of the transaction branch for this connection + * @param flag + * ignored + * @throws XAException + * if the connection is already enlisted in another transaction */ @Override public synchronized void end(final Xid xid, final int flag) throws XAException { - if (xid == null) { - throw new NullPointerException("xid is null"); - } + Objects.requireNonNull(xid, "xid is null"); if (!this.currentXid.equals(xid)) { throw new XAException("Invalid Xid: expected " + this.currentXid + ", 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 + // 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. + * 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 + * @param xid + * the id of the transaction branch for this connection * @return XAResource.XA_RDONLY if the connection.isReadOnly(); XAResource.XA_OK otherwise */ @Override @@ -207,21 +212,21 @@ public class LocalXAConnectionFactory im /** * 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 + * @param xid + * the id of the transaction branch for this connection + * @param flag + * ignored + * @throws XAException + * if connection.commit() throws a SQLException */ @Override public synchronized void commit(final Xid xid, final boolean flag) throws XAException { - if (xid == null) { - throw new NullPointerException("xid is null"); - } + Objects.requireNonNull(xid, "xid is null"); if (this.currentXid == null) { throw new XAException("There is no current transaction"); } if (!this.currentXid.equals(xid)) { - throw new XAException("Invalid Xid: expected " + - this.currentXid + ", but was " + xid); + throw new XAException("Invalid Xid: expected " + this.currentXid + ", but was " + xid); } try { @@ -240,6 +245,7 @@ public class LocalXAConnectionFactory im try { connection.setAutoCommit(originalAutoCommit); } catch (final SQLException e) { + // ignore } this.currentXid = null; } @@ -248,14 +254,14 @@ public class LocalXAConnectionFactory im /** * Rolls back 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 + * @param xid + * the id of the transaction branch for this connection + * @throws XAException + * if connection.rollback() throws a SQLException */ @Override public synchronized void rollback(final Xid xid) throws XAException { - if (xid == null) { - throw new NullPointerException("xid is null"); - } + Objects.requireNonNull(xid, "xid is null"); if (!this.currentXid.equals(xid)) { throw new XAException("Invalid Xid: expected " + this.currentXid + ", but was " + xid); } @@ -268,6 +274,7 @@ public class LocalXAConnectionFactory im try { connection.setAutoCommit(originalAutoCommit); } catch (final SQLException e) { + // Ignore. } this.currentXid = null; } @@ -276,7 +283,8 @@ public class LocalXAConnectionFactory im /** * Returns true if the specified XAResource == this XAResource. * - * @param xaResource the XAResource to test + * @param xaResource + * the XAResource to test * @return true if the specified XAResource == this XAResource; false otherwise */ @Override @@ -287,7 +295,8 @@ public class LocalXAConnectionFactory im /** * Clears the currently associated transaction if it is the specified xid. * - * @param xid the id of the transaction to forget + * @param xid + * the id of the transaction to forget */ @Override public synchronized void forget(final Xid xid) { @@ -297,9 +306,11 @@ public class LocalXAConnectionFactory im } /** - * Always returns a zero length Xid array. The LocalXAConnectionFactory can not support recovery, so no xids will ever be found. + * Always returns a zero length Xid array. The LocalXAConnectionFactory can not support recovery, so no xids + * will ever be found. * - * @param flag ignored since recovery is not supported + * @param flag + * ignored since recovery is not supported * @return always a zero length Xid array. */ @Override @@ -320,7 +331,8 @@ public class LocalXAConnectionFactory im /** * 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 + * @param transactionTimeout + * ignored since we have no way to set a transaction timeout on a JDBC connection * @return always false */ @Override Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java Tue Jun 19 09:04:08 2018 @@ -19,43 +19,59 @@ package org.apache.tomcat.dbcp.dbcp2.man import java.sql.Connection; import java.sql.SQLException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection; import org.apache.tomcat.dbcp.pool2.ObjectPool; /** - * ManagedConnection is responsible for managing a database connection in a transactional environment - * (typically called "Container Managed"). A managed connection operates like any other connection - * when no global 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 - * enlisted in the transaction, but some vendors do not properly implement this. + * ManagedConnection is responsible for managing a database connection in a transactional environment (typically called + * "Container Managed"). A managed connection operates like any other connection when no global 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 enlisted in the transaction, but + * some vendors do not properly implement this. + * <p> + * When enlisted 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. + * </p> * - * When enlisted 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. + * @param <C> + * the Connection type * - * @param <C> the Connection type - * - * @author Dain Sundstrom * @since 2.0 */ public class ManagedConnection<C extends Connection> extends DelegatingConnection<C> { + private final ObjectPool<C> pool; private final TransactionRegistry transactionRegistry; private final boolean accessToUnderlyingConnectionAllowed; private TransactionContext transactionContext; private boolean isSharedConnection; + private final Lock lock; - public ManagedConnection(final ObjectPool<C> pool, - final TransactionRegistry transactionRegistry, + /** + * Constructs a new instance responsible for managing a database connection in a transactional environment. + * + * @param pool + * The connection pool. + * @param transactionRegistry + * The transaction registry. + * @param accessToUnderlyingConnectionAllowed + * Whether or not to allow access to the underlying Connection. + * @throws SQLException + * Thrown when there is problem managing transactions. + */ + public ManagedConnection(final ObjectPool<C> pool, final TransactionRegistry transactionRegistry, final boolean accessToUnderlyingConnectionAllowed) throws SQLException { super(null); this.pool = pool; this.transactionRegistry = transactionRegistry; this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed; + this.lock = new ReentrantLock(); updateTransactionStatus(); } @@ -67,7 +83,7 @@ public class ManagedConnection<C extends 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 != null && !transactionContext.isTransactionComplete()) { if (transactionContext.isActive()) { if (transactionContext != transactionRegistry.getActiveTransactionContext()) { throw new SQLException("Connection can not be used while enlisted in another transaction"); @@ -76,7 +92,7 @@ public class ManagedConnection<C extends } // 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 + // our listener is called. In that rare case, trigger the transaction complete call now transactionComplete(); } @@ -111,8 +127,7 @@ public class ManagedConnection<C extends // always be of type C since it has been shared by another // connection from the same pool. @SuppressWarnings("unchecked") - final - C shared = (C) transactionContext.getSharedConnection(); + final C shared = (C) transactionContext.getSharedConnection(); setDelegate(shared); // remember that we are using a shared connection so it can be cleared after the @@ -162,18 +177,25 @@ public class ManagedConnection<C extends try { // Don't actually close the connection if in a transaction. The // connection will be closed by the transactionComplete method. - if (transactionContext == null) { + // + // DBCP-484 we need to make sure setClosedInternal(true) being + // invoked if transactionContext is not null as this value will + // be modified by the transactionComplete method which could run + // in the different thread with the transaction calling back. + lock.lock(); + if (transactionContext == null || transactionContext.isTransactionComplete()) { super.close(); } } finally { setClosedInternal(true); + lock.unlock(); } } } /** - * Delegates to {@link ManagedConnection#transactionComplete()} - * for transaction completion events. + * Delegates to {@link ManagedConnection#transactionComplete()} for transaction completion events. + * * @since 2.0 */ protected class CompletionListener implements TransactionContextListener { @@ -186,7 +208,9 @@ public class ManagedConnection<C extends } protected void transactionComplete() { - transactionContext = null; + lock.lock(); + transactionContext.completeTransaction(); + lock.unlock(); // If we were using a shared connection, clear the reference now that // the transaction has completed @@ -225,7 +249,6 @@ public class ManagedConnection<C extends super.setAutoCommit(autoCommit); } - @Override public void commit() throws SQLException { if (transactionContext != null) { @@ -242,7 +265,6 @@ public class ManagedConnection<C extends super.rollback(); } - @Override public void setReadOnly(final boolean readOnly) throws SQLException { if (transactionContext != null) { @@ -257,6 +279,7 @@ public class ManagedConnection<C extends /** * If false, getDelegate() and getInnermostDelegate() will return null. + * * @return if false, getDelegate() and getInnermostDelegate() will return null */ public boolean isAccessToUnderlyingConnectionAllowed() { Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java Tue Jun 19 09:04:08 2018 @@ -19,6 +19,7 @@ package org.apache.tomcat.dbcp.dbcp2.man import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; import org.apache.tomcat.dbcp.dbcp2.PoolingDataSource; import org.apache.tomcat.dbcp.pool2.ObjectPool; @@ -26,45 +27,43 @@ import org.apache.tomcat.dbcp.pool2.Obje /** * The ManagedDataSource is a PoolingDataSource that creates ManagedConnections. * - * @author Dain Sundstrom - * @param <C> The kind of {@link Connection} to manage. + * @param <C> + * The kind of {@link Connection} to manage. * @since 2.0 */ public class ManagedDataSource<C extends Connection> extends PoolingDataSource<C> { private TransactionRegistry transactionRegistry; /** - * 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 occur when attempting to use the connection - * in a global transaction because the XAResource object associated with the connection - * will be unavailable. + * 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 occur 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 + * @param pool + * the connection pool + * @param transactionRegistry + * the transaction registry obtained from the XAConnectionFactory used to create the connection pool + * object factory */ - public ManagedDataSource(final ObjectPool<C> pool, - final TransactionRegistry transactionRegistry) { + public ManagedDataSource(final ObjectPool<C> pool, final 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 + * 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(final TransactionRegistry transactionRegistry) { - if(this.transactionRegistry != null) { + if (this.transactionRegistry != null) { throw new IllegalStateException("TransactionRegistry already set"); } - if(transactionRegistry == null) { - throw new NullPointerException("TransactionRegistry is null"); - } + Objects.requireNonNull(transactionRegistry, "transactionRegistry is null"); this.transactionRegistry = transactionRegistry; } @@ -78,7 +77,6 @@ public class ManagedDataSource<C extends throw new IllegalStateException("TransactionRegistry has not been set"); } - final Connection connection = new ManagedConnection<>(getPool(), transactionRegistry, isAccessToUnderlyingConnectionAllowed()); - return connection; + return new ManagedConnection<>(getPool(), transactionRegistry, isAccessToUnderlyingConnectionAllowed()); } } Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java Tue Jun 19 09:04:08 2018 @@ -33,39 +33,43 @@ import org.apache.tomcat.dbcp.pool2.Obje public class PoolableManagedConnection extends PoolableConnection { private final TransactionRegistry transactionRegistry; - /** * Create a PoolableManagedConnection. * - * @param transactionRegistry transaction registry - * @param conn underlying connection - * @param pool connection pool + * @param transactionRegistry + * transaction registry + * @param conn + * underlying connection + * @param pool + * connection pool */ - public PoolableManagedConnection(final TransactionRegistry transactionRegistry, - final Connection conn, final ObjectPool<PoolableConnection> pool) { + public PoolableManagedConnection(final TransactionRegistry transactionRegistry, final Connection conn, + final ObjectPool<PoolableConnection> pool) { this(transactionRegistry, conn, pool, null, false); } - /** * Create a PoolableManagedConnection. * - * @param transactionRegistry transaction registry - * @param conn underlying connection - * @param pool connection pool - * @param disconnectSqlCodes SQL_STATE codes considered fatal disconnection errors - * @param fastFailValidation true means fatal disconnection errors cause subsequent - * validations to fail immediately (no attempt to run query or isValid) + * @param transactionRegistry + * transaction registry + * @param conn + * underlying connection + * @param pool + * connection pool + * @param disconnectSqlCodes + * SQL_STATE codes considered fatal disconnection errors + * @param fastFailValidation + * true means fatal disconnection errors cause subsequent validations to fail immediately (no attempt to + * run query or isValid) */ - public PoolableManagedConnection(final TransactionRegistry transactionRegistry, - final Connection conn, final ObjectPool<PoolableConnection> pool, - final Collection<String> disconnectSqlCodes, + public PoolableManagedConnection(final TransactionRegistry transactionRegistry, final Connection conn, + final ObjectPool<PoolableConnection> pool, final Collection<String> disconnectSqlCodes, final boolean fastFailValidation) { super(conn, pool, null, disconnectSqlCodes, fastFailValidation); this.transactionRegistry = transactionRegistry; } - /** * Actually close the underlying connection. */ Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java Tue Jun 19 09:04:08 2018 @@ -44,26 +44,26 @@ public class PoolableManagedConnectionFa private final TransactionRegistry transactionRegistry; /** - * Create a PoolableManagedConnectionFactory and attach it to a connection pool. + * Creates a PoolableManagedConnectionFactory and attach it to a connection pool. * - * @param connFactory XAConnectionFactory - * @param dataSourceJmxName JMX name of the datasource + * @param connFactory + * XAConnectionFactory + * @param dataSourceJmxName + * The data source name. */ - public PoolableManagedConnectionFactory(final XAConnectionFactory connFactory, - final ObjectName dataSourceJmxName) { + public PoolableManagedConnectionFactory(final XAConnectionFactory connFactory, final ObjectName dataSourceJmxName) { super(connFactory, dataSourceJmxName); this.transactionRegistry = connFactory.getTransactionRegistry(); } /** - * Uses the configured XAConnectionFactory to create a {@link PoolableManagedConnection}. - * Throws <code>IllegalStateException</code> if the connection factory returns null. - * Also initializes the connection using configured initialization sql (if provided) - * and sets up a prepared statement pool associated with the PoolableManagedConnection - * if statement pooling is enabled. + * Uses the configured XAConnectionFactory to create a {@link PoolableManagedConnection}. Throws + * <code>IllegalStateException</code> if the connection factory returns null. Also initializes the connection using + * configured initialization SQL (if provided) and sets up a prepared statement pool associated with the + * PoolableManagedConnection if statement pooling is enabled. */ @Override - public synchronized PooledObject<PoolableConnection> makeObject() throws Exception { + synchronized public PooledObject<PoolableConnection> makeObject() throws Exception { Connection conn = getConnectionFactory().createConnection(); if (conn == null) { throw new IllegalStateException("Connection factory returned null from createConnection"); @@ -71,7 +71,7 @@ public class PoolableManagedConnectionFa initializeConnection(conn); if (getPoolStatements()) { conn = new PoolingConnection(conn); - final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>(); + final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig(); config.setMaxTotalPerKey(-1); config.setBlockWhenExhausted(false); config.setMaxWaitMillis(0); @@ -88,15 +88,14 @@ public class PoolableManagedConnectionFa } else { config.setJmxEnabled(false); } - final KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> stmtPool = - new GenericKeyedObjectPool<>((PoolingConnection)conn, config); - ((PoolingConnection)conn).setStatementPool(stmtPool); + final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>( + (PoolingConnection) conn, config); + ((PoolingConnection) conn).setStatementPool(stmtPool); ((PoolingConnection) conn).setCacheState(getCacheState()); } - final PoolableManagedConnection pmc = - new PoolableManagedConnection(transactionRegistry, conn, getPool(), - getDisconnectionSqlCodes(), isFastFailValidation()); + final PoolableManagedConnection pmc = new PoolableManagedConnection(transactionRegistry, conn, getPool(), + getDisconnectionSqlCodes(), isFastFailValidation()); pmc.setCacheState(getCacheState()); - return new DefaultPooledObject<>(pmc); + return new DefaultPooledObject<PoolableConnection>(pmc); } } Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java Tue Jun 19 09:04:08 2018 @@ -20,6 +20,7 @@ package org.apache.tomcat.dbcp.dbcp2.man import java.lang.ref.WeakReference; import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; import javax.transaction.RollbackException; import javax.transaction.Status; @@ -29,43 +30,39 @@ import javax.transaction.Transaction; import javax.transaction.xa.XAResource; /** - * 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. + * 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 * @since 2.0 */ public class TransactionContext { private final TransactionRegistry transactionRegistry; private final WeakReference<Transaction> transactionRef; private Connection sharedConnection; + private boolean transactionComplete; /** - * 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 + * 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(final TransactionRegistry transactionRegistry, final Transaction transaction) { - if (transactionRegistry == null) { - throw new NullPointerException("transactionRegistry is null"); - } - if (transaction == null) { - throw new NullPointerException("transaction is null"); - } + Objects.requireNonNull(transactionRegistry, "transactionRegistry is null"); + Objects.requireNonNull(transaction, "transaction is null"); this.transactionRegistry = transactionRegistry; this.transactionRef = new WeakReference<>(transaction); + this.transactionComplete = false; } /** - * Gets the connection shared by all ManagedConnections in the transaction. Specifically, - * connection using the same XAConnectionFactory from which the TransactionRegistry was - * obtained. + * 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() { @@ -73,13 +70,13 @@ public class TransactionContext { } /** - * Sets the shared connection for this transaction. The shared connection is enlisted - * in the transaction. + * 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 + * @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(final Connection sharedConnection) throws SQLException { if (this.sharedConnection != null) { @@ -91,9 +88,12 @@ public class TransactionContext { final Transaction transaction = getTransaction(); try { final XAResource xaResource = transactionRegistry.getXAResource(sharedConnection); - if ( !transaction.enlistResource(xaResource) ) { + if (!transaction.enlistResource(xaResource)) { throw new SQLException("Unable to enlist connection in transaction: enlistResource returns 'false'."); } + } catch (final IllegalStateException e) { + // This can happen if the transaction is already timed out + throw new SQLException("Unable to enlist connection in the transaction", e); } catch (final RollbackException e) { // transaction was rolled back... proceed as if there never was a transaction } catch (final SystemException e) { @@ -106,14 +106,17 @@ public class TransactionContext { /** * 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 + * @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 { getTransaction().registerSynchronization(new Synchronization() { @Override public void beforeCompletion() { + // empty } @Override @@ -131,8 +134,10 @@ public class TransactionContext { /** * 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 + * @throws SQLException + * if a problem occurs obtaining the transaction status */ public boolean isActive() throws SQLException { try { @@ -154,4 +159,24 @@ public class TransactionContext { } return transaction; } + + /** + * Sets the transaction complete flag to true. + * + * @since 2.4.0 + */ + public void completeTransaction() { + this.transactionComplete = true; + } + + /** + * Gets the transaction complete flag to true. + * + * @return The transaction complete flag. + * + * @since 2.4.0 + */ + public boolean isTransactionComplete() { + return this.transactionComplete; + } } Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java?rev=1833794&r1=1833793&r2=1833794&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java (original) +++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java Tue Jun 19 09:04:08 2018 @@ -20,14 +20,16 @@ package org.apache.tomcat.dbcp.dbcp2.man /** * A listener for transaction completion events. * - * @author Dain Sundstrom * @since 2.0 */ 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 + * + * @param transactionContext + * the transaction context that completed + * @param commited + * true if the transaction committed; false otherwise */ void afterCompletion(TransactionContext transactionContext, boolean commited); } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org