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