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 018 package org.apache.commons.dbcp; 019 020 import java.io.ByteArrayInputStream; 021 import java.sql.Connection; 022 import java.util.Enumeration; 023 import java.util.Hashtable; 024 import java.util.Properties; 025 import java.util.StringTokenizer; 026 import java.util.Collections; 027 028 import javax.naming.Context; 029 import javax.naming.Name; 030 import javax.naming.RefAddr; 031 import javax.naming.Reference; 032 import javax.naming.spi.ObjectFactory; 033 import javax.sql.DataSource; 034 035 /** 036 * <p>JNDI object factory that creates an instance of 037 * <code>BasicDataSource</code> that has been configured based on the 038 * <code>RefAddr</code> values of the specified <code>Reference</code>, 039 * which must match the names and data types of the 040 * <code>BasicDataSource</code> bean properties.</p> 041 * 042 * @author Craig R. McClanahan 043 * @author Dirk Verbeeck 044 * @version $Revision: 828639 $ $Date: 2009-10-22 06:27:43 -0400 (Thu, 22 Oct 2009) $ 045 */ 046 public class BasicDataSourceFactory implements ObjectFactory { 047 048 private final static String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; 049 private final static String PROP_DEFAULTREADONLY = "defaultReadOnly"; 050 private final static String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; 051 private final static String PROP_DEFAULTCATALOG = "defaultCatalog"; 052 private final static String PROP_DRIVERCLASSNAME = "driverClassName"; 053 private final static String PROP_MAXACTIVE = "maxActive"; 054 private final static String PROP_MAXIDLE = "maxIdle"; 055 private final static String PROP_MINIDLE = "minIdle"; 056 private final static String PROP_INITIALSIZE = "initialSize"; 057 private final static String PROP_MAXWAIT = "maxWait"; 058 private final static String PROP_TESTONBORROW = "testOnBorrow"; 059 private final static String PROP_TESTONRETURN = "testOnReturn"; 060 private final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; 061 private final static String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; 062 private final static String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; 063 private final static String PROP_TESTWHILEIDLE = "testWhileIdle"; 064 private final static String PROP_PASSWORD = "password"; 065 private final static String PROP_URL = "url"; 066 private final static String PROP_USERNAME = "username"; 067 private final static String PROP_VALIDATIONQUERY = "validationQuery"; 068 private final static String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; 069 /** 070 * The property name for initConnectionSqls. 071 * The associated value String must be of the form [query;]* 072 * @since 1.3 073 */ 074 private final static String PROP_INITCONNECTIONSQLS = "initConnectionSqls"; 075 private final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; 076 private final static String PROP_REMOVEABANDONED = "removeAbandoned"; 077 private final static String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; 078 private final static String PROP_LOGABANDONED = "logAbandoned"; 079 private final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; 080 private final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; 081 private final static String PROP_CONNECTIONPROPERTIES = "connectionProperties"; 082 083 private final static String[] ALL_PROPERTIES = { 084 PROP_DEFAULTAUTOCOMMIT, 085 PROP_DEFAULTREADONLY, 086 PROP_DEFAULTTRANSACTIONISOLATION, 087 PROP_DEFAULTCATALOG, 088 PROP_DRIVERCLASSNAME, 089 PROP_MAXACTIVE, 090 PROP_MAXIDLE, 091 PROP_MINIDLE, 092 PROP_INITIALSIZE, 093 PROP_MAXWAIT, 094 PROP_TESTONBORROW, 095 PROP_TESTONRETURN, 096 PROP_TIMEBETWEENEVICTIONRUNSMILLIS, 097 PROP_NUMTESTSPEREVICTIONRUN, 098 PROP_MINEVICTABLEIDLETIMEMILLIS, 099 PROP_TESTWHILEIDLE, 100 PROP_PASSWORD, 101 PROP_URL, 102 PROP_USERNAME, 103 PROP_VALIDATIONQUERY, 104 PROP_VALIDATIONQUERY_TIMEOUT, 105 PROP_INITCONNECTIONSQLS, 106 PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, 107 PROP_REMOVEABANDONED, 108 PROP_REMOVEABANDONEDTIMEOUT, 109 PROP_LOGABANDONED, 110 PROP_POOLPREPAREDSTATEMENTS, 111 PROP_MAXOPENPREPAREDSTATEMENTS, 112 PROP_CONNECTIONPROPERTIES 113 }; 114 115 // -------------------------------------------------- ObjectFactory Methods 116 117 /** 118 * <p>Create and return a new <code>BasicDataSource</code> instance. If no 119 * instance can be created, return <code>null</code> instead.</p> 120 * 121 * @param obj The possibly null object containing location or 122 * reference information that can be used in creating an object 123 * @param name The name of this object relative to <code>nameCtx</code> 124 * @param nameCtx The context relative to which the <code>name</code> 125 * parameter is specified, or <code>null</code> if <code>name</code> 126 * is relative to the default initial context 127 * @param environment The possibly null environment that is used in 128 * creating this object 129 * 130 * @exception Exception if an exception occurs creating the instance 131 */ 132 public Object getObjectInstance(Object obj, Name name, Context nameCtx, 133 Hashtable environment) 134 throws Exception { 135 136 // We only know how to deal with <code>javax.naming.Reference</code>s 137 // that specify a class name of "javax.sql.DataSource" 138 if ((obj == null) || !(obj instanceof Reference)) { 139 return null; 140 } 141 Reference ref = (Reference) obj; 142 if (!"javax.sql.DataSource".equals(ref.getClassName())) { 143 return null; 144 } 145 146 Properties properties = new Properties(); 147 for (int i = 0 ; i < ALL_PROPERTIES.length ; i++) { 148 String propertyName = ALL_PROPERTIES[i]; 149 RefAddr ra = ref.get(propertyName); 150 if (ra != null) { 151 String propertyValue = ra.getContent().toString(); 152 properties.setProperty(propertyName, propertyValue); 153 } 154 } 155 156 return createDataSource(properties); 157 } 158 159 /** 160 * Creates and configures a {@link BasicDataSource} instance based on the 161 * given properties. 162 * 163 * @param properties the datasource configuration properties 164 * @throws Exception if an error occurs creating the data source 165 */ 166 public static DataSource createDataSource(Properties properties) throws Exception { 167 BasicDataSource dataSource = new BasicDataSource(); 168 String value = null; 169 170 value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); 171 if (value != null) { 172 dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue()); 173 } 174 175 value = properties.getProperty(PROP_DEFAULTREADONLY); 176 if (value != null) { 177 dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue()); 178 } 179 180 value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); 181 if (value != null) { 182 int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; 183 if ("NONE".equalsIgnoreCase(value)) { 184 level = Connection.TRANSACTION_NONE; 185 } 186 else if ("READ_COMMITTED".equalsIgnoreCase(value)) { 187 level = Connection.TRANSACTION_READ_COMMITTED; 188 } 189 else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { 190 level = Connection.TRANSACTION_READ_UNCOMMITTED; 191 } 192 else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { 193 level = Connection.TRANSACTION_REPEATABLE_READ; 194 } 195 else if ("SERIALIZABLE".equalsIgnoreCase(value)) { 196 level = Connection.TRANSACTION_SERIALIZABLE; 197 } 198 else { 199 try { 200 level = Integer.parseInt(value); 201 } catch (NumberFormatException e) { 202 System.err.println("Could not parse defaultTransactionIsolation: " + value); 203 System.err.println("WARNING: defaultTransactionIsolation not set"); 204 System.err.println("using default value of database driver"); 205 level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; 206 } 207 } 208 dataSource.setDefaultTransactionIsolation(level); 209 } 210 211 value = properties.getProperty(PROP_DEFAULTCATALOG); 212 if (value != null) { 213 dataSource.setDefaultCatalog(value); 214 } 215 216 value = properties.getProperty(PROP_DRIVERCLASSNAME); 217 if (value != null) { 218 dataSource.setDriverClassName(value); 219 } 220 221 value = properties.getProperty(PROP_MAXACTIVE); 222 if (value != null) { 223 dataSource.setMaxActive(Integer.parseInt(value)); 224 } 225 226 value = properties.getProperty(PROP_MAXIDLE); 227 if (value != null) { 228 dataSource.setMaxIdle(Integer.parseInt(value)); 229 } 230 231 value = properties.getProperty(PROP_MINIDLE); 232 if (value != null) { 233 dataSource.setMinIdle(Integer.parseInt(value)); 234 } 235 236 value = properties.getProperty(PROP_INITIALSIZE); 237 if (value != null) { 238 dataSource.setInitialSize(Integer.parseInt(value)); 239 } 240 241 value = properties.getProperty(PROP_MAXWAIT); 242 if (value != null) { 243 dataSource.setMaxWait(Long.parseLong(value)); 244 } 245 246 value = properties.getProperty(PROP_TESTONBORROW); 247 if (value != null) { 248 dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue()); 249 } 250 251 value = properties.getProperty(PROP_TESTONRETURN); 252 if (value != null) { 253 dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue()); 254 } 255 256 value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); 257 if (value != null) { 258 dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value)); 259 } 260 261 value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); 262 if (value != null) { 263 dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value)); 264 } 265 266 value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); 267 if (value != null) { 268 dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value)); 269 } 270 271 value = properties.getProperty(PROP_TESTWHILEIDLE); 272 if (value != null) { 273 dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue()); 274 } 275 276 value = properties.getProperty(PROP_PASSWORD); 277 if (value != null) { 278 dataSource.setPassword(value); 279 } 280 281 value = properties.getProperty(PROP_URL); 282 if (value != null) { 283 dataSource.setUrl(value); 284 } 285 286 value = properties.getProperty(PROP_USERNAME); 287 if (value != null) { 288 dataSource.setUsername(value); 289 } 290 291 value = properties.getProperty(PROP_VALIDATIONQUERY); 292 if (value != null) { 293 dataSource.setValidationQuery(value); 294 } 295 296 value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); 297 if (value != null) { 298 dataSource.setValidationQueryTimeout(Integer.parseInt(value)); 299 } 300 301 value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); 302 if (value != null) { 303 dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue()); 304 } 305 306 value = properties.getProperty(PROP_REMOVEABANDONED); 307 if (value != null) { 308 dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue()); 309 } 310 311 value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); 312 if (value != null) { 313 dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value)); 314 } 315 316 value = properties.getProperty(PROP_LOGABANDONED); 317 if (value != null) { 318 dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue()); 319 } 320 321 value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); 322 if (value != null) { 323 dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue()); 324 } 325 326 value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); 327 if (value != null) { 328 dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value)); 329 } 330 331 value = properties.getProperty(PROP_INITCONNECTIONSQLS); 332 if (value != null) { 333 StringTokenizer tokenizer = new StringTokenizer(value, ";"); 334 dataSource.setConnectionInitSqls(Collections.list(tokenizer)); 335 } 336 337 value = properties.getProperty(PROP_CONNECTIONPROPERTIES); 338 if (value != null) { 339 Properties p = getProperties(value); 340 Enumeration e = p.propertyNames(); 341 while (e.hasMoreElements()) { 342 String propertyName = (String) e.nextElement(); 343 dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName)); 344 } 345 } 346 347 // DBCP-215 348 // Trick to make sure that initialSize connections are created 349 if (dataSource.getInitialSize() > 0) { 350 dataSource.getLogWriter(); 351 } 352 353 // Return the configured DataSource instance 354 return dataSource; 355 } 356 357 /** 358 * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p> 359 * @param propText 360 * @return Properties 361 * @throws Exception 362 */ 363 static private Properties getProperties(String propText) throws Exception { 364 Properties p = new Properties(); 365 if (propText != null) { 366 p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes())); 367 } 368 return p; 369 } 370 }