1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.dbcp;
19
20 import java.io.ByteArrayInputStream;
21 import java.sql.Connection;
22 import java.util.Enumeration;
23 import java.util.Hashtable;
24 import java.util.Properties;
25
26 import javax.naming.Context;
27 import javax.naming.Name;
28 import javax.naming.RefAddr;
29 import javax.naming.Reference;
30 import javax.naming.spi.ObjectFactory;
31 import javax.sql.DataSource;
32
33 /**
34 * <p>JNDI object factory that creates an instance of
35 * <code>BasicDataSource</code> that has been configured based on the
36 * <code>RefAddr</code> values of the specified <code>Reference</code>,
37 * which must match the names and data types of the
38 * <code>BasicDataSource</code> bean properties.</p>
39 *
40 * @author Craig R. McClanahan
41 * @author Dirk Verbeeck
42 * @version $Revision: 491655 $ $Date: 2007-01-01 15:05:30 -0700 (Mon, 01 Jan 2007) $
43 */
44 public class BasicDataSourceFactory implements ObjectFactory {
45
46 private final static String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
47 private final static String PROP_DEFAULTREADONLY = "defaultReadOnly";
48 private final static String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
49 private final static String PROP_DEFAULTCATALOG = "defaultCatalog";
50 private final static String PROP_DRIVERCLASSNAME = "driverClassName";
51 private final static String PROP_MAXACTIVE = "maxActive";
52 private final static String PROP_MAXIDLE = "maxIdle";
53 private final static String PROP_MINIDLE = "minIdle";
54 private final static String PROP_INITIALSIZE = "initialSize";
55 private final static String PROP_MAXWAIT = "maxWait";
56 private final static String PROP_TESTONBORROW = "testOnBorrow";
57 private final static String PROP_TESTONRETURN = "testOnReturn";
58 private final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
59 private final static String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
60 private final static String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
61 private final static String PROP_TESTWHILEIDLE = "testWhileIdle";
62 private final static String PROP_PASSWORD = "password";
63 private final static String PROP_URL = "url";
64 private final static String PROP_USERNAME = "username";
65 private final static String PROP_VALIDATIONQUERY = "validationQuery";
66 private final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
67 private final static String PROP_REMOVEABANDONED = "removeAbandoned";
68 private final static String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
69 private final static String PROP_LOGABANDONED = "logAbandoned";
70 private final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
71 private final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
72 private final static String PROP_CONNECTIONPROPERTIES = "connectionProperties";
73
74 private final static String[] ALL_PROPERTIES = {
75 PROP_DEFAULTAUTOCOMMIT,
76 PROP_DEFAULTREADONLY,
77 PROP_DEFAULTTRANSACTIONISOLATION,
78 PROP_DEFAULTCATALOG,
79 PROP_DRIVERCLASSNAME,
80 PROP_MAXACTIVE,
81 PROP_MAXIDLE,
82 PROP_MINIDLE,
83 PROP_INITIALSIZE,
84 PROP_MAXWAIT,
85 PROP_TESTONBORROW,
86 PROP_TESTONRETURN,
87 PROP_TIMEBETWEENEVICTIONRUNSMILLIS,
88 PROP_NUMTESTSPEREVICTIONRUN,
89 PROP_MINEVICTABLEIDLETIMEMILLIS,
90 PROP_TESTWHILEIDLE,
91 PROP_PASSWORD,
92 PROP_URL,
93 PROP_USERNAME,
94 PROP_VALIDATIONQUERY,
95 PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
96 PROP_REMOVEABANDONED,
97 PROP_REMOVEABANDONEDTIMEOUT,
98 PROP_LOGABANDONED,
99 PROP_POOLPREPAREDSTATEMENTS,
100 PROP_MAXOPENPREPAREDSTATEMENTS,
101 PROP_CONNECTIONPROPERTIES
102 };
103
104 // -------------------------------------------------- ObjectFactory Methods
105
106 /**
107 * <p>Create and return a new <code>BasicDataSource</code> instance. If no
108 * instance can be created, return <code>null</code> instead.</p>
109 *
110 * @param obj The possibly null object containing location or
111 * reference information that can be used in creating an object
112 * @param name The name of this object relative to <code>nameCtx</code>
113 * @param nameCtx The context relative to which the <code>name</code>
114 * parameter is specified, or <code>null</code> if <code>name</code>
115 * is relative to the default initial context
116 * @param environment The possibly null environment that is used in
117 * creating this object
118 *
119 * @exception Exception if an exception occurs creating the instance
120 */
121 public Object getObjectInstance(Object obj, Name name, Context nameCtx,
122 Hashtable environment)
123 throws Exception {
124
125 // We only know how to deal with <code>javax.naming.Reference</code>s
126 // that specify a class name of "javax.sql.DataSource"
127 if ((obj == null) || !(obj instanceof Reference)) {
128 return null;
129 }
130 Reference ref = (Reference) obj;
131 if (!"javax.sql.DataSource".equals(ref.getClassName())) {
132 return null;
133 }
134
135 Properties properties = new Properties();
136 for (int i = 0 ; i < ALL_PROPERTIES.length ; i++) {
137 String propertyName = ALL_PROPERTIES[i];
138 RefAddr ra = ref.get(propertyName);
139 if (ra != null) {
140 String propertyValue = ra.getContent().toString();
141 properties.setProperty(propertyName, propertyValue);
142 }
143 }
144
145 return createDataSource(properties);
146 }
147
148 /**
149 * Creates and configures a {@link BasicDataSource} instance based on the
150 * given properties.
151 *
152 * @param properties the datasource configuration properties
153 * @throws Exception if an error occurs creating the data source
154 */
155 public static DataSource createDataSource(Properties properties) throws Exception {
156 BasicDataSource dataSource = new BasicDataSource();
157 String value = null;
158
159 value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
160 if (value != null) {
161 dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue());
162 }
163
164 value = properties.getProperty(PROP_DEFAULTREADONLY);
165 if (value != null) {
166 dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue());
167 }
168
169 value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
170 if (value != null) {
171 int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
172 if ("NONE".equalsIgnoreCase(value)) {
173 level = Connection.TRANSACTION_NONE;
174 }
175 else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
176 level = Connection.TRANSACTION_READ_COMMITTED;
177 }
178 else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
179 level = Connection.TRANSACTION_READ_UNCOMMITTED;
180 }
181 else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
182 level = Connection.TRANSACTION_REPEATABLE_READ;
183 }
184 else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
185 level = Connection.TRANSACTION_SERIALIZABLE;
186 }
187 else {
188 try {
189 level = Integer.parseInt(value);
190 } catch (NumberFormatException e) {
191 System.err.println("Could not parse defaultTransactionIsolation: " + value);
192 System.err.println("WARNING: defaultTransactionIsolation not set");
193 System.err.println("using default value of database driver");
194 level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
195 }
196 }
197 dataSource.setDefaultTransactionIsolation(level);
198 }
199
200 value = properties.getProperty(PROP_DEFAULTCATALOG);
201 if (value != null) {
202 dataSource.setDefaultCatalog(value);
203 }
204
205 value = properties.getProperty(PROP_DRIVERCLASSNAME);
206 if (value != null) {
207 dataSource.setDriverClassName(value);
208 }
209
210 value = properties.getProperty(PROP_MAXACTIVE);
211 if (value != null) {
212 dataSource.setMaxActive(Integer.parseInt(value));
213 }
214
215 value = properties.getProperty(PROP_MAXIDLE);
216 if (value != null) {
217 dataSource.setMaxIdle(Integer.parseInt(value));
218 }
219
220 value = properties.getProperty(PROP_MINIDLE);
221 if (value != null) {
222 dataSource.setMinIdle(Integer.parseInt(value));
223 }
224
225 value = properties.getProperty(PROP_INITIALSIZE);
226 if (value != null) {
227 dataSource.setInitialSize(Integer.parseInt(value));
228 }
229
230 value = properties.getProperty(PROP_MAXWAIT);
231 if (value != null) {
232 dataSource.setMaxWait(Long.parseLong(value));
233 }
234
235 value = properties.getProperty(PROP_TESTONBORROW);
236 if (value != null) {
237 dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
238 }
239
240 value = properties.getProperty(PROP_TESTONRETURN);
241 if (value != null) {
242 dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
243 }
244
245 value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
246 if (value != null) {
247 dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
248 }
249
250 value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
251 if (value != null) {
252 dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
253 }
254
255 value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
256 if (value != null) {
257 dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
258 }
259
260 value = properties.getProperty(PROP_TESTWHILEIDLE);
261 if (value != null) {
262 dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
263 }
264
265 value = properties.getProperty(PROP_PASSWORD);
266 if (value != null) {
267 dataSource.setPassword(value);
268 }
269
270 value = properties.getProperty(PROP_URL);
271 if (value != null) {
272 dataSource.setUrl(value);
273 }
274
275 value = properties.getProperty(PROP_USERNAME);
276 if (value != null) {
277 dataSource.setUsername(value);
278 }
279
280 value = properties.getProperty(PROP_VALIDATIONQUERY);
281 if (value != null) {
282 dataSource.setValidationQuery(value);
283 }
284
285 value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
286 if (value != null) {
287 dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
288 }
289
290 value = properties.getProperty(PROP_REMOVEABANDONED);
291 if (value != null) {
292 dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue());
293 }
294
295 value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
296 if (value != null) {
297 dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
298 }
299
300 value = properties.getProperty(PROP_LOGABANDONED);
301 if (value != null) {
302 dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
303 }
304
305 value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
306 if (value != null) {
307 dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
308 }
309
310 value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
311 if (value != null) {
312 dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
313 }
314
315 value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
316 if (value != null) {
317 Properties p = getProperties(value);
318 Enumeration e = p.propertyNames();
319 while (e.hasMoreElements()) {
320 String propertyName = (String) e.nextElement();
321 dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
322 }
323 }
324
325 // Return the configured DataSource instance
326 return dataSource;
327 }
328
329 /**
330 * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p>
331 * @param propText
332 * @return Properties
333 * @throws Exception
334 */
335 static private Properties getProperties(String propText) throws Exception {
336 Properties p = new Properties();
337 if (propText != null) {
338 p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes()));
339 }
340 return p;
341 }
342 }