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