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