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