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