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