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 1660240 2015-02-16 22:45:31Z 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_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_POOLPREPAREDSTATEMENTS,
171        PROP_MAXOPENPREPAREDSTATEMENTS,
172        PROP_CONNECTIONPROPERTIES,
173        PROP_MAXCONNLIFETIMEMILLIS,
174        PROP_LOGEXPIREDCONNECTIONS,
175        PROP_ROLLBACK_ON_RETURN,
176        PROP_ENABLE_AUTOCOMMIT_ON_RETURN,
177        PROP_DEFAULT_QUERYTIMEOUT,
178        PROP_FASTFAIL_VALIDATION,
179        PROP_DISCONNECTION_SQL_CODES
180    };
181
182    /**
183     * Obsolete properties from DBCP 1.x. with warning strings suggesting
184     * new properties. LinkedHashMap will guarantee that properties will be listed
185     * to output in order of insertion into map.
186     */
187    private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
188
189    static {
190        NUPROP_WARNTEXT.put(
191                NUPROP_MAXACTIVE,
192                "Property " + NUPROP_MAXACTIVE + " is not used in DBCP2, use " + PROP_MAXTOTAL + " instead. "
193                        + PROP_MAXTOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL+".");
194        NUPROP_WARNTEXT.put(
195                NUPROP_REMOVEABANDONED,
196                "Property " + NUPROP_REMOVEABANDONED + " is not used in DBCP2,"
197                        + " use one or both of "
198                        + PROP_REMOVEABANDONEDONBORROW + " or " + PROP_REMOVEABANDONEDONMAINTENANCE + " instead. "
199                        + "Both have default value set to false.");
200        NUPROP_WARNTEXT.put(
201                NUPROP_MAXWAIT,
202                "Property " + NUPROP_MAXWAIT + " is not used in DBCP2"
203                        + " , use " + PROP_MAXWAITMILLIS + " instead. "
204                        + PROP_MAXWAITMILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS+".");
205    }
206
207    /**
208     * Silent Properties.
209     * These properties will not be listed as ignored - we know that they may appear in JDBC Resource references,
210     * and we will not list them as ignored.
211     */
212    private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
213
214    static {
215        SILENT_PROPERTIES.add(SILENTPROP_FACTORY);
216        SILENT_PROPERTIES.add(SILENTPROP_SCOPE);
217        SILENT_PROPERTIES.add(SILENTPROP_SINGLETON);
218        SILENT_PROPERTIES.add(SILENTPROP_AUTH);
219
220    }
221
222    // -------------------------------------------------- ObjectFactory Methods
223
224    /**
225     * <p>Create and return a new <code>BasicDataSource</code> instance.  If no
226     * instance can be created, return <code>null</code> instead.</p>
227     *
228     * @param obj The possibly null object containing location or
229     *  reference information that can be used in creating an object
230     * @param name The name of this object relative to <code>nameCtx</code>
231     * @param nameCtx The context relative to which the <code>name</code>
232     *  parameter is specified, or <code>null</code> if <code>name</code>
233     *  is relative to the default initial context
234     * @param environment The possibly null environment that is used in
235     *  creating this object
236     *
237     * @exception Exception if an exception occurs creating the instance
238     */
239    @Override
240    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?,?> environment)
241        throws Exception {
242
243        // We only know how to deal with <code>javax.naming.Reference</code>s
244        // that specify a class name of "javax.sql.DataSource"
245        if (obj == null || !(obj instanceof Reference)) {
246            return null;
247        }
248        Reference ref = (Reference) obj;
249        if (!"javax.sql.DataSource".equals(ref.getClassName())) {
250            return null;
251        }
252
253        // Check property names and log warnings about obsolete and / or unknown properties
254        final List<String> warnings = new ArrayList<String>();
255        final List<String> infoMessages = new ArrayList<String>();
256        validatePropertyNames(ref, name, warnings, infoMessages);
257        for (String warning : warnings) {
258            log.warn(warning);
259        }
260        for (String infoMessage : infoMessages) {
261            log.info(infoMessage);
262        }
263
264        Properties properties = new Properties();
265        for (String propertyName : ALL_PROPERTIES) {
266            RefAddr ra = ref.get(propertyName);
267            if (ra != null) {
268                String propertyValue = ra.getContent().toString();
269                properties.setProperty(propertyName, propertyValue);
270            }
271        }
272
273        return createDataSource(properties);
274    }
275
276    /**
277     * Collects warnings and info messages.  Warnings are generated when an obsolete
278     * property is set.  Unknown properties generate info messages.
279     *
280     * @param ref Reference to check properties of
281     * @param name Name provided to getObject
282     * @param warnings container for warning messages
283     * @param infoMessages container for info messages
284     */
285    private void validatePropertyNames(Reference ref, Name name, List<String> warnings,
286                                      List<String> infoMessages) {
287        final List<String> allPropsAsList = Arrays.asList(ALL_PROPERTIES);
288        final String nameString = name != null ? "Name = " + name.toString() + " " : ""; 
289        if (NUPROP_WARNTEXT!=null && !NUPROP_WARNTEXT.keySet().isEmpty()) {
290            for (String propertyName : NUPROP_WARNTEXT.keySet()) {
291                final RefAddr ra = ref.get(propertyName);
292                if (ra != null && !allPropsAsList.contains(ra.getType())) {
293                    final StringBuilder stringBuilder = new StringBuilder(nameString);
294                    final String propertyValue = ra.getContent().toString();
295                    stringBuilder.append(NUPROP_WARNTEXT.get(propertyName))
296                            .append(" You have set value of \"")
297                            .append(propertyValue)
298                            .append("\" for \"")
299                            .append(propertyName)
300                            .append("\" property, which is being ignored.");
301                    warnings.add(stringBuilder.toString());
302                }
303            }
304        }
305
306        final Enumeration<RefAddr> allRefAddrs = ref.getAll();
307        while (allRefAddrs.hasMoreElements()) {
308            final RefAddr ra = allRefAddrs.nextElement();
309            final String propertyName = ra.getType();
310            // If property name is not in the properties list, we haven't warned on it
311            // and it is not in the "silent" list, tell user we are ignoring it.
312            if (!(allPropsAsList.contains(propertyName)
313                    || NUPROP_WARNTEXT.keySet().contains(propertyName)
314                    || SILENT_PROPERTIES.contains(propertyName))) {
315                final String propertyValue = ra.getContent().toString();
316                final StringBuilder stringBuilder = new StringBuilder(nameString);
317                stringBuilder.append("Ignoring unknown property: ")
318                        .append("value of \"")
319                        .append(propertyValue)
320                        .append("\" for \"")
321                        .append(propertyName)
322                        .append("\" property");
323                infoMessages.add(stringBuilder.toString());
324            }
325        }
326    }
327
328    /**
329     * Creates and configures a {@link BasicDataSource} instance based on the
330     * given properties.
331     *
332     * @param properties the datasource configuration properties
333     * @throws Exception if an error occurs creating the data source
334     */
335    public static BasicDataSource createDataSource(Properties properties) throws Exception {
336        BasicDataSource dataSource = new BasicDataSource();
337        String value = null;
338
339        value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
340        if (value != null) {
341            dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
342        }
343
344        value = properties.getProperty(PROP_DEFAULTREADONLY);
345        if (value != null) {
346            dataSource.setDefaultReadOnly(Boolean.valueOf(value));
347        }
348
349        value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
350        if (value != null) {
351            int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
352            if ("NONE".equalsIgnoreCase(value)) {
353                level = Connection.TRANSACTION_NONE;
354            }
355            else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
356                level = Connection.TRANSACTION_READ_COMMITTED;
357            }
358            else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
359                level = Connection.TRANSACTION_READ_UNCOMMITTED;
360            }
361            else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
362                level = Connection.TRANSACTION_REPEATABLE_READ;
363            }
364            else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
365                level = Connection.TRANSACTION_SERIALIZABLE;
366            }
367            else {
368                try {
369                    level = Integer.parseInt(value);
370                } catch (NumberFormatException e) {
371                    System.err.println("Could not parse defaultTransactionIsolation: " + value);
372                    System.err.println("WARNING: defaultTransactionIsolation not set");
373                    System.err.println("using default value of database driver");
374                    level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
375                }
376            }
377            dataSource.setDefaultTransactionIsolation(level);
378        }
379
380        value = properties.getProperty(PROP_DEFAULTCATALOG);
381        if (value != null) {
382            dataSource.setDefaultCatalog(value);
383        }
384
385        value = properties.getProperty(PROP_CACHESTATE);
386        if (value != null) {
387            dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
388        }
389
390        value = properties.getProperty(PROP_DRIVERCLASSNAME);
391        if (value != null) {
392            dataSource.setDriverClassName(value);
393        }
394
395        value = properties.getProperty(PROP_LIFO);
396        if (value != null) {
397            dataSource.setLifo(Boolean.valueOf(value).booleanValue());
398        }
399
400        value = properties.getProperty(PROP_MAXTOTAL);
401        if (value != null) {
402            dataSource.setMaxTotal(Integer.parseInt(value));
403        }
404
405        value = properties.getProperty(PROP_MAXIDLE);
406        if (value != null) {
407            dataSource.setMaxIdle(Integer.parseInt(value));
408        }
409
410        value = properties.getProperty(PROP_MINIDLE);
411        if (value != null) {
412            dataSource.setMinIdle(Integer.parseInt(value));
413        }
414
415        value = properties.getProperty(PROP_INITIALSIZE);
416        if (value != null) {
417            dataSource.setInitialSize(Integer.parseInt(value));
418        }
419
420        value = properties.getProperty(PROP_MAXWAITMILLIS);
421        if (value != null) {
422            dataSource.setMaxWaitMillis(Long.parseLong(value));
423        }
424
425        value = properties.getProperty(PROP_TESTONCREATE);
426        if (value != null) {
427            dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
428        }
429
430        value = properties.getProperty(PROP_TESTONBORROW);
431        if (value != null) {
432            dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
433        }
434
435        value = properties.getProperty(PROP_TESTONRETURN);
436        if (value != null) {
437            dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
438        }
439
440        value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
441        if (value != null) {
442            dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
443        }
444
445        value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
446        if (value != null) {
447            dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
448        }
449
450        value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
451        if (value != null) {
452            dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
453        }
454
455        value = properties.getProperty(PROP_SOFTMINEVICTABLEIDLETIMEMILLIS);
456        if (value != null) {
457            dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
458        }
459
460        value = properties.getProperty(PROP_EVICTIONPOLICYCLASSNAME);
461        if (value != null) {
462            dataSource.setEvictionPolicyClassName(value);
463        }
464
465        value = properties.getProperty(PROP_TESTWHILEIDLE);
466        if (value != null) {
467            dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
468        }
469
470        value = properties.getProperty(PROP_PASSWORD);
471        if (value != null) {
472            dataSource.setPassword(value);
473        }
474
475        value = properties.getProperty(PROP_URL);
476        if (value != null) {
477            dataSource.setUrl(value);
478        }
479
480        value = properties.getProperty(PROP_USERNAME);
481        if (value != null) {
482            dataSource.setUsername(value);
483        }
484
485        value = properties.getProperty(PROP_VALIDATIONQUERY);
486        if (value != null) {
487            dataSource.setValidationQuery(value);
488        }
489
490        value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
491        if (value != null) {
492            dataSource.setValidationQueryTimeout(Integer.parseInt(value));
493        }
494
495        value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
496        if (value != null) {
497            dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
498        }
499
500        value = properties.getProperty(PROP_REMOVEABANDONEDONBORROW);
501        if (value != null) {
502            dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
503        }
504
505        value = properties.getProperty(PROP_REMOVEABANDONEDONMAINTENANCE);
506        if (value != null) {
507            dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
508        }
509
510        value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
511        if (value != null) {
512            dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
513        }
514
515        value = properties.getProperty(PROP_LOGABANDONED);
516        if (value != null) {
517            dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
518        }
519
520        value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
521        if (value != null) {
522            dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
523        }
524
525        value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
526        if (value != null) {
527            dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
528        }
529
530        value = properties.getProperty(PROP_CONNECTIONINITSQLS);
531        if (value != null) {
532            dataSource.setConnectionInitSqls(parseList(value, ';'));
533        }
534
535        value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
536        if (value != null) {
537          Properties p = getProperties(value);
538          Enumeration<?> e = p.propertyNames();
539          while (e.hasMoreElements()) {
540            String propertyName = (String) e.nextElement();
541            dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
542          }
543        }
544
545        value = properties.getProperty(PROP_MAXCONNLIFETIMEMILLIS);
546        if (value != null) {
547            dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
548        }
549
550        value = properties.getProperty(PROP_LOGEXPIREDCONNECTIONS);
551        if (value != null) {
552            dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
553        }
554
555        value = properties.getProperty(PROP_JMX_NAME);
556        if (value != null) {
557            dataSource.setJmxName(value);
558        }
559
560        value = properties.getProperty(PROP_ENABLE_AUTOCOMMIT_ON_RETURN);
561        if (value != null) {
562            dataSource.setEnableAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
563        }
564
565        value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
566        if (value != null) {
567            dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
568        }
569
570        value = properties.getProperty(PROP_DEFAULT_QUERYTIMEOUT);
571        if (value != null) {
572            dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
573        }
574
575        value = properties.getProperty(PROP_FASTFAIL_VALIDATION);
576        if (value != null) {
577            dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
578        }
579
580        value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
581        if (value != null) {
582            dataSource.setDisconnectionSqlCodes(parseList(value, ','));
583        }
584
585        // DBCP-215
586        // Trick to make sure that initialSize connections are created
587        if (dataSource.getInitialSize() > 0) {
588            dataSource.getLogWriter();
589        }
590
591        // Return the configured DataSource instance
592        return dataSource;
593    }
594
595    /**
596     * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p>
597     * @param propText
598     * @return Properties
599     * @throws Exception
600     */
601    private static Properties getProperties(String propText) throws Exception {
602      Properties p = new Properties();
603      if (propText != null) {
604        p.load(new ByteArrayInputStream(
605                propText.replace(';', '\n').getBytes(StandardCharsets.ISO_8859_1)));
606      }
607      return p;
608    }
609
610    /**
611     * Parse list of property values from a delimited string
612     * @param value delimited list of values
613     * @param delimiter character used to separate values in the list
614     * @return String Collection of values
615     */
616    private static Collection<String> parseList(String value, char delimiter) {
617        StringTokenizer tokenizer = new StringTokenizer(value, Character.toString(delimiter));
618        Collection<String> tokens =
619                new ArrayList<String>(tokenizer.countTokens());
620        while (tokenizer.hasMoreTokens()) {
621            tokens.add(tokenizer.nextToken());
622        }
623        return tokens;
624    }
625}