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