    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 */
    017
    018package org.apache.commons.dbcp2;
    019
    020import java.io.ByteArrayInputStream;
    021import java.nio.charset.StandardCharsets;
    022import java.sql.Connection;
    023import java.util.ArrayList;
    024import java.util.Arrays;
    025import java.util.Collection;
    026import java.util.Enumeration;
    027import java.util.Hashtable;
    028import java.util.LinkedHashMap;
    029import java.util.List;
    030import java.util.Map;
    031import java.util.Properties;
    032import java.util.StringTokenizer;
    033
    034import javax.naming.Context;
    035import javax.naming.Name;
    036import javax.naming.RefAddr;
    037import javax.naming.Reference;
    038import javax.naming.spi.ObjectFactory;
    039
    040import org.apache.commons.logging.Log;
    041import org.apache.commons.logging.LogFactory;
    042import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
    043import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    044
    045/**
    046 * <p>
    047 * JNDI object factory that creates an instance of <code>BasicDataSource</code> that has been configured based on the
    048 * <code>RefAddr</code> values of the specified <code>Reference</code>, which must match the names and data types of the
    049 * <code>BasicDataSource</code> bean properties with the following exceptions:
    050 * </p>
    051 * <ul>
    052 * <li><code>connectionInitSqls</code> must be passed to this factory as a single String using semi-colon to delimit the
    053 * statements whereas <code>BasicDataSource</code> requires a collection of Strings.</li>
    054 * </ul>
    055 *
    056 * @since 2.0
    057 */
    058public class BasicDataSourceFactory implements ObjectFactory {
    059
    060    private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
    061
    062    private static final String PROP_DEFAULT_AUTO_COMMIT = "defaultAutoCommit";
    063    private static final String PROP_DEFAULT_READ_ONLY = "defaultReadOnly";
    064    private static final String PROP_DEFAULT_TRANSACTION_ISOLATION = "defaultTransactionIsolation";
    065    private static final String PROP_DEFAULT_CATALOG = "defaultCatalog";
    066    private static final String PROP_DEFAULT_SCHEMA = "defaultSchema";
    067    private static final String PROP_CACHE_STATE = "cacheState";
    068    private static final String PROP_DRIVER_CLASS_NAME = "driverClassName";
    069    private static final String PROP_LIFO = "lifo";
    070    private static final String PROP_MAX_TOTAL = "maxTotal";
    071    private static final String PROP_MAX_IDLE = "maxIdle";
    072    private static final String PROP_MIN_IDLE = "minIdle";
    073    private static final String PROP_INITIAL_SIZE = "initialSize";
    074    private static final String PROP_MAX_WAIT_MILLIS = "maxWaitMillis";
    075    private static final String PROP_TEST_ON_CREATE = "testOnCreate";
    076    private static final String PROP_TEST_ON_BORROW = "testOnBorrow";
    077    private static final String PROP_TEST_ON_RETURN = "testOnReturn";
    078    private static final String PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis";
    079    private static final String PROP_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
    080    private static final String PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis";
    081    private static final String PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "softMinEvictableIdleTimeMillis";
    082    private static final String PROP_EVICTION_POLICY_CLASS_NAME = "evictionPolicyClassName";
    083    private static final String PROP_TEST_WHILE_IDLE = "testWhileIdle";
    084    private static final String PROP_PASSWORD = "password";
    085    private static final String PROP_URL = "url";
    086    private static final String PROP_USER_NAME = "username";
    087    private static final String PROP_VALIDATION_QUERY = "validationQuery";
    088    private static final String PROP_VALIDATION_QUERY_TIMEOUT = "validationQueryTimeout";
    089    private static final String PROP_JMX_NAME = "jmxName";
    090
    091    /**
    092     * The property name for connectionInitSqls. The associated value String must be of the form [query;]*
    093     */
    094    private static final String PROP_CONNECTION_INIT_SQLS = "connectionInitSqls";
    095    private static final String PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED = "accessToUnderlyingConnectionAllowed";
    096    private static final String PROP_REMOVE_ABANDONED_ON_BORROW = "removeAbandonedOnBorrow";
    097    private static final String PROP_REMOVE_ABANDONED_ON_MAINTENANCE = "removeAbandonedOnMaintenance";
    098    private static final String PROP_REMOVE_ABANDONED_TIMEOUT = "removeAbandonedTimeout";
    099    private static final String PROP_LOG_ABANDONED = "logAbandoned";
    100    private static final String PROP_ABANDONED_USAGE_TRACKING = "abandonedUsageTracking";
    101    private static final String PROP_POOL_PREPARED_STATEMENTS = "poolPreparedStatements";
    102    private static final String PROP_MAX_OPEN_PREPARED_STATEMENTS = "maxOpenPreparedStatements";
    103    private static final String PROP_CONNECTION_PROPERTIES = "connectionProperties";
    104    private static final String PROP_MAX_CONN_LIFETIME_MILLIS = "maxConnLifetimeMillis";
    105    private static final String PROP_LOG_EXPIRED_CONNECTIONS = "logExpiredConnections";
    106    private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
    107    private static final String PROP_ENABLE_AUTO_COMMIT_ON_RETURN = "enableAutoCommitOnReturn";
    108    private static final String PROP_DEFAULT_QUERY_TIMEOUT = "defaultQueryTimeout";
    109    private static final String PROP_FAST_FAIL_VALIDATION = "fastFailValidation";
    110
    111    /**
    112     * Value string must be of the form [STATE_CODE,]*
    113     */
    114    private static final String PROP_DISCONNECTION_SQL_CODES = "disconnectionSqlCodes";
    115
    116    /*
    117     * Block with obsolete properties from DBCP 1.x. Warn users that these are ignored and they should use the 2.x
    118     * properties.
    119     */
    120    private static final String NUPROP_MAX_ACTIVE = "maxActive";
    121    private static final String NUPROP_REMOVE_ABANDONED = "removeAbandoned";
    122    private static final String NUPROP_MAXWAIT = "maxWait";
    123
    124    /*
    125     * Block with properties expected in a DataSource This props will not be listed as ignored - we know that they may
    126     * appear in Resource, and not listing them as ignored.
    127     */
    128    private static final String SILENT_PROP_FACTORY = "factory";
    129    private static final String SILENT_PROP_SCOPE = "scope";
    130    private static final String SILENT_PROP_SINGLETON = "singleton";
    131    private static final String SILENT_PROP_AUTH = "auth";
    132
    133    private static final String[] ALL_PROPERTIES = {PROP_DEFAULT_AUTO_COMMIT, PROP_DEFAULT_READ_ONLY,
    134            PROP_DEFAULT_TRANSACTION_ISOLATION, PROP_DEFAULT_CATALOG, PROP_DEFAULT_SCHEMA, PROP_CACHE_STATE,
    135            PROP_DRIVER_CLASS_NAME, PROP_LIFO, PROP_MAX_TOTAL, PROP_MAX_IDLE, PROP_MIN_IDLE, PROP_INITIAL_SIZE,
    136            PROP_MAX_WAIT_MILLIS, PROP_TEST_ON_CREATE, PROP_TEST_ON_BORROW, PROP_TEST_ON_RETURN,
    137            PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS, PROP_NUM_TESTS_PER_EVICTION_RUN, PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS,
    138            PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, PROP_EVICTION_POLICY_CLASS_NAME, PROP_TEST_WHILE_IDLE, PROP_PASSWORD,
    139            PROP_URL, PROP_USER_NAME, PROP_VALIDATION_QUERY, PROP_VALIDATION_QUERY_TIMEOUT, PROP_CONNECTION_INIT_SQLS,
    140            PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED, PROP_REMOVE_ABANDONED_ON_BORROW, PROP_REMOVE_ABANDONED_ON_MAINTENANCE,
    141            PROP_REMOVE_ABANDONED_TIMEOUT, PROP_LOG_ABANDONED, PROP_ABANDONED_USAGE_TRACKING, PROP_POOL_PREPARED_STATEMENTS,
    142            PROP_MAX_OPEN_PREPARED_STATEMENTS, PROP_CONNECTION_PROPERTIES, PROP_MAX_CONN_LIFETIME_MILLIS,
    143            PROP_LOG_EXPIRED_CONNECTIONS, PROP_ROLLBACK_ON_RETURN, PROP_ENABLE_AUTO_COMMIT_ON_RETURN,
    144            PROP_DEFAULT_QUERY_TIMEOUT, PROP_FAST_FAIL_VALIDATION, PROP_DISCONNECTION_SQL_CODES, PROP_JMX_NAME };
    145
    146    /**
    147     * Obsolete properties from DBCP 1.x. with warning strings suggesting new properties. LinkedHashMap will guarantee
    148     * that properties will be listed to output in order of insertion into map.
    149     */
    150    private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
    151
    152    static {
    153        NUPROP_WARNTEXT.put(NUPROP_MAX_ACTIVE,
    154                "Property " + NUPROP_MAX_ACTIVE + " is not used in DBCP2, use " + PROP_MAX_TOTAL + " instead. "
    155                        + PROP_MAX_TOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + ".");
    156        NUPROP_WARNTEXT.put(NUPROP_REMOVE_ABANDONED,
    157                "Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2," + " use one or both of "
    158                        + PROP_REMOVE_ABANDONED_ON_BORROW + " or " + PROP_REMOVE_ABANDONED_ON_MAINTENANCE + " instead. "
    159                        + "Both have default value set to false.");
    160        NUPROP_WARNTEXT.put(NUPROP_MAXWAIT,
    161                "Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAX_WAIT_MILLIS + " instead. "
    162                        + PROP_MAX_WAIT_MILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS
    163                        + ".");
    164    }
    165
    166    /**
    167     * Silent Properties. These properties will not be listed as ignored - we know that they may appear in JDBC Resource
    168     * references, and we will not list them as ignored.
    169     */
    170    private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
    171
    172    static {
    173        SILENT_PROPERTIES.add(SILENT_PROP_FACTORY);
    174        SILENT_PROPERTIES.add(SILENT_PROP_SCOPE);
    175        SILENT_PROPERTIES.add(SILENT_PROP_SINGLETON);
    176        SILENT_PROPERTIES.add(SILENT_PROP_AUTH);
    177
    178    }
    179
    180    // -------------------------------------------------- ObjectFactory Methods
    181
    182    /**
    183     * <p>
    184     * Create and return a new <code>BasicDataSource</code> instance. If no instance can be created, return
    185     * <code>null</code> instead.
    186     * </p>
    187     *
    188     * @param obj
    189     *            The possibly null object containing location or reference information that can be used in creating an
    190     *            object
    191     * @param name
    192     *            The name of this object relative to <code>nameCtx</code>
    193     * @param nameCtx
    194     *            The context relative to which the <code>name</code> parameter is specified, or <code>null</code> if
    195     *            <code>name</code> is relative to the default initial context
    196     * @param environment
    197     *            The possibly null environment that is used in creating this object
    198     *
    199     * @throws Exception
    200     *             if an exception occurs creating the instance
    201     */
    202    @Override
    203    public Object getObjectInstance(final Object obj, final Name name, final Context nameCtx,
    204            final Hashtable<?, ?> environment) throws Exception {
    205
    206        // We only know how to deal with <code>javax.naming.Reference</code>s
    207        // that specify a class name of "javax.sql.DataSource"
    208        if (obj == null || !(obj instanceof Reference)) {
    209            return null;
    210        }
    211        final Reference ref = (Reference) obj;
    212        if (!"javax.sql.DataSource".equals(ref.getClassName())) {
    213            return null;
    214        }
    215
    216        // Check property names and log warnings about obsolete and / or unknown properties
    217        final List<String> warnings = new ArrayList<>();
    218        final List<String> infoMessages = new ArrayList<>();
    219        validatePropertyNames(ref, name, warnings, infoMessages);
    220        for (final String warning : warnings) {
    221            log.warn(warning);
    222        }
    223        for (final String infoMessage : infoMessages) {
    224            log.info(infoMessage);
    225        }
    226
    227        final Properties properties = new Properties();
    228        for (final String propertyName : ALL_PROPERTIES) {
    229            final RefAddr ra = ref.get(propertyName);
    230            if (ra != null) {
    231                final String propertyValue = ra.getContent().toString();
    232                properties.setProperty(propertyName, propertyValue);
    233            }
    234        }
    235
    236        return createDataSource(properties);
    237    }
    238
    239    /**
    240     * Collects warnings and info messages. Warnings are generated when an obsolete property is set. Unknown properties
    241     * generate info messages.
    242     *
    243     * @param ref
    244     *            Reference to check properties of
    245     * @param name
    246     *            Name provided to getObject
    247     * @param warnings
    248     *            container for warning messages
    249     * @param infoMessages
    250     *            container for info messages
    251     */
    252    private void validatePropertyNames(final Reference ref, final Name name, final List<String> warnings,
    253            final List<String> infoMessages) {
    254        final List<String> allPropsAsList = Arrays.asList(ALL_PROPERTIES);
    255        final String nameString = name != null ? "Name = " + name.toString() + " " : "";
    256        if (NUPROP_WARNTEXT != null && !NUPROP_WARNTEXT.keySet().isEmpty()) {
    257            for (final String propertyName : NUPROP_WARNTEXT.keySet()) {
    258                final RefAddr ra = ref.get(propertyName);
    259                if (ra != null && !allPropsAsList.contains(ra.getType())) {
    260                    final StringBuilder stringBuilder = new StringBuilder(nameString);
    261                    final String propertyValue = ra.getContent().toString();
    262                    stringBuilder.append(NUPROP_WARNTEXT.get(propertyName)).append(" You have set value of \"")
    263                            .append(propertyValue).append("\" for \"").append(propertyName)
    264                            .append("\" property, which is being ignored.");
    265                    warnings.add(stringBuilder.toString());
    266                }
    267            }
    268        }
    269
    270        final Enumeration<RefAddr> allRefAddrs = ref.getAll();
    271        while (allRefAddrs.hasMoreElements()) {
    272            final RefAddr ra = allRefAddrs.nextElement();
    273            final String propertyName = ra.getType();
    274            // If property name is not in the properties list, we haven't warned on it
    275            // and it is not in the "silent" list, tell user we are ignoring it.
    276            if (!(allPropsAsList.contains(propertyName) || NUPROP_WARNTEXT.keySet().contains(propertyName)
    277                    || SILENT_PROPERTIES.contains(propertyName))) {
    278                final String propertyValue = ra.getContent().toString();
    279                final StringBuilder stringBuilder = new StringBuilder(nameString);
    280                stringBuilder.append("Ignoring unknown property: ").append("value of \"").append(propertyValue)
    281                        .append("\" for \"").append(propertyName).append("\" property");
    282                infoMessages.add(stringBuilder.toString());
    283            }
    284        }
    285    }
    286
    287    /**
    288     * Creates and configures a {@link BasicDataSource} instance based on the given properties.
    289     *
    290     * @param properties
    291     *            The data source configuration properties.
    292     * @return A new a {@link BasicDataSource} instance based on the given properties.
    293     * @throws Exception
    294     *             Thrown when an error occurs creating the data source.
    295     */
    296    public static BasicDataSource createDataSource(final Properties properties) throws Exception {
    297        final BasicDataSource dataSource = new BasicDataSource();
    298        String value = null;
    299
    300        value = properties.getProperty(PROP_DEFAULT_AUTO_COMMIT);
    301        if (value != null) {
    302            dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
    303        }
    304
    305        value = properties.getProperty(PROP_DEFAULT_READ_ONLY);
    306        if (value != null) {
    307            dataSource.setDefaultReadOnly(Boolean.valueOf(value));
    308        }
    309
    310        value = properties.getProperty(PROP_DEFAULT_TRANSACTION_ISOLATION);
    311        if (value != null) {
    312            int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
    313            if ("NONE".equalsIgnoreCase(value)) {
    314                level = Connection.TRANSACTION_NONE;
    315            } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
    316                level = Connection.TRANSACTION_READ_COMMITTED;
    317            } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
    318                level = Connection.TRANSACTION_READ_UNCOMMITTED;
    319            } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
    320                level = Connection.TRANSACTION_REPEATABLE_READ;
    321            } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
    322                level = Connection.TRANSACTION_SERIALIZABLE;
    323            } else {
    324                try {
    325                    level = Integer.parseInt(value);
    326                } catch (final NumberFormatException e) {
    327                    System.err.println("Could not parse defaultTransactionIsolation: " + value);
    328                    System.err.println("WARNING: defaultTransactionIsolation not set");
    329                    System.err.println("using default value of database driver");
    330                    level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
    331                }
    332            }
    333            dataSource.setDefaultTransactionIsolation(level);
    334        }
    335
    336        value = properties.getProperty(PROP_DEFAULT_CATALOG);
    337        if (value != null) {
    338            dataSource.setDefaultCatalog(value);
    339        }
    340
    341        value = properties.getProperty(PROP_DEFAULT_SCHEMA);
    342        if (value != null) {
    343            dataSource.setDefaultSchema(value);
    344        }
    345
    346        value = properties.getProperty(PROP_CACHE_STATE);
    347        if (value != null) {
    348            dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
    349        }
    350
    351        value = properties.getProperty(PROP_DRIVER_CLASS_NAME);
    352        if (value != null) {
    353            dataSource.setDriverClassName(value);
    354        }
    355
    356        value = properties.getProperty(PROP_LIFO);
    357        if (value != null) {
    358            dataSource.setLifo(Boolean.valueOf(value).booleanValue());
    359        }
    360
    361        value = properties.getProperty(PROP_MAX_TOTAL);
    362        if (value != null) {
    363            dataSource.setMaxTotal(Integer.parseInt(value));
    364        }
    365
    366        value = properties.getProperty(PROP_MAX_IDLE);
    367        if (value != null) {
    368            dataSource.setMaxIdle(Integer.parseInt(value));
    369        }
    370
    371        value = properties.getProperty(PROP_MIN_IDLE);
    372        if (value != null) {
    373            dataSource.setMinIdle(Integer.parseInt(value));
    374        }
    375
    376        value = properties.getProperty(PROP_INITIAL_SIZE);
    377        if (value != null) {
    378            dataSource.setInitialSize(Integer.parseInt(value));
    379        }
    380
    381        value = properties.getProperty(PROP_MAX_WAIT_MILLIS);
    382        if (value != null) {
    383            dataSource.setMaxWaitMillis(Long.parseLong(value));
    384        }
    385
    386        value = properties.getProperty(PROP_TEST_ON_CREATE);
    387        if (value != null) {
    388            dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
    389        }
    390
    391        value = properties.getProperty(PROP_TEST_ON_BORROW);
    392        if (value != null) {
    393            dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
    394        }
    395
    396        value = properties.getProperty(PROP_TEST_ON_RETURN);
    397        if (value != null) {
    398            dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
    399        }
    400
    401        value = properties.getProperty(PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
    402        if (value != null) {
    403            dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
    404        }
    405
    406        value = properties.getProperty(PROP_NUM_TESTS_PER_EVICTION_RUN);
    407        if (value != null) {
    408            dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
    409        }
    410
    411        value = properties.getProperty(PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    412        if (value != null) {
    413            dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
    414        }
    415
    416        value = properties.getProperty(PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    417        if (value != null) {
    418            dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
    419        }
    420
    421        value = properties.getProperty(PROP_EVICTION_POLICY_CLASS_NAME);
    422        if (value != null) {
    423            dataSource.setEvictionPolicyClassName(value);
    424        }
    425
    426        value = properties.getProperty(PROP_TEST_WHILE_IDLE);
    427        if (value != null) {
    428            dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
    429        }
    430
    431        value = properties.getProperty(PROP_PASSWORD);
    432        if (value != null) {
    433            dataSource.setPassword(value);
    434        }
    435
    436        value = properties.getProperty(PROP_URL);
    437        if (value != null) {
    438            dataSource.setUrl(value);
    439        }
    440
    441        value = properties.getProperty(PROP_USER_NAME);
    442        if (value != null) {
    443            dataSource.setUsername(value);
    444        }
    445
    446        value = properties.getProperty(PROP_VALIDATION_QUERY);
    447        if (value != null) {
    448            dataSource.setValidationQuery(value);
    449        }
    450
    451        value = properties.getProperty(PROP_VALIDATION_QUERY_TIMEOUT);
    452        if (value != null) {
    453            dataSource.setValidationQueryTimeout(Integer.parseInt(value));
    454        }
    455
    456        value = properties.getProperty(PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
    457        if (value != null) {
    458            dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
    459        }
    460
    461        value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_BORROW);
    462        if (value != null) {
    463            dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
    464        }
    465
    466        value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_MAINTENANCE);
    467        if (value != null) {
    468            dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
    469        }
    470
    471        value = properties.getProperty(PROP_REMOVE_ABANDONED_TIMEOUT);
    472        if (value != null) {
    473            dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
    474        }
    475
    476        value = properties.getProperty(PROP_LOG_ABANDONED);
    477        if (value != null) {
    478            dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
    479        }
    480
    481        value = properties.getProperty(PROP_ABANDONED_USAGE_TRACKING);
    482        if (value != null) {
    483            dataSource.setAbandonedUsageTracking(Boolean.valueOf(value).booleanValue());
    484        }
    485
    486        value = properties.getProperty(PROP_POOL_PREPARED_STATEMENTS);
    487        if (value != null) {
    488            dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
    489        }
    490
    491        value = properties.getProperty(PROP_MAX_OPEN_PREPARED_STATEMENTS);
    492        if (value != null) {
    493            dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
    494        }
    495
    496        value = properties.getProperty(PROP_CONNECTION_INIT_SQLS);
    497        if (value != null) {
    498            dataSource.setConnectionInitSqls(parseList(value, ';'));
    499        }
    500
    501        value = properties.getProperty(PROP_CONNECTION_PROPERTIES);
    502        if (value != null) {
    503            final Properties p = getProperties(value);
    504            final Enumeration<?> e = p.propertyNames();
    505            while (e.hasMoreElements()) {
    506                final String propertyName = (String) e.nextElement();
    507                dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
    508            }
    509        }
    510
    511        value = properties.getProperty(PROP_MAX_CONN_LIFETIME_MILLIS);
    512        if (value != null) {
    513            dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
    514        }
    515
    516        value = properties.getProperty(PROP_LOG_EXPIRED_CONNECTIONS);
    517        if (value != null) {
    518            dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
    519        }
    520
    521        value = properties.getProperty(PROP_JMX_NAME);
    522        if (value != null) {
    523            dataSource.setJmxName(value);
    524        }
    525
    526        value = properties.getProperty(PROP_ENABLE_AUTO_COMMIT_ON_RETURN);
    527        if (value != null) {
    528            dataSource.setAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
    529        }
    530
    531        value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
    532        if (value != null) {
    533            dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
    534        }
    535
    536        value = properties.getProperty(PROP_DEFAULT_QUERY_TIMEOUT);
    537        if (value != null) {
    538            dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
    539        }
    540
    541        value = properties.getProperty(PROP_FAST_FAIL_VALIDATION);
    542        if (value != null) {
    543            dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
    544        }
    545
    546        value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
    547        if (value != null) {
    548            dataSource.setDisconnectionSqlCodes(parseList(value, ','));
    549        }
    550
    551        // DBCP-215
    552        // Trick to make sure that initialSize connections are created
    553        if (dataSource.getInitialSize() > 0) {
    554            dataSource.getLogWriter();
    555        }
    556
    557        // Return the configured DataSource instance
    558        return dataSource;
    559    }
    560
    561    /**
    562     * <p>
    563     * Parse properties from the string. Format of the string must be [propertyName=property;]*
    564     * <p>
    565     *
    566     * @param propText
    567     * @return Properties
    568     * @throws Exception
    569     */
    570    private static Properties getProperties(final String propText) throws Exception {
    571        final Properties p = new Properties();
    572        if (propText != null) {
    573            p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes(StandardCharsets.ISO_8859_1)));
    574        }
    575        return p;
    576    }
    577
    578    /**
    579     * Parse list of property values from a delimited string
    580     *
    581     * @param value
    582     *            delimited list of values
    583     * @param delimiter
    584     *            character used to separate values in the list
    585     * @return String Collection of values
    586     */
    587    private static Collection<String> parseList(final String value, final char delimiter) {
    588        final StringTokenizer tokenizer = new StringTokenizer(value, Character.toString(delimiter));
    589        final Collection<String> tokens = new ArrayList<>(tokenizer.countTokens());
    590        while (tokenizer.hasMoreTokens()) {
    591            tokens.add(tokenizer.nextToken());
    592        }
    593        return tokens;
    594    }
    595}