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