    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.dbcp2.datasources;
    018
    019import java.io.IOException;
    020import java.io.ObjectInputStream;
    021import java.sql.Connection;
    022import java.sql.SQLException;
    023import java.util.HashMap;
    024import java.util.Map;
    025import java.util.NoSuchElementException;
    026
    027import javax.naming.NamingException;
    028import javax.naming.Reference;
    029import javax.naming.StringRefAddr;
    030import javax.sql.ConnectionPoolDataSource;
    031
    032import org.apache.commons.dbcp2.SwallowedExceptionLogger;
    033import org.apache.commons.logging.Log;
    034import org.apache.commons.logging.LogFactory;
    035import org.apache.commons.pool2.ObjectPool;
    036import org.apache.commons.pool2.impl.GenericObjectPool;
    037
    038/**
    039 * <p>
    040 * A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration
    041 * options, most of which are defined in the parent class. This datasource uses individual pools per user, and some
    042 * properties can be set specifically for a given user, if the deployment environment can support initialization of
    043 * mapped properties. So for example, a pool of admin or write-access Connections can be guaranteed a certain number of
    044 * connections, separate from a maximum set for users with read-only connections.
    045 * </p>
    046 *
    047 * <p>
    048 * User passwords can be changed without re-initializing the datasource. When a
    049 * <code>getConnection(userName, password)</code> request is processed with a password that is different from those used
    050 * to create connections in the pool associated with <code>userName</code>, an attempt is made to create a new
    051 * connection using the supplied password and if this succeeds, the existing pool is cleared and a new pool is created
    052 * for connections using the new password.
    053 * </p>
    054 *
    055 * @since 2.0
    056 */
    057public class PerUserPoolDataSource extends InstanceKeyDataSource {
    058
    059    private static final long serialVersionUID = 7872747993848065028L;
    060
    061    private static final Log log = LogFactory.getLog(PerUserPoolDataSource.class);
    062
    063    // Per user pool properties
    064    private Map<String, Boolean> perUserBlockWhenExhausted;
    065    private Map<String, String> perUserEvictionPolicyClassName;
    066    private Map<String, Boolean> perUserLifo;
    067    private Map<String, Integer> perUserMaxIdle;
    068    private Map<String, Integer> perUserMaxTotal;
    069    private Map<String, Long> perUserMaxWaitMillis;
    070    private Map<String, Long> perUserMinEvictableIdleTimeMillis;
    071    private Map<String, Integer> perUserMinIdle;
    072    private Map<String, Integer> perUserNumTestsPerEvictionRun;
    073    private Map<String, Long> perUserSoftMinEvictableIdleTimeMillis;
    074    private Map<String, Boolean> perUserTestOnCreate;
    075    private Map<String, Boolean> perUserTestOnBorrow;
    076    private Map<String, Boolean> perUserTestOnReturn;
    077    private Map<String, Boolean> perUserTestWhileIdle;
    078    private Map<String, Long> perUserTimeBetweenEvictionRunsMillis;
    079
    080    // Per user connection properties
    081    private Map<String, Boolean> perUserDefaultAutoCommit;
    082    private Map<String, Integer> perUserDefaultTransactionIsolation;
    083    private Map<String, Boolean> perUserDefaultReadOnly;
    084
    085    /**
    086     * Map to keep track of Pools for a given user.
    087     */
    088    private transient Map<PoolKey, PooledConnectionManager> managers = new HashMap<>();
    089
    090    /**
    091     * Default no-arg constructor for Serialization.
    092     */
    093    public PerUserPoolDataSource() {
    094    }
    095
    096    /**
    097     * Clears pool(s) maintained by this data source.
    098     *
    099     * @see org.apache.commons.pool2.ObjectPool#clear()
    100     * @since 2.3.0
    101     */
    102    public void clear() {
    103        for (final PooledConnectionManager manager : managers.values()) {
    104            try {
    105                getCPDSConnectionFactoryPool(manager).clear();
    106            } catch (final Exception closePoolException) {
    107                // ignore and try to close others.
    108            }
    109        }
    110        InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
    111    }
    112
    113    /**
    114     * Closes pool(s) maintained by this data source.
    115     *
    116     * @see org.apache.commons.pool2.ObjectPool#close()
    117     */
    118    @Override
    119    public void close() {
    120        for (final PooledConnectionManager manager : managers.values()) {
    121            try {
    122                getCPDSConnectionFactoryPool(manager).close();
    123            } catch (final Exception closePoolException) {
    124                // ignore and try to close others.
    125            }
    126        }
    127        InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
    128    }
    129
    130    private HashMap<String, Boolean> createMap() {
    131        // Should there be a default size different than what this ctor provides?
    132        return new HashMap<>();
    133    }
    134
    135    @Override
    136    protected PooledConnectionManager getConnectionManager(final UserPassKey upKey) {
    137        return managers.get(getPoolKey(upKey.getUsername()));
    138    }
    139
    140    private ObjectPool<PooledConnectionAndInfo> getCPDSConnectionFactoryPool(final PooledConnectionManager manager) {
    141        return ((CPDSConnectionFactory) manager).getPool();
    142    }
    143
    144    /**
    145     * Gets the number of active connections in the default pool.
    146     *
    147     * @return The number of active connections in the default pool.
    148     */
    149    public int getNumActive() {
    150        return getNumActive(null);
    151    }
    152
    153    /**
    154     * Gets the number of active connections in the pool for a given user.
    155     *
    156     * @param userName
    157     *            The user name key.
    158     * @return The user specific value.
    159     */
    160    @SuppressWarnings("resource")
    161    public int getNumActive(final String userName) {
    162        final ObjectPool<PooledConnectionAndInfo> pool = getPool(getPoolKey(userName));
    163        return pool == null ? 0 : pool.getNumActive();
    164    }
    165
    166    /**
    167     * Gets the number of idle connections in the default pool.
    168     *
    169     * @return The number of idle connections in the default pool.
    170     */
    171    public int getNumIdle() {
    172        return getNumIdle(null);
    173    }
    174
    175    /**
    176     * Gets the number of idle connections in the pool for a given user.
    177     *
    178     * @param userName
    179     *            The user name key.
    180     * @return The user specific value.
    181     */
    182    @SuppressWarnings("resource")
    183    public int getNumIdle(final String userName) {
    184        final ObjectPool<PooledConnectionAndInfo> pool = getPool(getPoolKey(userName));
    185        return pool == null ? 0 : pool.getNumIdle();
    186    }
    187
    188    /**
    189     * Gets the user specific value for {@link GenericObjectPool#getBlockWhenExhausted()} for the specified user's pool
    190     * or the default if no user specific value is defined.
    191     *
    192     * @param userName
    193     *            The user name key.
    194     * @return The user specific value.
    195     */
    196    public boolean getPerUserBlockWhenExhausted(final String userName) {
    197        Boolean value = null;
    198        if (perUserBlockWhenExhausted != null) {
    199            value = perUserBlockWhenExhausted.get(userName);
    200        }
    201        if (value == null) {
    202            return getDefaultBlockWhenExhausted();
    203        }
    204        return value.booleanValue();
    205    }
    206
    207    /**
    208     * Gets the user specific default value for {@link Connection#setAutoCommit(boolean)} for the specified user's pool.
    209     *
    210     * @param userName
    211     *            The user name key.
    212     * @return The user specific value.
    213     */
    214    public Boolean getPerUserDefaultAutoCommit(final String userName) {
    215        Boolean value = null;
    216        if (perUserDefaultAutoCommit != null) {
    217            value = perUserDefaultAutoCommit.get(userName);
    218        }
    219        return value;
    220    }
    221
    222    /**
    223     * Gets the user specific default value for {@link Connection#setReadOnly(boolean)} for the specified user's pool.
    224     *
    225     * @param userName
    226     *            The user name key.
    227     * @return The user specific value.
    228     */
    229    public Boolean getPerUserDefaultReadOnly(final String userName) {
    230        Boolean value = null;
    231        if (perUserDefaultReadOnly != null) {
    232            value = perUserDefaultReadOnly.get(userName);
    233        }
    234        return value;
    235    }
    236
    237    /**
    238     * Gets the user specific default value for {@link Connection#setTransactionIsolation(int)} for the specified user's
    239     * pool.
    240     *
    241     * @param userName
    242     *            The user name key.
    243     * @return The user specific value.
    244     */
    245    public Integer getPerUserDefaultTransactionIsolation(final String userName) {
    246        Integer value = null;
    247        if (perUserDefaultTransactionIsolation != null) {
    248            value = perUserDefaultTransactionIsolation.get(userName);
    249        }
    250        return value;
    251    }
    252
    253    /**
    254     * Gets the user specific value for {@link GenericObjectPool#getEvictionPolicyClassName()} for the specified user's
    255     * pool or the default if no user specific value is defined.
    256     *
    257     * @param userName
    258     *            The user name key.
    259     * @return The user specific value.
    260     */
    261    public String getPerUserEvictionPolicyClassName(final String userName) {
    262        String value = null;
    263        if (perUserEvictionPolicyClassName != null) {
    264            value = perUserEvictionPolicyClassName.get(userName);
    265        }
    266        if (value == null) {
    267            return getDefaultEvictionPolicyClassName();
    268        }
    269        return value;
    270    }
    271
    272    /**
    273     * Gets the user specific value for {@link GenericObjectPool#getLifo()} for the specified user's pool or the default
    274     * if no user specific value is defined.
    275     *
    276     * @param userName
    277     *            The user name key.
    278     * @return The user specific value.
    279     */
    280    public boolean getPerUserLifo(final String userName) {
    281        Boolean value = null;
    282        if (perUserLifo != null) {
    283            value = perUserLifo.get(userName);
    284        }
    285        if (value == null) {
    286            return getDefaultLifo();
    287        }
    288        return value.booleanValue();
    289    }
    290
    291    /**
    292     * Gets the user specific value for {@link GenericObjectPool#getMaxIdle()} for the specified user's pool or the
    293     * default if no user specific value is defined.
    294     *
    295     * @param userName
    296     *            The user name key.
    297     * @return The user specific value.
    298     */
    299    public int getPerUserMaxIdle(final String userName) {
    300        Integer value = null;
    301        if (perUserMaxIdle != null) {
    302            value = perUserMaxIdle.get(userName);
    303        }
    304        if (value == null) {
    305            return getDefaultMaxIdle();
    306        }
    307        return value.intValue();
    308    }
    309
    310    /**
    311     * Gets the user specific value for {@link GenericObjectPool#getMaxTotal()} for the specified user's pool or the
    312     * default if no user specific value is defined.
    313     *
    314     * @param userName
    315     *            The user name key.
    316     * @return The user specific value.
    317     */
    318    public int getPerUserMaxTotal(final String userName) {
    319        Integer value = null;
    320        if (perUserMaxTotal != null) {
    321            value = perUserMaxTotal.get(userName);
    322        }
    323        if (value == null) {
    324            return getDefaultMaxTotal();
    325        }
    326        return value.intValue();
    327    }
    328
    329    /**
    330     * Gets the user specific value for {@link GenericObjectPool#getMaxWaitMillis()} for the specified user's pool or
    331     * the default if no user specific value is defined.
    332     *
    333     * @param userName
    334     *            The user name key.
    335     * @return The user specific value.
    336     */
    337    public long getPerUserMaxWaitMillis(final String userName) {
    338        Long value = null;
    339        if (perUserMaxWaitMillis != null) {
    340            value = perUserMaxWaitMillis.get(userName);
    341        }
    342        if (value == null) {
    343            return getDefaultMaxWaitMillis();
    344        }
    345        return value.longValue();
    346    }
    347
    348    /**
    349     * Gets the user specific value for {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the specified
    350     * user's pool or the default if no user specific value is defined.
    351     *
    352     * @param userName
    353     *            The user name key.
    354     * @return The user specific value.
    355     */
    356    public long getPerUserMinEvictableIdleTimeMillis(final String userName) {
    357        Long value = null;
    358        if (perUserMinEvictableIdleTimeMillis != null) {
    359            value = perUserMinEvictableIdleTimeMillis.get(userName);
    360        }
    361        if (value == null) {
    362            return getDefaultMinEvictableIdleTimeMillis();
    363        }
    364        return value.longValue();
    365    }
    366
    367    /**
    368     * Gets the user specific value for {@link GenericObjectPool#getMinIdle()} for the specified user's pool or the
    369     * default if no user specific value is defined.
    370     *
    371     * @param userName
    372     *            The user name key.
    373     * @return The user specific value.
    374     */
    375    public int getPerUserMinIdle(final String userName) {
    376        Integer value = null;
    377        if (perUserMinIdle != null) {
    378            value = perUserMinIdle.get(userName);
    379        }
    380        if (value == null) {
    381            return getDefaultMinIdle();
    382        }
    383        return value.intValue();
    384    }
    385
    386    /**
    387     * Gets the user specific value for {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the specified user's
    388     * pool or the default if no user specific value is defined.
    389     *
    390     * @param userName
    391     *            The user name key.
    392     * @return The user specific value.
    393     */
    394    public int getPerUserNumTestsPerEvictionRun(final String userName) {
    395        Integer value = null;
    396        if (perUserNumTestsPerEvictionRun != null) {
    397            value = perUserNumTestsPerEvictionRun.get(userName);
    398        }
    399        if (value == null) {
    400            return getDefaultNumTestsPerEvictionRun();
    401        }
    402        return value.intValue();
    403    }
    404
    405    /**
    406     * Gets the user specific value for {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the specified
    407     * user's pool or the default if no user specific value is defined.
    408     *
    409     * @param userName
    410     *            The user name key.
    411     * @return The user specific value.
    412     */
    413    public long getPerUserSoftMinEvictableIdleTimeMillis(final String userName) {
    414        Long value = null;
    415        if (perUserSoftMinEvictableIdleTimeMillis != null) {
    416            value = perUserSoftMinEvictableIdleTimeMillis.get(userName);
    417        }
    418        if (value == null) {
    419            return getDefaultSoftMinEvictableIdleTimeMillis();
    420        }
    421        return value.longValue();
    422    }
    423
    424    /**
    425     * Gets the user specific value for {@link GenericObjectPool#getTestOnBorrow()} for the specified user's pool or the
    426     * default if no user specific value is defined.
    427     *
    428     * @param userName
    429     *            The user name key.
    430     * @return The user specific value.
    431     */
    432    public boolean getPerUserTestOnBorrow(final String userName) {
    433        Boolean value = null;
    434        if (perUserTestOnBorrow != null) {
    435            value = perUserTestOnBorrow.get(userName);
    436        }
    437        if (value == null) {
    438            return getDefaultTestOnBorrow();
    439        }
    440        return value.booleanValue();
    441    }
    442
    443    /**
    444     * Gets the user specific value for {@link GenericObjectPool#getTestOnCreate()} for the specified user's pool or the
    445     * default if no user specific value is defined.
    446     *
    447     * @param userName
    448     *            The user name key.
    449     * @return The user specific value.
    450     */
    451    public boolean getPerUserTestOnCreate(final String userName) {
    452        Boolean value = null;
    453        if (perUserTestOnCreate != null) {
    454            value = perUserTestOnCreate.get(userName);
    455        }
    456        if (value == null) {
    457            return getDefaultTestOnCreate();
    458        }
    459        return value.booleanValue();
    460    }
    461
    462    /**
    463     * Gets the user specific value for {@link GenericObjectPool#getTestOnReturn()} for the specified user's pool or the
    464     * default if no user specific value is defined.
    465     *
    466     * @param userName
    467     *            The user name key.
    468     * @return The user specific value.
    469     */
    470    public boolean getPerUserTestOnReturn(final String userName) {
    471        Boolean value = null;
    472        if (perUserTestOnReturn != null) {
    473            value = perUserTestOnReturn.get(userName);
    474        }
    475        if (value == null) {
    476            return getDefaultTestOnReturn();
    477        }
    478        return value.booleanValue();
    479    }
    480
    481    /**
    482     * Gets the user specific value for {@link GenericObjectPool#getTestWhileIdle()} for the specified user's pool or
    483     * the default if no user specific value is defined.
    484     *
    485     * @param userName
    486     *            The user name key.
    487     * @return The user specific value.
    488     */
    489    public boolean getPerUserTestWhileIdle(final String userName) {
    490        Boolean value = null;
    491        if (perUserTestWhileIdle != null) {
    492            value = perUserTestWhileIdle.get(userName);
    493        }
    494        if (value == null) {
    495            return getDefaultTestWhileIdle();
    496        }
    497        return value.booleanValue();
    498    }
    499
    500    /**
    501     * Gets the user specific value for {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} for the specified
    502     * user's pool or the default if no user specific value is defined.
    503     *
    504     * @param userName
    505     *            The user name key.
    506     * @return The user specific value.
    507     */
    508    public long getPerUserTimeBetweenEvictionRunsMillis(final String userName) {
    509        Long value = null;
    510        if (perUserTimeBetweenEvictionRunsMillis != null) {
    511            value = perUserTimeBetweenEvictionRunsMillis.get(userName);
    512        }
    513        if (value == null) {
    514            return getDefaultTimeBetweenEvictionRunsMillis();
    515        }
    516        return value.longValue();
    517    }
    518
    519    /**
    520     * Returns the object pool associated with the given PoolKey.
    521     *
    522     * @param poolKey
    523     *            PoolKey identifying the pool
    524     * @return the GenericObjectPool pooling connections for the userName and datasource specified by the PoolKey
    525     */
    526    private ObjectPool<PooledConnectionAndInfo> getPool(final PoolKey poolKey) {
    527        final CPDSConnectionFactory mgr = (CPDSConnectionFactory) managers.get(poolKey);
    528        return mgr == null ? null : mgr.getPool();
    529    }
    530
    531    @Override
    532    protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String password)
    533            throws SQLException {
    534
    535        final PoolKey key = getPoolKey(userName);
    536        ObjectPool<PooledConnectionAndInfo> pool;
    537        PooledConnectionManager manager;
    538        synchronized (this) {
    539            manager = managers.get(key);
    540            if (manager == null) {
    541                try {
    542                    registerPool(userName, password);
    543                    manager = managers.get(key);
    544                } catch (final NamingException e) {
    545                    throw new SQLException("RegisterPool failed", e);
    546                }
    547            }
    548            pool = getCPDSConnectionFactoryPool(manager);
    549        }
    550
    551        PooledConnectionAndInfo info = null;
    552        try {
    553            info = pool.borrowObject();
    554        } catch (final NoSuchElementException ex) {
    555            throw new SQLException("Could not retrieve connection info from pool", ex);
    556        } catch (final Exception e) {
    557            // See if failure is due to CPDSConnectionFactory authentication failure
    558            try {
    559                testCPDS(userName, password);
    560            } catch (final Exception ex) {
    561                throw new SQLException("Could not retrieve connection info from pool", ex);
    562            }
    563            // New password works, so kill the old pool, create a new one, and borrow
    564            manager.closePool(userName);
    565            synchronized (this) {
    566                managers.remove(key);
    567            }
    568            try {
    569                registerPool(userName, password);
    570                pool = getPool(key);
    571            } catch (final NamingException ne) {
    572                throw new SQLException("RegisterPool failed", ne);
    573            }
    574            try {
    575                info = pool.borrowObject();
    576            } catch (final Exception ex) {
    577                throw new SQLException("Could not retrieve connection info from pool", ex);
    578            }
    579        }
    580        return info;
    581    }
    582
    583    /**
    584     * Creates a pool key from the provided parameters.
    585     *
    586     * @param userName
    587     *            User name
    588     * @return The pool key
    589     */
    590    private PoolKey getPoolKey(final String userName) {
    591        return new PoolKey(getDataSourceName(), userName);
    592    }
    593
    594    /**
    595     * Returns a <code>PerUserPoolDataSource</code> {@link Reference}.
    596     */
    597    @Override
    598    public Reference getReference() throws NamingException {
    599        final Reference ref = new Reference(getClass().getName(), PerUserPoolDataSourceFactory.class.getName(), null);
    600        ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
    601        return ref;
    602    }
    603
    604    /**
    605     * Supports Serialization interface.
    606     *
    607     * @param in
    608     *            a <code>java.io.ObjectInputStream</code> value
    609     * @throws IOException
    610     *             if an error occurs
    611     * @throws ClassNotFoundException
    612     *             if an error occurs
    613     */
    614    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
    615        try {
    616            in.defaultReadObject();
    617            final PerUserPoolDataSource oldDS = (PerUserPoolDataSource) new PerUserPoolDataSourceFactory()
    618                    .getObjectInstance(getReference(), null, null, null);
    619            this.managers = oldDS.managers;
    620        } catch (final NamingException e) {
    621            throw new IOException("NamingException: " + e);
    622        }
    623    }
    624
    625    private synchronized void registerPool(final String userName, final String password)
    626            throws NamingException, SQLException {
    627
    628        final ConnectionPoolDataSource cpds = testCPDS(userName, password);
    629
    630        // Set up the factory we will use (passing the pool associates
    631        // the factory with the pool, so we do not have to do so
    632        // explicitly)
    633        final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, getValidationQuery(),
    634                getValidationQueryTimeout(), isRollbackAfterValidation(), userName, password);
    635        factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
    636
    637        // Create an object pool to contain our PooledConnections
    638        final GenericObjectPool<PooledConnectionAndInfo> pool = new GenericObjectPool<>(factory);
    639        factory.setPool(pool);
    640        pool.setBlockWhenExhausted(getPerUserBlockWhenExhausted(userName));
    641        pool.setEvictionPolicyClassName(getPerUserEvictionPolicyClassName(userName));
    642        pool.setLifo(getPerUserLifo(userName));
    643        pool.setMaxIdle(getPerUserMaxIdle(userName));
    644        pool.setMaxTotal(getPerUserMaxTotal(userName));
    645        pool.setMaxWaitMillis(getPerUserMaxWaitMillis(userName));
    646        pool.setMinEvictableIdleTimeMillis(getPerUserMinEvictableIdleTimeMillis(userName));
    647        pool.setMinIdle(getPerUserMinIdle(userName));
    648        pool.setNumTestsPerEvictionRun(getPerUserNumTestsPerEvictionRun(userName));
    649        pool.setSoftMinEvictableIdleTimeMillis(getPerUserSoftMinEvictableIdleTimeMillis(userName));
    650        pool.setTestOnCreate(getPerUserTestOnCreate(userName));
    651        pool.setTestOnBorrow(getPerUserTestOnBorrow(userName));
    652        pool.setTestOnReturn(getPerUserTestOnReturn(userName));
    653        pool.setTestWhileIdle(getPerUserTestWhileIdle(userName));
    654        pool.setTimeBetweenEvictionRunsMillis(getPerUserTimeBetweenEvictionRunsMillis(userName));
    655
    656        pool.setSwallowedExceptionListener(new SwallowedExceptionLogger(log));
    657
    658        final Object old = managers.put(getPoolKey(userName), factory);
    659        if (old != null) {
    660            throw new IllegalStateException("Pool already contains an entry for this user/password: " + userName);
    661        }
    662    }
    663
    664    void setPerUserBlockWhenExhausted(final Map<String, Boolean> userDefaultBlockWhenExhausted) {
    665        assertInitializationAllowed();
    666        if (perUserBlockWhenExhausted == null) {
    667            perUserBlockWhenExhausted = createMap();
    668        } else {
    669            perUserBlockWhenExhausted.clear();
    670        }
    671        perUserBlockWhenExhausted.putAll(userDefaultBlockWhenExhausted);
    672    }
    673
    674    /**
    675     * Sets a user specific value for {@link GenericObjectPool#getBlockWhenExhausted()} for the specified user's pool.
    676     *
    677     * @param userName
    678     *            The user name key.
    679     * @param value
    680     *            The user specific value.
    681     */
    682    public void setPerUserBlockWhenExhausted(final String userName, final Boolean value) {
    683        assertInitializationAllowed();
    684        if (perUserBlockWhenExhausted == null) {
    685            perUserBlockWhenExhausted = createMap();
    686        }
    687        perUserBlockWhenExhausted.put(userName, value);
    688    }
    689
    690    void setPerUserDefaultAutoCommit(final Map<String, Boolean> userDefaultAutoCommit) {
    691        assertInitializationAllowed();
    692        if (perUserDefaultAutoCommit == null) {
    693            perUserDefaultAutoCommit = createMap();
    694        } else {
    695            perUserDefaultAutoCommit.clear();
    696        }
    697        perUserDefaultAutoCommit.putAll(userDefaultAutoCommit);
    698    }
    699
    700    /**
    701     * Sets a user specific default value for {@link Connection#setAutoCommit(boolean)} for the specified user's pool.
    702     *
    703     * @param userName
    704     *            The user name key.
    705     * @param value
    706     *            The user specific value.
    707     */
    708    public void setPerUserDefaultAutoCommit(final String userName, final Boolean value) {
    709        assertInitializationAllowed();
    710        if (perUserDefaultAutoCommit == null) {
    711            perUserDefaultAutoCommit = createMap();
    712        }
    713        perUserDefaultAutoCommit.put(userName, value);
    714    }
    715
    716    void setPerUserDefaultReadOnly(final Map<String, Boolean> userDefaultReadOnly) {
    717        assertInitializationAllowed();
    718        if (perUserDefaultReadOnly == null) {
    719            perUserDefaultReadOnly = createMap();
    720        } else {
    721            perUserDefaultReadOnly.clear();
    722        }
    723        perUserDefaultReadOnly.putAll(userDefaultReadOnly);
    724    }
    725
    726    /**
    727     * Sets a user specific default value for {@link Connection#setReadOnly(boolean)} for the specified user's pool.
    728     *
    729     * @param userName
    730     *            The user name key.
    731     * @param value
    732     *            The user specific value.
    733     */
    734    public void setPerUserDefaultReadOnly(final String userName, final Boolean value) {
    735        assertInitializationAllowed();
    736        if (perUserDefaultReadOnly == null) {
    737            perUserDefaultReadOnly = createMap();
    738        }
    739        perUserDefaultReadOnly.put(userName, value);
    740    }
    741
    742    void setPerUserDefaultTransactionIsolation(final Map<String, Integer> userDefaultTransactionIsolation) {
    743        assertInitializationAllowed();
    744        if (perUserDefaultTransactionIsolation == null) {
    745            perUserDefaultTransactionIsolation = new HashMap<>();
    746        } else {
    747            perUserDefaultTransactionIsolation.clear();
    748        }
    749        perUserDefaultTransactionIsolation.putAll(userDefaultTransactionIsolation);
    750    }
    751
    752    /**
    753     * Sets a user specific default value for {@link Connection#setTransactionIsolation(int)} for the specified user's
    754     * pool.
    755     *
    756     * @param userName
    757     *            The user name key.
    758     * @param value
    759     *            The user specific value.
    760     */
    761    public void setPerUserDefaultTransactionIsolation(final String userName, final Integer value) {
    762        assertInitializationAllowed();
    763        if (perUserDefaultTransactionIsolation == null) {
    764            perUserDefaultTransactionIsolation = new HashMap<>();
    765        }
    766        perUserDefaultTransactionIsolation.put(userName, value);
    767    }
    768
    769    void setPerUserEvictionPolicyClassName(final Map<String, String> userDefaultEvictionPolicyClassName) {
    770        assertInitializationAllowed();
    771        if (perUserEvictionPolicyClassName == null) {
    772            perUserEvictionPolicyClassName = new HashMap<>();
    773        } else {
    774            perUserEvictionPolicyClassName.clear();
    775        }
    776        perUserEvictionPolicyClassName.putAll(userDefaultEvictionPolicyClassName);
    777    }
    778
    779    /**
    780     * Sets a user specific value for {@link GenericObjectPool#getEvictionPolicyClassName()} for the specified user's
    781     * pool.
    782     *
    783     * @param userName
    784     *            The user name key.
    785     * @param value
    786     *            The user specific value.
    787     */
    788    public void setPerUserEvictionPolicyClassName(final String userName, final String value) {
    789        assertInitializationAllowed();
    790        if (perUserEvictionPolicyClassName == null) {
    791            perUserEvictionPolicyClassName = new HashMap<>();
    792        }
    793        perUserEvictionPolicyClassName.put(userName, value);
    794    }
    795
    796    void setPerUserLifo(final Map<String, Boolean> userDefaultLifo) {
    797        assertInitializationAllowed();
    798        if (perUserLifo == null) {
    799            perUserLifo = createMap();
    800        } else {
    801            perUserLifo.clear();
    802        }
    803        perUserLifo.putAll(userDefaultLifo);
    804    }
    805
    806    /**
    807     * Sets a user specific value for {@link GenericObjectPool#getLifo()} for the specified user's pool.
    808     *
    809     * @param userName
    810     *            The user name key.
    811     * @param value
    812     *            The user specific value.
    813     */
    814    public void setPerUserLifo(final String userName, final Boolean value) {
    815        assertInitializationAllowed();
    816        if (perUserLifo == null) {
    817            perUserLifo = createMap();
    818        }
    819        perUserLifo.put(userName, value);
    820    }
    821
    822    void setPerUserMaxIdle(final Map<String, Integer> userDefaultMaxIdle) {
    823        assertInitializationAllowed();
    824        if (perUserMaxIdle == null) {
    825            perUserMaxIdle = new HashMap<>();
    826        } else {
    827            perUserMaxIdle.clear();
    828        }
    829        perUserMaxIdle.putAll(userDefaultMaxIdle);
    830    }
    831
    832    /**
    833     * Sets a user specific value for {@link GenericObjectPool#getMaxIdle()} for the specified user's pool.
    834     *
    835     * @param userName
    836     *            The user name key.
    837     * @param value
    838     *            The user specific value.
    839     */
    840    public void setPerUserMaxIdle(final String userName, final Integer value) {
    841        assertInitializationAllowed();
    842        if (perUserMaxIdle == null) {
    843            perUserMaxIdle = new HashMap<>();
    844        }
    845        perUserMaxIdle.put(userName, value);
    846    }
    847
    848    void setPerUserMaxTotal(final Map<String, Integer> userDefaultMaxTotal) {
    849        assertInitializationAllowed();
    850        if (perUserMaxTotal == null) {
    851            perUserMaxTotal = new HashMap<>();
    852        } else {
    853            perUserMaxTotal.clear();
    854        }
    855        perUserMaxTotal.putAll(userDefaultMaxTotal);
    856    }
    857
    858    /**
    859     * Sets a user specific value for {@link GenericObjectPool#getMaxTotal()} for the specified user's pool.
    860     *
    861     * @param userName
    862     *            The user name key.
    863     * @param value
    864     *            The user specific value.
    865     */
    866    public void setPerUserMaxTotal(final String userName, final Integer value) {
    867        assertInitializationAllowed();
    868        if (perUserMaxTotal == null) {
    869            perUserMaxTotal = new HashMap<>();
    870        }
    871        perUserMaxTotal.put(userName, value);
    872    }
    873
    874    void setPerUserMaxWaitMillis(final Map<String, Long> userDefaultMaxWaitMillis) {
    875        assertInitializationAllowed();
    876        if (perUserMaxWaitMillis == null) {
    877            perUserMaxWaitMillis = new HashMap<>();
    878        } else {
    879            perUserMaxWaitMillis.clear();
    880        }
    881        perUserMaxWaitMillis.putAll(userDefaultMaxWaitMillis);
    882    }
    883
    884    /**
    885     * Sets a user specific value for {@link GenericObjectPool#getMaxWaitMillis()} for the specified user's pool.
    886     *
    887     * @param userName
    888     *            The user name key.
    889     * @param value
    890     *            The user specific value.
    891     */
    892    public void setPerUserMaxWaitMillis(final String userName, final Long value) {
    893        assertInitializationAllowed();
    894        if (perUserMaxWaitMillis == null) {
    895            perUserMaxWaitMillis = new HashMap<>();
    896        }
    897        perUserMaxWaitMillis.put(userName, value);
    898    }
    899
    900    void setPerUserMinEvictableIdleTimeMillis(final Map<String, Long> userDefaultMinEvictableIdleTimeMillis) {
    901        assertInitializationAllowed();
    902        if (perUserMinEvictableIdleTimeMillis == null) {
    903            perUserMinEvictableIdleTimeMillis = new HashMap<>();
    904        } else {
    905            perUserMinEvictableIdleTimeMillis.clear();
    906        }
    907        perUserMinEvictableIdleTimeMillis.putAll(userDefaultMinEvictableIdleTimeMillis);
    908    }
    909
    910    /**
    911     * Sets a user specific value for {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the specified user's
    912     * pool.
    913     *
    914     * @param userName
    915     *            The user name key.
    916     * @param value
    917     *            The user specific value.
    918     */
    919    public void setPerUserMinEvictableIdleTimeMillis(final String userName, final Long value) {
    920        assertInitializationAllowed();
    921        if (perUserMinEvictableIdleTimeMillis == null) {
    922            perUserMinEvictableIdleTimeMillis = new HashMap<>();
    923        }
    924        perUserMinEvictableIdleTimeMillis.put(userName, value);
    925    }
    926
    927    void setPerUserMinIdle(final Map<String, Integer> userDefaultMinIdle) {
    928        assertInitializationAllowed();
    929        if (perUserMinIdle == null) {
    930            perUserMinIdle = new HashMap<>();
    931        } else {
    932            perUserMinIdle.clear();
    933        }
    934        perUserMinIdle.putAll(userDefaultMinIdle);
    935    }
    936
    937    /**
    938     * Sets a user specific value for {@link GenericObjectPool#getMinIdle()} for the specified user's pool.
    939     *
    940     * @param userName
    941     *            The user name key.
    942     * @param value
    943     *            The user specific value.
    944     */
    945    public void setPerUserMinIdle(final String userName, final Integer value) {
    946        assertInitializationAllowed();
    947        if (perUserMinIdle == null) {
    948            perUserMinIdle = new HashMap<>();
    949        }
    950        perUserMinIdle.put(userName, value);
    951    }
    952
    953    void setPerUserNumTestsPerEvictionRun(final Map<String, Integer> userDefaultNumTestsPerEvictionRun) {
    954        assertInitializationAllowed();
    955        if (perUserNumTestsPerEvictionRun == null) {
    956            perUserNumTestsPerEvictionRun = new HashMap<>();
    957        } else {
    958            perUserNumTestsPerEvictionRun.clear();
    959        }
    960        perUserNumTestsPerEvictionRun.putAll(userDefaultNumTestsPerEvictionRun);
    961    }
    962
    963    /**
    964     * Sets a user specific value for {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the specified user's
    965     * pool.
    966     *
    967     * @param userName
    968     *            The user name key.
    969     * @param value
    970     *            The user specific value.
    971     */
    972    public void setPerUserNumTestsPerEvictionRun(final String userName, final Integer value) {
    973        assertInitializationAllowed();
    974        if (perUserNumTestsPerEvictionRun == null) {
    975            perUserNumTestsPerEvictionRun = new HashMap<>();
    976        }
    977        perUserNumTestsPerEvictionRun.put(userName, value);
    978    }
    979
    980    void setPerUserSoftMinEvictableIdleTimeMillis(final Map<String, Long> userDefaultSoftMinEvictableIdleTimeMillis) {
    981        assertInitializationAllowed();
    982        if (perUserSoftMinEvictableIdleTimeMillis == null) {
    983            perUserSoftMinEvictableIdleTimeMillis = new HashMap<>();
    984        } else {
    985            perUserSoftMinEvictableIdleTimeMillis.clear();
    986        }
    987        perUserSoftMinEvictableIdleTimeMillis.putAll(userDefaultSoftMinEvictableIdleTimeMillis);
    988    }
    989
    990    /**
    991     * Sets a user specific value for {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the specified
    992     * user's pool.
    993     *
    994     * @param userName
    995     *            The user name key.
    996     * @param value
    997     *            The user specific value.
    998     */
    999    public void setPerUserSoftMinEvictableIdleTimeMillis(final String userName, final Long value) {
    1000        assertInitializationAllowed();
    1001        if (perUserSoftMinEvictableIdleTimeMillis == null) {
    1002            perUserSoftMinEvictableIdleTimeMillis = new HashMap<>();
    1003        }
    1004        perUserSoftMinEvictableIdleTimeMillis.put(userName, value);
    1005    }
    1006
    1007    void setPerUserTestOnBorrow(final Map<String, Boolean> userDefaultTestOnBorrow) {
    1008        assertInitializationAllowed();
    1009        if (perUserTestOnBorrow == null) {
    1010            perUserTestOnBorrow = createMap();
    1011        } else {
    1012            perUserTestOnBorrow.clear();
    1013        }
    1014        perUserTestOnBorrow.putAll(userDefaultTestOnBorrow);
    1015    }
    1016
    1017    /**
    1018     * Sets a user specific value for {@link GenericObjectPool#getTestOnBorrow()} for the specified user's pool.
    1019     *
    1020     * @param userName
    1021     *            The user name key.
    1022     * @param value
    1023     *            The user specific value.
    1024     */
    1025    public void setPerUserTestOnBorrow(final String userName, final Boolean value) {
    1026        assertInitializationAllowed();
    1027        if (perUserTestOnBorrow == null) {
    1028            perUserTestOnBorrow = createMap();
    1029        }
    1030        perUserTestOnBorrow.put(userName, value);
    1031    }
    1032
    1033    void setPerUserTestOnCreate(final Map<String, Boolean> userDefaultTestOnCreate) {
    1034        assertInitializationAllowed();
    1035        if (perUserTestOnCreate == null) {
    1036            perUserTestOnCreate = createMap();
    1037        } else {
    1038            perUserTestOnCreate.clear();
    1039        }
    1040        perUserTestOnCreate.putAll(userDefaultTestOnCreate);
    1041    }
    1042
    1043    /**
    1044     * Sets a user specific value for {@link GenericObjectPool#getTestOnCreate()} for the specified user's pool.
    1045     *
    1046     * @param userName
    1047     *            The user name key.
    1048     * @param value
    1049     *            The user specific value.
    1050     */
    1051    public void setPerUserTestOnCreate(final String userName, final Boolean value) {
    1052        assertInitializationAllowed();
    1053        if (perUserTestOnCreate == null) {
    1054            perUserTestOnCreate = createMap();
    1055        }
    1056        perUserTestOnCreate.put(userName, value);
    1057    }
    1058
    1059    void setPerUserTestOnReturn(final Map<String, Boolean> userDefaultTestOnReturn) {
    1060        assertInitializationAllowed();
    1061        if (perUserTestOnReturn == null) {
    1062            perUserTestOnReturn = createMap();
    1063        } else {
    1064            perUserTestOnReturn.clear();
    1065        }
    1066        perUserTestOnReturn.putAll(userDefaultTestOnReturn);
    1067    }
    1068
    1069    /**
    1070     * Sets a user specific value for {@link GenericObjectPool#getTestOnReturn()} for the specified user's pool.
    1071     *
    1072     * @param userName
    1073     *            The user name key.
    1074     * @param value
    1075     *            The user specific value.
    1076     */
    1077    public void setPerUserTestOnReturn(final String userName, final Boolean value) {
    1078        assertInitializationAllowed();
    1079        if (perUserTestOnReturn == null) {
    1080            perUserTestOnReturn = createMap();
    1081        }
    1082        perUserTestOnReturn.put(userName, value);
    1083    }
    1084
    1085    void setPerUserTestWhileIdle(final Map<String, Boolean> userDefaultTestWhileIdle) {
    1086        assertInitializationAllowed();
    1087        if (perUserTestWhileIdle == null) {
    1088            perUserTestWhileIdle = createMap();
    1089        } else {
    1090            perUserTestWhileIdle.clear();
    1091        }
    1092        perUserTestWhileIdle.putAll(userDefaultTestWhileIdle);
    1093    }
    1094
    1095    /**
    1096     * Sets a user specific value for {@link GenericObjectPool#getTestWhileIdle()} for the specified user's pool.
    1097     *
    1098     * @param userName
    1099     *            The user name key.
    1100     * @param value
    1101     *            The user specific value.
    1102     */
    1103    public void setPerUserTestWhileIdle(final String userName, final Boolean value) {
    1104        assertInitializationAllowed();
    1105        if (perUserTestWhileIdle == null) {
    1106            perUserTestWhileIdle = createMap();
    1107        }
    1108        perUserTestWhileIdle.put(userName, value);
    1109    }
    1110
    1111    void setPerUserTimeBetweenEvictionRunsMillis(final Map<String, Long> userDefaultTimeBetweenEvictionRunsMillis) {
    1112        assertInitializationAllowed();
    1113        if (perUserTimeBetweenEvictionRunsMillis == null) {
    1114            perUserTimeBetweenEvictionRunsMillis = new HashMap<>();
    1115        } else {
    1116            perUserTimeBetweenEvictionRunsMillis.clear();
    1117        }
    1118        perUserTimeBetweenEvictionRunsMillis.putAll(userDefaultTimeBetweenEvictionRunsMillis);
    1119    }
    1120
    1121    /**
    1122     * Sets a user specific value for {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for the specified
    1123     * user's pool.
    1124     *
    1125     * @param userName
    1126     *            The user name key.
    1127     * @param value
    1128     *            The user specific value.
    1129     */
    1130    public void setPerUserTimeBetweenEvictionRunsMillis(final String userName, final Long value) {
    1131        assertInitializationAllowed();
    1132        if (perUserTimeBetweenEvictionRunsMillis == null) {
    1133            perUserTimeBetweenEvictionRunsMillis = new HashMap<>();
    1134        }
    1135        perUserTimeBetweenEvictionRunsMillis.put(userName, value);
    1136    }
    1137
    1138    @Override
    1139    protected void setupDefaults(final Connection con, final String userName) throws SQLException {
    1140        Boolean defaultAutoCommit = isDefaultAutoCommit();
    1141        if (userName != null) {
    1142            final Boolean userMax = getPerUserDefaultAutoCommit(userName);
    1143            if (userMax != null) {
    1144                defaultAutoCommit = userMax;
    1145            }
    1146        }
    1147
    1148        Boolean defaultReadOnly = isDefaultReadOnly();
    1149        if (userName != null) {
    1150            final Boolean userMax = getPerUserDefaultReadOnly(userName);
    1151            if (userMax != null) {
    1152                defaultReadOnly = userMax;
    1153            }
    1154        }
    1155
    1156        int defaultTransactionIsolation = getDefaultTransactionIsolation();
    1157        if (userName != null) {
    1158            final Integer userMax = getPerUserDefaultTransactionIsolation(userName);
    1159            if (userMax != null) {
    1160                defaultTransactionIsolation = userMax.intValue();
    1161            }
    1162        }
    1163
    1164        if (defaultAutoCommit != null && con.getAutoCommit() != defaultAutoCommit.booleanValue()) {
    1165            con.setAutoCommit(defaultAutoCommit.booleanValue());
    1166        }
    1167
    1168        if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
    1169            con.setTransactionIsolation(defaultTransactionIsolation);
    1170        }
    1171
    1172        if (defaultReadOnly != null && con.isReadOnly() != defaultReadOnly.booleanValue()) {
    1173            con.setReadOnly(defaultReadOnly.booleanValue());
    1174        }
    1175    }
    1176}