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