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