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