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