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