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