SharedPoolDataSource.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2.datasources;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.sql.Connection;
  21. import java.sql.SQLException;

  22. import javax.naming.NamingException;
  23. import javax.naming.Reference;
  24. import javax.naming.StringRefAddr;
  25. import javax.sql.ConnectionPoolDataSource;

  26. import org.apache.commons.pool2.KeyedObjectPool;
  27. import org.apache.commons.pool2.KeyedPooledObjectFactory;
  28. import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
  29. import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;

  30. /**
  31.  * <p>
  32.  * A pooling {@code DataSource} appropriate for deployment within J2EE environment. There are many configuration
  33.  * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number
  34.  * of Connections in this data source.
  35.  * </p>
  36.  *
  37.  * <p>
  38.  * User passwords can be changed without re-initializing the data source. When a
  39.  * {@code getConnection(user name, password)} request is processed with a password that is different from those
  40.  * used to create connections in the pool associated with {@code user name}, an attempt is made to create a new
  41.  * connection using the supplied password and if this succeeds, idle connections created using the old password are
  42.  * destroyed and new connections are created using the new password.
  43.  * </p>
  44.  *
  45.  * @since 2.0
  46.  */
  47. public class SharedPoolDataSource extends InstanceKeyDataSource {

  48.     private static final long serialVersionUID = -1458539734480586454L;

  49.     /**
  50.      * Max total defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_TOTAL}.
  51.      */
  52.     private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;

  53.     /**
  54.      * Maps user credentials to pooled connection with credentials.
  55.      */
  56.     private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;

  57.     /**
  58.      * A {@link KeyedPooledObjectFactory} that creates {@link PoolableConnection}s.
  59.      */
  60.     private transient KeyedCPDSConnectionFactory factory;

  61.     /**
  62.      * Default no-argument constructor for Serialization
  63.      */
  64.     public SharedPoolDataSource() {
  65.         // empty.
  66.     }

  67.     /**
  68.      * Closes pool being maintained by this data source.
  69.      */
  70.     @Override
  71.     public void close() throws SQLException {
  72.         if (pool != null) {
  73.             pool.close();
  74.         }
  75.         InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
  76.     }

  77.     @Override
  78.     protected PooledConnectionManager getConnectionManager(final UserPassKey userPassKey) {
  79.         return factory;
  80.     }

  81.     /**
  82.      * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
  83.      *
  84.      * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
  85.      */
  86.     public int getMaxTotal() {
  87.         return this.maxTotal;
  88.     }

  89.     /**
  90.      * Gets the number of active connections in the pool.
  91.      *
  92.      * @return The number of active connections in the pool.
  93.      */
  94.     public int getNumActive() {
  95.         return pool == null ? 0 : pool.getNumActive();
  96.     }

  97.     /**
  98.      * Gets the number of idle connections in the pool.
  99.      *
  100.      * @return The number of idle connections in the pool.
  101.      */
  102.     public int getNumIdle() {
  103.         return pool == null ? 0 : pool.getNumIdle();
  104.     }

  105.     @Override
  106.     protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword)
  107.             throws SQLException {

  108.         synchronized (this) {
  109.             if (pool == null) {
  110.                 try {
  111.                     registerPool(userName, userPassword);
  112.                 } catch (final NamingException e) {
  113.                     throw new SQLException("registerPool failed", e);
  114.                 }
  115.             }
  116.         }

  117.         try {
  118.             return pool.borrowObject(new UserPassKey(userName, userPassword));
  119.         } catch (final Exception e) {
  120.             throw new SQLException("Could not retrieve connection info from pool", e);
  121.         }
  122.     }

  123.     /**
  124.      * Creates a new {@link Reference} to a {@link SharedPoolDataSource}.
  125.      */
  126.     @Override
  127.     public Reference getReference() throws NamingException {
  128.         final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null);
  129.         ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
  130.         return ref;
  131.     }

  132.     /**
  133.      * Deserializes an instance from an ObjectInputStream.
  134.      *
  135.      * @param in The source ObjectInputStream.
  136.      * @throws IOException            Any of the usual Input/Output related exceptions.
  137.      * @throws ClassNotFoundException A class of a serialized object cannot be found.
  138.      */
  139.     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
  140.         in.defaultReadObject();
  141.         this.pool = readObjectImpl();
  142.     }

  143.     private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> readObjectImpl() throws IOException, ClassNotFoundException {
  144.         try {
  145.             return ((SharedPoolDataSource) new SharedPoolDataSourceFactory().getObjectInstance(getReference(), null, null, null)).pool;
  146.         } catch (final NamingException e) {
  147.             throw new IOException("NamingException: " + e);
  148.         }
  149.     }

  150.     private void registerPool(final String userName, final String password) throws NamingException, SQLException {

  151.         final ConnectionPoolDataSource cpds = testCPDS(userName, password);

  152.         // Create an object pool to contain our PooledConnections
  153.         factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeoutDuration(), isRollbackAfterValidation());
  154.         factory.setMaxConn(getMaxConnDuration());

  155.         final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = new GenericKeyedObjectPoolConfig<>();
  156.         config.setBlockWhenExhausted(getDefaultBlockWhenExhausted());
  157.         config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName());
  158.         config.setLifo(getDefaultLifo());
  159.         config.setMaxIdlePerKey(getDefaultMaxIdle());
  160.         config.setMaxTotal(getMaxTotal());
  161.         config.setMaxTotalPerKey(getDefaultMaxTotal());
  162.         config.setMaxWait(getDefaultMaxWait());
  163.         config.setMinEvictableIdleDuration(getDefaultMinEvictableIdleDuration());
  164.         config.setMinIdlePerKey(getDefaultMinIdle());
  165.         config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun());
  166.         config.setSoftMinEvictableIdleDuration(getDefaultSoftMinEvictableIdleDuration());
  167.         config.setTestOnCreate(getDefaultTestOnCreate());
  168.         config.setTestOnBorrow(getDefaultTestOnBorrow());
  169.         config.setTestOnReturn(getDefaultTestOnReturn());
  170.         config.setTestWhileIdle(getDefaultTestWhileIdle());
  171.         config.setTimeBetweenEvictionRuns(getDefaultDurationBetweenEvictionRuns());

  172.         final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, config);
  173.         factory.setPool(tmpPool);
  174.         pool = tmpPool;
  175.     }

  176.     /**
  177.      * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
  178.      *
  179.      * @param maxTotal
  180.      *            {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
  181.      */
  182.     public void setMaxTotal(final int maxTotal) {
  183.         assertInitializationAllowed();
  184.         this.maxTotal = maxTotal;
  185.     }

  186.     @Override
  187.     protected void setupDefaults(final Connection connection, final String userName) throws SQLException {
  188.         final Boolean defaultAutoCommit = isDefaultAutoCommit();
  189.         if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit) {
  190.             connection.setAutoCommit(defaultAutoCommit);
  191.         }

  192.         final int defaultTransactionIsolation = getDefaultTransactionIsolation();
  193.         if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
  194.             connection.setTransactionIsolation(defaultTransactionIsolation);
  195.         }

  196.         final Boolean defaultReadOnly = isDefaultReadOnly();
  197.         if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly) {
  198.             connection.setReadOnly(defaultReadOnly);
  199.         }
  200.     }

  201.     @Override
  202.     protected void toStringFields(final StringBuilder builder) {
  203.         super.toStringFields(builder);
  204.         builder.append(", maxTotal=");
  205.         builder.append(maxTotal);
  206.     }
  207. }