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.IOException;
021: import java.io.InputStream;
022: import java.sql.CallableStatement;
023: import java.sql.Connection;
024: import java.sql.DatabaseMetaData;
025: import java.sql.Driver;
026: import java.sql.DriverManager;
027: import java.sql.DriverPropertyInfo;
028: import java.sql.PreparedStatement;
029: import java.sql.SQLException;
030: import java.sql.SQLWarning;
031: import java.sql.Statement;
032: import java.util.HashMap;
033: import java.util.Map;
034: import java.util.NoSuchElementException;
035: import java.util.Properties;
036: import java.util.Set;
037:
038: import org.apache.commons.jocl.JOCLContentHandler;
039: import org.apache.commons.pool.ObjectPool;
040: import org.xml.sax.SAXException;
041:
042: /**
043: * A {@link Driver} implementation that obtains
044: * {@link Connection}s from a registered
045: * {@link ObjectPool}.
046: *
047: * @author Rodney Waldhoff
048: * @author Dirk Verbeeck
049: * @version $Revision: 500687 $ $Date: 2007-01-27 16:33:47 -0700 (Sat, 27 Jan 2007) $
050: */
051: public class PoolingDriver implements Driver {
052: /** Register an myself with the {@link DriverManager}. */
053: static {
054: try {
055: DriverManager.registerDriver(new PoolingDriver());
056: } catch (Exception e) {
057: }
058: }
059:
060: /** The map of registered pools. */
061: protected static HashMap _pools = new HashMap();
062:
063: /** Controls access to the underlying connection */
064: private static boolean accessToUnderlyingConnectionAllowed = false;
065:
066: public PoolingDriver() {
067: }
068:
069: /**
070: * Returns the value of the accessToUnderlyingConnectionAllowed property.
071: *
072: * @return true if access to the underlying is allowed, false otherwise.
073: */
074: public static synchronized boolean isAccessToUnderlyingConnectionAllowed() {
075: return accessToUnderlyingConnectionAllowed;
076: }
077:
078: /**
079: * Sets the value of the accessToUnderlyingConnectionAllowed property.
080: * It controls if the PoolGuard allows access to the underlying connection.
081: * (Default: false)
082: *
083: * @param allow Access to the underlying connection is granted when true.
084: */
085: public static synchronized void setAccessToUnderlyingConnectionAllowed(
086: boolean allow) {
087: accessToUnderlyingConnectionAllowed = allow;
088: }
089:
090: /**
091: * WARNING: This method throws DbcpExceptions (RuntimeExceptions)
092: * and will be replaced by the protected getConnectionPool method.
093: *
094: * @deprecated This will be removed in a future version of DBCP.
095: */
096: public synchronized ObjectPool getPool(String name) {
097: try {
098: return getConnectionPool(name);
099: } catch (Exception e) {
100: throw new DbcpException(e);
101: }
102: }
103:
104: public synchronized ObjectPool getConnectionPool(String name)
105: throws SQLException {
106: ObjectPool pool = (ObjectPool) (_pools.get(name));
107: if (null == pool) {
108: InputStream in = this .getClass().getResourceAsStream(
109: String.valueOf(name) + ".jocl");
110: if (null != in) {
111: JOCLContentHandler jocl = null;
112: try {
113: jocl = JOCLContentHandler.parse(in);
114: } catch (SAXException e) {
115: throw new SQLNestedException(
116: "Could not parse configuration file", e);
117: } catch (IOException e) {
118: throw new SQLNestedException(
119: "Could not load configuration file", e);
120: }
121: if (jocl.getType(0).equals(String.class)) {
122: pool = getPool((String) (jocl.getValue(0)));
123: if (null != pool) {
124: registerPool(name, pool);
125: }
126: } else {
127: pool = ((PoolableConnectionFactory) (jocl
128: .getValue(0))).getPool();
129: if (null != pool) {
130: registerPool(name, pool);
131: }
132: }
133: } else {
134: throw new SQLException("Configuration file not found");
135: }
136: }
137: return pool;
138: }
139:
140: public synchronized void registerPool(String name, ObjectPool pool) {
141: _pools.put(name, pool);
142: }
143:
144: public synchronized void closePool(String name) throws SQLException {
145: ObjectPool pool = (ObjectPool) _pools.get(name);
146: if (pool != null) {
147: _pools.remove(name);
148: try {
149: pool.close();
150: } catch (Exception e) {
151: throw new SQLNestedException("Error closing pool "
152: + name, e);
153: }
154: }
155: }
156:
157: public synchronized String[] getPoolNames() throws SQLException {
158: Set names = _pools.keySet();
159: return (String[]) names.toArray(new String[names.size()]);
160: }
161:
162: public boolean acceptsURL(String url) throws SQLException {
163: try {
164: return url.startsWith(URL_PREFIX);
165: } catch (NullPointerException e) {
166: return false;
167: }
168: }
169:
170: public Connection connect(String url, Properties info)
171: throws SQLException {
172: if (acceptsURL(url)) {
173: ObjectPool pool = getConnectionPool(url
174: .substring(URL_PREFIX_LEN));
175: if (null == pool) {
176: throw new SQLException("No pool found for " + url + ".");
177: } else {
178: try {
179: Connection conn = (Connection) (pool.borrowObject());
180: if (conn != null) {
181: conn = new PoolGuardConnectionWrapper(pool,
182: conn);
183: }
184: return conn;
185: } catch (SQLException e) {
186: throw e;
187: } catch (NoSuchElementException e) {
188: throw new SQLNestedException(
189: "Cannot get a connection, pool error: "
190: + e.getMessage(), e);
191: } catch (RuntimeException e) {
192: throw e;
193: } catch (Exception e) {
194: throw new SQLNestedException(
195: "Cannot get a connection, general error: "
196: + e.getMessage(), e);
197: }
198: }
199: } else {
200: return null;
201: }
202: }
203:
204: /**
205: * Invalidates the given connection.
206: *
207: * @param conn connection to invalidate
208: * @throws SQLException if the connection is not a
209: * <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating
210: * the connection
211: * @since 1.2.2
212: */
213: public void invalidateConnection(Connection conn)
214: throws SQLException {
215: if (conn instanceof PoolGuardConnectionWrapper) { // normal case
216: PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
217: ObjectPool pool = pgconn.pool;
218: Connection delegate = pgconn.delegate;
219: try {
220: pool.invalidateObject(delegate);
221: } catch (Exception e) {
222: }
223: pgconn.delegate = null;
224: } else {
225: throw new SQLException("Invalid connection class");
226: }
227: }
228:
229: public int getMajorVersion() {
230: return MAJOR_VERSION;
231: }
232:
233: public int getMinorVersion() {
234: return MINOR_VERSION;
235: }
236:
237: public boolean jdbcCompliant() {
238: return true;
239: }
240:
241: public DriverPropertyInfo[] getPropertyInfo(String url,
242: Properties info) {
243: return new DriverPropertyInfo[0];
244: }
245:
246: /** My URL prefix */
247: protected static String URL_PREFIX = "jdbc:apache:commons:dbcp:";
248: protected static int URL_PREFIX_LEN = URL_PREFIX.length();
249:
250: // version numbers
251: protected static int MAJOR_VERSION = 1;
252: protected static int MINOR_VERSION = 0;
253:
254: /**
255: * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a
256: * closed connection cannot be used anymore.
257: */
258: private class PoolGuardConnectionWrapper extends
259: DelegatingConnection {
260:
261: private ObjectPool pool;
262: private Connection delegate;
263:
264: PoolGuardConnectionWrapper(ObjectPool pool, Connection delegate) {
265: super (delegate);
266: this .pool = pool;
267: this .delegate = delegate;
268: }
269:
270: protected void checkOpen() throws SQLException {
271: if (delegate == null) {
272: throw new SQLException("Connection is closed.");
273: }
274: }
275:
276: public void close() throws SQLException {
277: checkOpen();
278: this .delegate.close();
279: this .delegate = null;
280: super .setDelegate(null);
281: }
282:
283: public boolean isClosed() throws SQLException {
284: if (delegate == null) {
285: return true;
286: }
287: return delegate.isClosed();
288: }
289:
290: public void clearWarnings() throws SQLException {
291: checkOpen();
292: delegate.clearWarnings();
293: }
294:
295: public void commit() throws SQLException {
296: checkOpen();
297: delegate.commit();
298: }
299:
300: public Statement createStatement() throws SQLException {
301: checkOpen();
302: return delegate.createStatement();
303: }
304:
305: public Statement createStatement(int resultSetType,
306: int resultSetConcurrency) throws SQLException {
307: checkOpen();
308: return delegate.createStatement(resultSetType,
309: resultSetConcurrency);
310: }
311:
312: public boolean equals(Object obj) {
313: if (delegate == null) {
314: return false;
315: }
316: return delegate.equals(obj);
317: }
318:
319: public boolean getAutoCommit() throws SQLException {
320: checkOpen();
321: return delegate.getAutoCommit();
322: }
323:
324: public String getCatalog() throws SQLException {
325: checkOpen();
326: return delegate.getCatalog();
327: }
328:
329: public DatabaseMetaData getMetaData() throws SQLException {
330: checkOpen();
331: return delegate.getMetaData();
332: }
333:
334: public int getTransactionIsolation() throws SQLException {
335: checkOpen();
336: return delegate.getTransactionIsolation();
337: }
338:
339: public Map getTypeMap() throws SQLException {
340: checkOpen();
341: return delegate.getTypeMap();
342: }
343:
344: public SQLWarning getWarnings() throws SQLException {
345: checkOpen();
346: return delegate.getWarnings();
347: }
348:
349: public int hashCode() {
350: if (delegate == null) {
351: return 0;
352: }
353: return delegate.hashCode();
354: }
355:
356: public boolean isReadOnly() throws SQLException {
357: checkOpen();
358: return delegate.isReadOnly();
359: }
360:
361: public String nativeSQL(String sql) throws SQLException {
362: checkOpen();
363: return delegate.nativeSQL(sql);
364: }
365:
366: public CallableStatement prepareCall(String sql)
367: throws SQLException {
368: checkOpen();
369: return delegate.prepareCall(sql);
370: }
371:
372: public CallableStatement prepareCall(String sql,
373: int resultSetType, int resultSetConcurrency)
374: throws SQLException {
375: checkOpen();
376: return delegate.prepareCall(sql, resultSetType,
377: resultSetConcurrency);
378: }
379:
380: public PreparedStatement prepareStatement(String sql)
381: throws SQLException {
382: checkOpen();
383: return delegate.prepareStatement(sql);
384: }
385:
386: public PreparedStatement prepareStatement(String sql,
387: int resultSetType, int resultSetConcurrency)
388: throws SQLException {
389: checkOpen();
390: return delegate.prepareStatement(sql, resultSetType,
391: resultSetConcurrency);
392: }
393:
394: public void rollback() throws SQLException {
395: checkOpen();
396: delegate.rollback();
397: }
398:
399: public void setAutoCommit(boolean autoCommit)
400: throws SQLException {
401: checkOpen();
402: delegate.setAutoCommit(autoCommit);
403: }
404:
405: public void setCatalog(String catalog) throws SQLException {
406: checkOpen();
407: delegate.setCatalog(catalog);
408: }
409:
410: public void setReadOnly(boolean readOnly) throws SQLException {
411: checkOpen();
412: delegate.setReadOnly(readOnly);
413: }
414:
415: public void setTransactionIsolation(int level)
416: throws SQLException {
417: checkOpen();
418: delegate.setTransactionIsolation(level);
419: }
420:
421: public void setTypeMap(Map map) throws SQLException {
422: checkOpen();
423: delegate.setTypeMap(map);
424: }
425:
426: public String toString() {
427: if (delegate == null) {
428: return null;
429: }
430: return delegate.toString();
431: }
432:
433: // ------------------- JDBC 3.0 -----------------------------------------
434: // Will be commented by the build process on a JDBC 2.0 system
435:
436: /* JDBC_3_ANT_KEY_BEGIN */
437:
438: public int getHoldability() throws SQLException {
439: checkOpen();
440: return delegate.getHoldability();
441: }
442:
443: public void setHoldability(int holdability) throws SQLException {
444: checkOpen();
445: delegate.setHoldability(holdability);
446: }
447:
448: public java.sql.Savepoint setSavepoint() throws SQLException {
449: checkOpen();
450: return delegate.setSavepoint();
451: }
452:
453: public java.sql.Savepoint setSavepoint(String name)
454: throws SQLException {
455: checkOpen();
456: return delegate.setSavepoint(name);
457: }
458:
459: public void releaseSavepoint(java.sql.Savepoint savepoint)
460: throws SQLException {
461: checkOpen();
462: delegate.releaseSavepoint(savepoint);
463: }
464:
465: public void rollback(java.sql.Savepoint savepoint)
466: throws SQLException {
467: checkOpen();
468: delegate.rollback(savepoint);
469: }
470:
471: public Statement createStatement(int resultSetType,
472: int resultSetConcurrency, int resultSetHoldability)
473: throws SQLException {
474: checkOpen();
475: return delegate.createStatement(resultSetType,
476: resultSetConcurrency, resultSetHoldability);
477: }
478:
479: public CallableStatement prepareCall(String sql,
480: int resultSetType, int resultSetConcurrency,
481: int resultSetHoldability) throws SQLException {
482: checkOpen();
483: return delegate.prepareCall(sql, resultSetType,
484: resultSetConcurrency, resultSetHoldability);
485: }
486:
487: public PreparedStatement prepareStatement(String sql,
488: int autoGeneratedKeys) throws SQLException {
489: checkOpen();
490: return delegate.prepareStatement(sql, autoGeneratedKeys);
491: }
492:
493: public PreparedStatement prepareStatement(String sql,
494: int resultSetType, int resultSetConcurrency,
495: int resultSetHoldability) throws SQLException {
496: checkOpen();
497: return delegate.prepareStatement(sql, resultSetType,
498: resultSetConcurrency, resultSetHoldability);
499: }
500:
501: public PreparedStatement prepareStatement(String sql,
502: int[] columnIndexes) throws SQLException {
503: checkOpen();
504: return delegate.prepareStatement(sql, columnIndexes);
505: }
506:
507: public PreparedStatement prepareStatement(String sql,
508: String[] columnNames) throws SQLException {
509: checkOpen();
510: return delegate.prepareStatement(sql, columnNames);
511: }
512:
513: /* JDBC_3_ANT_KEY_END */
514:
515: /**
516: * @see org.apache.commons.dbcp.DelegatingConnection#getDelegate()
517: */
518: public Connection getDelegate() {
519: if (isAccessToUnderlyingConnectionAllowed()) {
520: return super .getDelegate();
521: } else {
522: return null;
523: }
524: }
525:
526: /**
527: * @see org.apache.commons.dbcp.DelegatingConnection#getInnermostDelegate()
528: */
529: public Connection getInnermostDelegate() {
530: if (isAccessToUnderlyingConnectionAllowed()) {
531: return super.getInnermostDelegate();
532: } else {
533: return null;
534: }
535: }
536: }
537: }
|