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.Collection; 025import java.util.Enumeration; 026import java.util.Hashtable; 027import java.util.Properties; 028import java.util.StringTokenizer; 029 030import javax.naming.Context; 031import javax.naming.Name; 032import javax.naming.RefAddr; 033import javax.naming.Reference; 034import javax.naming.spi.ObjectFactory; 035 036/** 037 * <p>JNDI object factory that creates an instance of 038 * <code>BasicDataSource</code> that has been configured based on the 039 * <code>RefAddr</code> values of the specified <code>Reference</code>, which 040 * must match the names and data types of the <code>BasicDataSource</code> bean 041 * properties with the following exceptions:</p> 042 * <ul> 043 * <li><code>connectionInitSqls</code> must be passed to this factory as a 044 * single String using semi-colon to delimt the statements whereas 045 * <code>BasicDataSource</code> requires a collection of Strings.</li> 046 * </ul> 047 * 048 * @author Craig R. McClanahan 049 * @author Dirk Verbeeck 050 * @version $Revision: 1572242 $ $Date: 2014-02-26 20:34:39 +0000 (Wed, 26 Feb 2014) $ 051 * @since 2.0 052 */ 053public class BasicDataSourceFactory implements ObjectFactory { 054 055 private final static String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; 056 private final static String PROP_DEFAULTREADONLY = "defaultReadOnly"; 057 private final static String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; 058 private final static String PROP_DEFAULTCATALOG = "defaultCatalog"; 059 private final static String PROP_CACHESTATE ="cacheState"; 060 private final static String PROP_DRIVERCLASSNAME = "driverClassName"; 061 private final static String PROP_LIFO = "lifo"; 062 private final static String PROP_MAXTOTAL = "maxTotal"; 063 private final static String PROP_MAXIDLE = "maxIdle"; 064 private final static String PROP_MINIDLE = "minIdle"; 065 private final static String PROP_INITIALSIZE = "initialSize"; 066 private final static String PROP_MAXWAITMILLIS = "maxWaitMillis"; 067 private final static String PROP_TESTONCREATE = "testOnCreate"; 068 private final static String PROP_TESTONBORROW = "testOnBorrow"; 069 private final static String PROP_TESTONRETURN = "testOnReturn"; 070 private final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; 071 private final static String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; 072 private final static String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; 073 private final static String PROP_SOFTMINEVICTABLEIDLETIMEMILLIS = "softMinEvictableIdleTimeMillis"; 074 private final static String PROP_EVICTIONPOLICYCLASSNAME = "evictionPolicyClassName"; 075 private final static String PROP_TESTWHILEIDLE = "testWhileIdle"; 076 private final static String PROP_PASSWORD = "password"; 077 private final static String PROP_URL = "url"; 078 private final static String PROP_USERNAME = "username"; 079 private final static String PROP_VALIDATIONQUERY = "validationQuery"; 080 private final static String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; 081 private final static String PROP_JMX_NAME = "jmxName"; 082 083 /** 084 * The property name for connectionInitSqls. 085 * The associated value String must be of the form [query;]* 086 */ 087 private final static String PROP_CONNECTIONINITSQLS = "connectionInitSqls"; 088 private final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; 089 private final static String PROP_REMOVEABANDONEDONBORROW = "removeAbandonedOnBorrow"; 090 private final static String PROP_REMOVEABANDONEDONMAINTENANCE = "removeAbandonedOnMaintenance"; 091 private final static String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; 092 private final static String PROP_LOGABANDONED = "logAbandoned"; 093 private final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; 094 private final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; 095 private final static String PROP_CONNECTIONPROPERTIES = "connectionProperties"; 096 private final static String PROP_MAXCONNLIFETIMEMILLIS = "maxConnLifetimeMillis"; 097 private final static String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn"; 098 private final static String PROP_ENABLE_AUTOCOMMIT_ON_RETURN = "enableAutoCommitOnReturn"; 099 private final static String PROP_DEFAULT_QUERYTIMEOUT = "defaultQueryTimeout"; 100 101 private final static String[] ALL_PROPERTIES = { 102 PROP_DEFAULTAUTOCOMMIT, 103 PROP_DEFAULTREADONLY, 104 PROP_DEFAULTTRANSACTIONISOLATION, 105 PROP_DEFAULTCATALOG, 106 PROP_CACHESTATE, 107 PROP_DRIVERCLASSNAME, 108 PROP_LIFO, 109 PROP_MAXTOTAL, 110 PROP_MAXIDLE, 111 PROP_MINIDLE, 112 PROP_INITIALSIZE, 113 PROP_MAXWAITMILLIS, 114 PROP_TESTONCREATE, 115 PROP_TESTONBORROW, 116 PROP_TESTONRETURN, 117 PROP_TIMEBETWEENEVICTIONRUNSMILLIS, 118 PROP_NUMTESTSPEREVICTIONRUN, 119 PROP_MINEVICTABLEIDLETIMEMILLIS, 120 PROP_SOFTMINEVICTABLEIDLETIMEMILLIS, 121 PROP_EVICTIONPOLICYCLASSNAME, 122 PROP_TESTWHILEIDLE, 123 PROP_PASSWORD, 124 PROP_URL, 125 PROP_USERNAME, 126 PROP_VALIDATIONQUERY, 127 PROP_VALIDATIONQUERY_TIMEOUT, 128 PROP_CONNECTIONINITSQLS, 129 PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, 130 PROP_REMOVEABANDONEDONBORROW, 131 PROP_REMOVEABANDONEDONMAINTENANCE, 132 PROP_REMOVEABANDONEDTIMEOUT, 133 PROP_LOGABANDONED, 134 PROP_POOLPREPAREDSTATEMENTS, 135 PROP_MAXOPENPREPAREDSTATEMENTS, 136 PROP_CONNECTIONPROPERTIES, 137 PROP_MAXCONNLIFETIMEMILLIS, 138 PROP_ROLLBACK_ON_RETURN, 139 PROP_ENABLE_AUTOCOMMIT_ON_RETURN, 140 PROP_DEFAULT_QUERYTIMEOUT 141 }; 142 143 // -------------------------------------------------- ObjectFactory Methods 144 145 /** 146 * <p>Create and return a new <code>BasicDataSource</code> instance. If no 147 * instance can be created, return <code>null</code> instead.</p> 148 * 149 * @param obj The possibly null object containing location or 150 * reference information that can be used in creating an object 151 * @param name The name of this object relative to <code>nameCtx</code> 152 * @param nameCtx The context relative to which the <code>name</code> 153 * parameter is specified, or <code>null</code> if <code>name</code> 154 * is relative to the default initial context 155 * @param environment The possibly null environment that is used in 156 * creating this object 157 * 158 * @exception Exception if an exception occurs creating the instance 159 */ 160 @Override 161 public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?,?> environment) 162 throws Exception { 163 164 // We only know how to deal with <code>javax.naming.Reference</code>s 165 // that specify a class name of "javax.sql.DataSource" 166 if (obj == null || !(obj instanceof Reference)) { 167 return null; 168 } 169 Reference ref = (Reference) obj; 170 if (!"javax.sql.DataSource".equals(ref.getClassName())) { 171 return null; 172 } 173 174 Properties properties = new Properties(); 175 for (String propertyName : ALL_PROPERTIES) { 176 RefAddr ra = ref.get(propertyName); 177 if (ra != null) { 178 String propertyValue = ra.getContent().toString(); 179 properties.setProperty(propertyName, propertyValue); 180 } 181 } 182 183 return createDataSource(properties); 184 } 185 186 /** 187 * Creates and configures a {@link BasicDataSource} instance based on the 188 * given properties. 189 * 190 * @param properties the datasource configuration properties 191 * @throws Exception if an error occurs creating the data source 192 */ 193 public static BasicDataSource createDataSource(Properties properties) throws Exception { 194 BasicDataSource dataSource = new BasicDataSource(); 195 String value = null; 196 197 value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); 198 if (value != null) { 199 dataSource.setDefaultAutoCommit(Boolean.valueOf(value)); 200 } 201 202 value = properties.getProperty(PROP_DEFAULTREADONLY); 203 if (value != null) { 204 dataSource.setDefaultReadOnly(Boolean.valueOf(value)); 205 } 206 207 value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); 208 if (value != null) { 209 int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; 210 if ("NONE".equalsIgnoreCase(value)) { 211 level = Connection.TRANSACTION_NONE; 212 } 213 else if ("READ_COMMITTED".equalsIgnoreCase(value)) { 214 level = Connection.TRANSACTION_READ_COMMITTED; 215 } 216 else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { 217 level = Connection.TRANSACTION_READ_UNCOMMITTED; 218 } 219 else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { 220 level = Connection.TRANSACTION_REPEATABLE_READ; 221 } 222 else if ("SERIALIZABLE".equalsIgnoreCase(value)) { 223 level = Connection.TRANSACTION_SERIALIZABLE; 224 } 225 else { 226 try { 227 level = Integer.parseInt(value); 228 } catch (NumberFormatException e) { 229 System.err.println("Could not parse defaultTransactionIsolation: " + value); 230 System.err.println("WARNING: defaultTransactionIsolation not set"); 231 System.err.println("using default value of database driver"); 232 level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; 233 } 234 } 235 dataSource.setDefaultTransactionIsolation(level); 236 } 237 238 value = properties.getProperty(PROP_DEFAULTCATALOG); 239 if (value != null) { 240 dataSource.setDefaultCatalog(value); 241 } 242 243 value = properties.getProperty(PROP_CACHESTATE); 244 if (value != null) { 245 dataSource.setCacheState(Boolean.valueOf(value).booleanValue()); 246 } 247 248 value = properties.getProperty(PROP_DRIVERCLASSNAME); 249 if (value != null) { 250 dataSource.setDriverClassName(value); 251 } 252 253 value = properties.getProperty(PROP_LIFO); 254 if (value != null) { 255 dataSource.setLifo(Boolean.valueOf(value).booleanValue()); 256 } 257 258 value = properties.getProperty(PROP_MAXTOTAL); 259 if (value != null) { 260 dataSource.setMaxTotal(Integer.parseInt(value)); 261 } 262 263 value = properties.getProperty(PROP_MAXIDLE); 264 if (value != null) { 265 dataSource.setMaxIdle(Integer.parseInt(value)); 266 } 267 268 value = properties.getProperty(PROP_MINIDLE); 269 if (value != null) { 270 dataSource.setMinIdle(Integer.parseInt(value)); 271 } 272 273 value = properties.getProperty(PROP_INITIALSIZE); 274 if (value != null) { 275 dataSource.setInitialSize(Integer.parseInt(value)); 276 } 277 278 value = properties.getProperty(PROP_MAXWAITMILLIS); 279 if (value != null) { 280 dataSource.setMaxWaitMillis(Long.parseLong(value)); 281 } 282 283 value = properties.getProperty(PROP_TESTONCREATE); 284 if (value != null) { 285 dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue()); 286 } 287 288 value = properties.getProperty(PROP_TESTONBORROW); 289 if (value != null) { 290 dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue()); 291 } 292 293 value = properties.getProperty(PROP_TESTONRETURN); 294 if (value != null) { 295 dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue()); 296 } 297 298 value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); 299 if (value != null) { 300 dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value)); 301 } 302 303 value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); 304 if (value != null) { 305 dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value)); 306 } 307 308 value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); 309 if (value != null) { 310 dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value)); 311 } 312 313 value = properties.getProperty(PROP_SOFTMINEVICTABLEIDLETIMEMILLIS); 314 if (value != null) { 315 dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value)); 316 } 317 318 value = properties.getProperty(PROP_EVICTIONPOLICYCLASSNAME); 319 if (value != null) { 320 dataSource.setEvictionPolicyClassName(value); 321 } 322 323 value = properties.getProperty(PROP_TESTWHILEIDLE); 324 if (value != null) { 325 dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue()); 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_USERNAME); 339 if (value != null) { 340 dataSource.setUsername(value); 341 } 342 343 value = properties.getProperty(PROP_VALIDATIONQUERY); 344 if (value != null) { 345 dataSource.setValidationQuery(value); 346 } 347 348 value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); 349 if (value != null) { 350 dataSource.setValidationQueryTimeout(Integer.parseInt(value)); 351 } 352 353 value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); 354 if (value != null) { 355 dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue()); 356 } 357 358 value = properties.getProperty(PROP_REMOVEABANDONEDONBORROW); 359 if (value != null) { 360 dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue()); 361 } 362 363 value = properties.getProperty(PROP_REMOVEABANDONEDONMAINTENANCE); 364 if (value != null) { 365 dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue()); 366 } 367 368 value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); 369 if (value != null) { 370 dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value)); 371 } 372 373 value = properties.getProperty(PROP_LOGABANDONED); 374 if (value != null) { 375 dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue()); 376 } 377 378 value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); 379 if (value != null) { 380 dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue()); 381 } 382 383 value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); 384 if (value != null) { 385 dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value)); 386 } 387 388 value = properties.getProperty(PROP_CONNECTIONINITSQLS); 389 if (value != null) { 390 StringTokenizer tokenizer = new StringTokenizer(value, ";"); 391 // Have to jump through these hoops as StringTokenizer implements 392 // Enumeration<Object> rather than Enumeration<String> 393 Collection<String> tokens = 394 new ArrayList<>(tokenizer.countTokens()); 395 while (tokenizer.hasMoreTokens()) { 396 tokens.add(tokenizer.nextToken()); 397 } 398 dataSource.setConnectionInitSqls(tokens); 399 } 400 401 value = properties.getProperty(PROP_CONNECTIONPROPERTIES); 402 if (value != null) { 403 Properties p = getProperties(value); 404 Enumeration<?> e = p.propertyNames(); 405 while (e.hasMoreElements()) { 406 String propertyName = (String) e.nextElement(); 407 dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName)); 408 } 409 } 410 411 value = properties.getProperty(PROP_MAXCONNLIFETIMEMILLIS); 412 if (value != null) { 413 dataSource.setMaxConnLifetimeMillis(Long.parseLong(value)); 414 } 415 416 value = properties.getProperty(PROP_JMX_NAME); 417 if (value != null) { 418 dataSource.setJmxName(value); 419 } 420 421 value = properties.getProperty(PROP_ENABLE_AUTOCOMMIT_ON_RETURN); 422 if (value != null) { 423 dataSource.setEnableAutoCommitOnReturn(Boolean.valueOf(value).booleanValue()); 424 } 425 426 value = properties.getProperty(PROP_ROLLBACK_ON_RETURN); 427 if (value != null) { 428 dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue()); 429 } 430 431 value = properties.getProperty(PROP_DEFAULT_QUERYTIMEOUT); 432 if (value != null) { 433 dataSource.setDefaultQueryTimeout(Integer.valueOf(value)); 434 } 435 436 437 // DBCP-215 438 // Trick to make sure that initialSize connections are created 439 if (dataSource.getInitialSize() > 0) { 440 dataSource.getLogWriter(); 441 } 442 443 // Return the configured DataSource instance 444 return dataSource; 445 } 446 447 /** 448 * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p> 449 * @param propText 450 * @return Properties 451 * @throws Exception 452 */ 453 static private Properties getProperties(String propText) throws Exception { 454 Properties p = new Properties(); 455 if (propText != null) { 456 p.load(new ByteArrayInputStream( 457 propText.replace(';', '\n').getBytes(StandardCharsets.ISO_8859_1))); 458 } 459 return p; 460 } 461}