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}