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}