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