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