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