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 org.apache.commons.logging.Log;
021import org.apache.commons.logging.LogFactory;
022import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
023import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
024
025import java.io.ByteArrayInputStream;
026import java.nio.charset.StandardCharsets;
027import java.sql.Connection;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Enumeration;
032import java.util.Hashtable;
033import java.util.LinkedHashMap;
034import java.util.List;
035import java.util.Map;
036import java.util.Properties;
037import java.util.StringTokenizer;
038
039import javax.naming.Context;
040import javax.naming.Name;
041import javax.naming.RefAddr;
042import javax.naming.Reference;
043import javax.naming.spi.ObjectFactory;
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 * @version $Id: BasicDataSourceFactory.java 1693385 2015-07-30 03:59:51Z psteitz $
060 * @since 2.0
061 */
062public class BasicDataSourceFactory implements ObjectFactory {
063
064    private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
065
066    private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
067    private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
068    private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
069    private static final String PROP_DEFAULTCATALOG = "defaultCatalog";
070    private static final String PROP_CACHESTATE ="cacheState";
071    private static final String PROP_DRIVERCLASSNAME = "driverClassName";
072    private static final String PROP_LIFO = "lifo";
073    private static final String PROP_MAXTOTAL = "maxTotal";
074    private static final String PROP_MAXIDLE = "maxIdle";
075    private static final String PROP_MINIDLE = "minIdle";
076    private static final String PROP_INITIALSIZE = "initialSize";
077    private static final String PROP_MAXWAITMILLIS = "maxWaitMillis";
078    private static final String PROP_TESTONCREATE = "testOnCreate";
079    private static final String PROP_TESTONBORROW = "testOnBorrow";
080    private static final String PROP_TESTONRETURN = "testOnReturn";
081    private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
082    private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
083    private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
084    private static final String PROP_SOFTMINEVICTABLEIDLETIMEMILLIS = "softMinEvictableIdleTimeMillis";
085    private static final String PROP_EVICTIONPOLICYCLASSNAME = "evictionPolicyClassName";
086    private static final String PROP_TESTWHILEIDLE = "testWhileIdle";
087    private static final String PROP_PASSWORD = "password";
088    private static final String PROP_URL = "url";
089    private static final String PROP_USERNAME = "username";
090    private static final String PROP_VALIDATIONQUERY = "validationQuery";
091    private static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
092    private static final String PROP_JMX_NAME = "jmxName";
093
094    /**
095     * The property name for connectionInitSqls.
096     * The associated value String must be of the form [query;]*
097     */
098    private static final String PROP_CONNECTIONINITSQLS = "connectionInitSqls";
099    private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
100    private static final String PROP_REMOVEABANDONEDONBORROW = "removeAbandonedOnBorrow";
101    private static final String PROP_REMOVEABANDONEDONMAINTENANCE = "removeAbandonedOnMaintenance";
102    private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
103    private static final String PROP_LOGABANDONED = "logAbandoned";
104    private static final String PROP_ABANDONEDUSAGETRACKING = "abandonedUsageTracking";
105    private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
106    private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
107    private static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
108    private static final String PROP_MAXCONNLIFETIMEMILLIS = "maxConnLifetimeMillis";
109    private static final String PROP_LOGEXPIREDCONNECTIONS = "logExpiredConnections";
110    private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
111    private static final String PROP_ENABLE_AUTOCOMMIT_ON_RETURN = "enableAutoCommitOnReturn";
112    private static final String PROP_DEFAULT_QUERYTIMEOUT = "defaultQueryTimeout";
113    private static final String PROP_FASTFAIL_VALIDATION = "fastFailValidation";
114
115    /**
116     * Value string must be of the form [STATE_CODE,]*
117     */
118    private static final String PROP_DISCONNECTION_SQL_CODES = "disconnectionSqlCodes";
119
120    /*
121     * Block with obsolete properties from DBCP 1.x.
122     * Warn users that these are ignored and they should use the 2.x properties.
123     */
124    private static final String NUPROP_MAXACTIVE = "maxActive";
125    private static final String NUPROP_REMOVEABANDONED = "removeAbandoned";
126    private static final String NUPROP_MAXWAIT = "maxWait";
127
128    /*
129     * Block with properties expected in a DataSource
130     * This props will not be listed as ignored - we know that they may appear in Resource,
131     * and not listing them as ignored.
132     */
133    private static final String SILENTPROP_FACTORY = "factory";
134    private static final String SILENTPROP_SCOPE = "scope";
135    private static final String SILENTPROP_SINGLETON = "singleton";
136    private static final String SILENTPROP_AUTH = "auth";
137
138    private static final String[] ALL_PROPERTIES = {
139        PROP_DEFAULTAUTOCOMMIT,
140        PROP_DEFAULTREADONLY,
141        PROP_DEFAULTTRANSACTIONISOLATION,
142        PROP_DEFAULTCATALOG,
143        PROP_CACHESTATE,
144        PROP_DRIVERCLASSNAME,
145        PROP_LIFO,
146        PROP_MAXTOTAL,
147        PROP_MAXIDLE,
148        PROP_MINIDLE,
149        PROP_INITIALSIZE,
150        PROP_MAXWAITMILLIS,
151        PROP_TESTONCREATE,
152        PROP_TESTONBORROW,
153        PROP_TESTONRETURN,
154        PROP_TIMEBETWEENEVICTIONRUNSMILLIS,
155        PROP_NUMTESTSPEREVICTIONRUN,
156        PROP_MINEVICTABLEIDLETIMEMILLIS,
157        PROP_SOFTMINEVICTABLEIDLETIMEMILLIS,
158        PROP_EVICTIONPOLICYCLASSNAME,
159        PROP_TESTWHILEIDLE,
160        PROP_PASSWORD,
161        PROP_URL,
162        PROP_USERNAME,
163        PROP_VALIDATIONQUERY,
164        PROP_VALIDATIONQUERY_TIMEOUT,
165        PROP_CONNECTIONINITSQLS,
166        PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
167        PROP_REMOVEABANDONEDONBORROW,
168        PROP_REMOVEABANDONEDONMAINTENANCE,
169        PROP_REMOVEABANDONEDTIMEOUT,
170        PROP_LOGABANDONED,
171        PROP_ABANDONEDUSAGETRACKING,
172        PROP_POOLPREPAREDSTATEMENTS,
173        PROP_MAXOPENPREPAREDSTATEMENTS,
174        PROP_CONNECTIONPROPERTIES,
175        PROP_MAXCONNLIFETIMEMILLIS,
176        PROP_LOGEXPIREDCONNECTIONS,
177        PROP_ROLLBACK_ON_RETURN,
178        PROP_ENABLE_AUTOCOMMIT_ON_RETURN,
179        PROP_DEFAULT_QUERYTIMEOUT,
180        PROP_FASTFAIL_VALIDATION,
181        PROP_DISCONNECTION_SQL_CODES
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     * @exception Exception if an exception occurs creating the instance
240     */
241    @Override
242    public Object getObjectInstance(Object obj, Name name, Context nameCtx, 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        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 (String warning : warnings) {
260            log.warn(warning);
261        }
262        for (String infoMessage : infoMessages) {
263            log.info(infoMessage);
264        }
265
266        Properties properties = new Properties();
267        for (String propertyName : ALL_PROPERTIES) {
268            RefAddr ra = ref.get(propertyName);
269            if (ra != null) {
270                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(Reference ref, Name name, List<String> warnings,
288                                      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 (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(Properties properties) throws Exception {
338        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 (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          Properties p = getProperties(value);
545          Enumeration<?> e = p.propertyNames();
546          while (e.hasMoreElements()) {
547            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(String propText) throws Exception {
609      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(String value, char delimiter) {
624        StringTokenizer tokenizer = new StringTokenizer(value, Character.toString(delimiter));
625        Collection<String> tokens = new ArrayList<>(tokenizer.countTokens());
626        while (tokenizer.hasMoreTokens()) {
627            tokens.add(tokenizer.nextToken());
628        }
629        return tokens;
630    }
631}