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