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