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