001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.dbcp2.datasources;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.sql.Connection;
022import java.sql.SQLException;
023import java.util.HashMap;
024import java.util.Map;
025import java.util.NoSuchElementException;
026
027import javax.naming.NamingException;
028import javax.naming.Reference;
029import javax.naming.StringRefAddr;
030import javax.sql.ConnectionPoolDataSource;
031
032import org.apache.commons.dbcp2.SwallowedExceptionLogger;
033import org.apache.commons.dbcp2.Utils;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.apache.commons.pool2.ObjectPool;
037import org.apache.commons.pool2.impl.GenericObjectPool;
038
039/**
040 * <p>
041 * A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration
042 * options, most of which are defined in the parent class. This datasource uses individual pools per user, and some
043 * properties can be set specifically for a given user, if the deployment environment can support initialization of
044 * mapped properties. So for example, a pool of admin or write-access Connections can be guaranteed a certain number of
045 * connections, separate from a maximum set for users with read-only connections.
046 * </p>
047 *
048 * <p>
049 * User passwords can be changed without re-initializing the datasource. When a
050 * <code>getConnection(userName, password)</code> request is processed with a password that is different from those used
051 * to create connections in the pool associated with <code>userName</code>, an attempt is made to create a new
052 * connection using the supplied password and if this succeeds, the existing pool is cleared and a new pool is created
053 * for connections using the new password.
054 * </p>
055 *
056 * @since 2.0
057 */
058public class PerUserPoolDataSource extends InstanceKeyDataSource {
059
060    private static final long serialVersionUID = 7872747993848065028L;
061
062    private static final Log log = LogFactory.getLog(PerUserPoolDataSource.class);
063
064    // Per user pool properties
065    private Map<String, Boolean> perUserBlockWhenExhausted;
066    private Map<String, String> perUserEvictionPolicyClassName;
067    private Map<String, Boolean> perUserLifo;
068    private Map<String, Integer> perUserMaxIdle;
069    private Map<String, Integer> perUserMaxTotal;
070    private Map<String, Long> perUserMaxWaitMillis;
071    private Map<String, Long> perUserMinEvictableIdleTimeMillis;
072    private Map<String, Integer> perUserMinIdle;
073    private Map<String, Integer> perUserNumTestsPerEvictionRun;
074    private Map<String, Long> perUserSoftMinEvictableIdleTimeMillis;
075    private Map<String, Boolean> perUserTestOnCreate;
076    private Map<String, Boolean> perUserTestOnBorrow;
077    private Map<String, Boolean> perUserTestOnReturn;
078    private Map<String, Boolean> perUserTestWhileIdle;
079    private Map<String, Long> perUserTimeBetweenEvictionRunsMillis;
080
081    // Per user connection properties
082    private Map<String, Boolean> perUserDefaultAutoCommit;
083    private Map<String, Integer> perUserDefaultTransactionIsolation;
084    private Map<String, Boolean> perUserDefaultReadOnly;
085
086    /**
087     * Map to keep track of Pools for a given user.
088     */
089    private transient Map<PoolKey, PooledConnectionManager> managers = new HashMap<>();
090
091    /**
092     * Default no-arg constructor for Serialization.
093     */
094    public PerUserPoolDataSource() {
095    }
096
097    /**
098     * Clears pool(s) maintained by this data source.
099     *
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 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 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 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}