    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}