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.cpdsadapter;
019:
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.util.Iterator;
024: import java.util.Vector;
025:
026: import javax.sql.ConnectionEvent;
027: import javax.sql.ConnectionEventListener;
028: import javax.sql.PooledConnection;
029:
030: import org.apache.commons.dbcp.DelegatingConnection;
031: import org.apache.commons.dbcp.DelegatingPreparedStatement;
032: import org.apache.commons.dbcp.SQLNestedException;
033: import org.apache.commons.pool.KeyedObjectPool;
034: import org.apache.commons.pool.KeyedPoolableObjectFactory;
035:
036: /**
037: * Implementation of PooledConnection that is returned by
038: * PooledConnectionDataSource.
039: *
040: * @author John D. McNally
041: * @version $Revision: 479137 $ $Date: 2006-11-25 08:51:48 -0700 (Sat, 25 Nov 2006) $
042: */
043: class PooledConnectionImpl implements PooledConnection,
044: KeyedPoolableObjectFactory {
045: private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
046:
047: /**
048: * The JDBC database connection that represents the physical db connection.
049: */
050: private Connection connection = null;
051:
052: /**
053: * A DelegatingConnection used to create a PoolablePreparedStatementStub
054: */
055: private DelegatingConnection delegatingConnection = null;
056:
057: /**
058: * The JDBC database logical connection.
059: */
060: private Connection logicalConnection = null;
061:
062: /**
063: * ConnectionEventListeners
064: */
065: private Vector eventListeners;
066:
067: /**
068: * flag set to true, once close() is called.
069: */
070: boolean isClosed;
071:
072: /** My pool of {*link PreparedStatement}s. */
073: protected KeyedObjectPool pstmtPool = null;
074:
075: /**
076: * Wrap the real connection.
077: */
078: PooledConnectionImpl(Connection connection, KeyedObjectPool pool) {
079: this .connection = connection;
080: if (connection instanceof DelegatingConnection) {
081: this .delegatingConnection = (DelegatingConnection) connection;
082: } else {
083: this .delegatingConnection = new DelegatingConnection(
084: connection);
085: }
086: eventListeners = new Vector();
087: isClosed = false;
088: if (pool != null) {
089: pstmtPool = pool;
090: pstmtPool.setFactory(this );
091: }
092: }
093:
094: /**
095: * Add an event listener.
096: */
097: public void addConnectionEventListener(
098: ConnectionEventListener listener) {
099: if (!eventListeners.contains(listener)) {
100: eventListeners.add(listener);
101: }
102: }
103:
104: /**
105: * Closes the physical connection and marks this
106: * <code>PooledConnection</code> so that it may not be used
107: * to generate any more logical <code>Connection</code>s.
108: *
109: * @exception SQLException if an error occurs
110: */
111: public void close() throws SQLException {
112: assertOpen();
113: isClosed = true;
114: try {
115: if (pstmtPool != null) {
116: try {
117: pstmtPool.close();
118: } finally {
119: pstmtPool = null;
120: }
121: }
122: } catch (RuntimeException e) {
123: throw e;
124: } catch (Exception e) {
125: throw new SQLNestedException(
126: "Cannot close connection (return to pool failed)",
127: e);
128: } finally {
129: try {
130: connection.close();
131: } finally {
132: connection = null;
133: }
134: }
135: }
136:
137: /**
138: * Throws an SQLException, if isClosed() is true
139: */
140: private void assertOpen() throws SQLException {
141: if (isClosed) {
142: throw new SQLException(CLOSED);
143: }
144: }
145:
146: /**
147: * Returns a JDBC connection.
148: *
149: * @return The database connection.
150: */
151: public Connection getConnection() throws SQLException {
152: assertOpen();
153: // make sure the last connection is marked as closed
154: if (logicalConnection != null && !logicalConnection.isClosed()) {
155: // should notify pool of error so the pooled connection can
156: // be removed !FIXME!
157: throw new SQLException(
158: "PooledConnection was reused, without"
159: + "its previous Connection being closed.");
160: }
161:
162: // the spec requires that this return a new Connection instance.
163: logicalConnection = new ConnectionImpl(this , connection);
164: return logicalConnection;
165: }
166:
167: /**
168: * Remove an event listener.
169: */
170: public void removeConnectionEventListener(
171: ConnectionEventListener listener) {
172: eventListeners.remove(listener);
173: }
174:
175: /**
176: * Closes the physical connection and checks that the logical connection
177: * was closed as well.
178: */
179: protected void finalize() throws Throwable {
180: // Closing the Connection ensures that if anyone tries to use it,
181: // an error will occur.
182: try {
183: connection.close();
184: } catch (Exception ignored) {
185: }
186:
187: // make sure the last connection is marked as closed
188: if (logicalConnection != null && !logicalConnection.isClosed()) {
189: throw new SQLException(
190: "PooledConnection was gc'ed, without"
191: + "its last Connection being closed.");
192: }
193: }
194:
195: /**
196: * sends a connectionClosed event.
197: */
198: void notifyListeners() {
199: ConnectionEvent event = new ConnectionEvent(this );
200: Iterator i = eventListeners.iterator();
201: while (i.hasNext()) {
202: ((ConnectionEventListener) i.next())
203: .connectionClosed(event);
204: }
205: }
206:
207: // -------------------------------------------------------------------
208: // The following code implements a PreparedStatement pool
209:
210: /**
211: * Create or obtain a {*link PreparedStatement} from my pool.
212: * @return a {*link PoolablePreparedStatement}
213: */
214: PreparedStatement prepareStatement(String sql) throws SQLException {
215: if (pstmtPool == null) {
216: return connection.prepareStatement(sql);
217: } else {
218: try {
219: return (PreparedStatement) pstmtPool
220: .borrowObject(createKey(sql));
221: } catch (RuntimeException e) {
222: throw e;
223: } catch (Exception e) {
224: throw new SQLNestedException(
225: "Borrow prepareStatement from pool failed", e);
226: }
227: }
228: }
229:
230: /**
231: * Create or obtain a {*link PreparedStatement} from my pool.
232: * @return a {*link PoolablePreparedStatement}
233: */
234: PreparedStatement prepareStatement(String sql, int resultSetType,
235: int resultSetConcurrency) throws SQLException {
236: if (pstmtPool == null) {
237: return connection.prepareStatement(sql, resultSetType,
238: resultSetConcurrency);
239: } else {
240: try {
241: return (PreparedStatement) pstmtPool
242: .borrowObject(createKey(sql, resultSetType,
243: resultSetConcurrency));
244: } catch (RuntimeException e) {
245: throw e;
246: } catch (Exception e) {
247: throw new SQLNestedException(
248: "Borrow prepareStatement from pool failed", e);
249: }
250: }
251: }
252:
253: /**
254: * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
255: */
256: protected Object createKey(String sql, int resultSetType,
257: int resultSetConcurrency) {
258: return new PStmtKey(normalizeSQL(sql), resultSetType,
259: resultSetConcurrency);
260: }
261:
262: /**
263: * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
264: */
265: protected Object createKey(String sql) {
266: return new PStmtKey(normalizeSQL(sql));
267: }
268:
269: /**
270: * Normalize the given SQL statement, producing a
271: * cannonical form that is semantically equivalent to the original.
272: */
273: protected String normalizeSQL(String sql) {
274: return sql.trim();
275: }
276:
277: /**
278: * My {*link KeyedPoolableObjectFactory} method for creating
279: * {*link PreparedStatement}s.
280: * @param obj the key for the {*link PreparedStatement} to be created
281: */
282: public Object makeObject(Object obj) throws Exception {
283: if (null == obj || !(obj instanceof PStmtKey)) {
284: throw new IllegalArgumentException();
285: } else {
286: // _openPstmts++;
287: PStmtKey key = (PStmtKey) obj;
288: if (null == key._resultSetType
289: && null == key._resultSetConcurrency) {
290: return new PoolablePreparedStatementStub(connection
291: .prepareStatement(key._sql), key, pstmtPool,
292: delegatingConnection);
293: } else {
294: return new PoolablePreparedStatementStub(connection
295: .prepareStatement(key._sql, key._resultSetType
296: .intValue(), key._resultSetConcurrency
297: .intValue()), key, pstmtPool,
298: delegatingConnection);
299: }
300: }
301: }
302:
303: /**
304: * My {*link KeyedPoolableObjectFactory} method for destroying
305: * {*link PreparedStatement}s.
306: * @param key ignored
307: * @param obj the {*link PreparedStatement} to be destroyed.
308: */
309: public void destroyObject(Object key, Object obj) throws Exception {
310: //_openPstmts--;
311: if (obj instanceof DelegatingPreparedStatement) {
312: ((DelegatingPreparedStatement) obj).getInnermostDelegate()
313: .close();
314: } else {
315: ((PreparedStatement) obj).close();
316: }
317: }
318:
319: /**
320: * My {*link KeyedPoolableObjectFactory} method for validating
321: * {*link PreparedStatement}s.
322: * @param key ignored
323: * @param obj ignored
324: * @return <tt>true</tt>
325: */
326: public boolean validateObject(Object key, Object obj) {
327: return true;
328: }
329:
330: /**
331: * My {*link KeyedPoolableObjectFactory} method for activating
332: * {*link PreparedStatement}s.
333: * @param key ignored
334: * @param obj ignored
335: */
336: public void activateObject(Object key, Object obj) throws Exception {
337: ((PoolablePreparedStatementStub) obj).activate();
338: }
339:
340: /**
341: * My {*link KeyedPoolableObjectFactory} method for passivating
342: * {*link PreparedStatement}s. Currently invokes {*link PreparedStatement#clearParameters}.
343: * @param key ignored
344: * @param obj a {*link PreparedStatement}
345: */
346: public void passivateObject(Object key, Object obj)
347: throws Exception {
348: ((PreparedStatement) obj).clearParameters();
349: ((PoolablePreparedStatementStub) obj).passivate();
350: }
351:
352: /**
353: * A key uniquely identifying {*link PreparedStatement}s.
354: */
355: class PStmtKey {
356: protected String _sql = null;
357: protected Integer _resultSetType = null;
358: protected Integer _resultSetConcurrency = null;
359:
360: PStmtKey(String sql) {
361: _sql = sql;
362: }
363:
364: PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
365: _sql = sql;
366: _resultSetType = new Integer(resultSetType);
367: _resultSetConcurrency = new Integer(resultSetConcurrency);
368: }
369:
370: public boolean equals(Object that) {
371: try {
372: PStmtKey key = (PStmtKey) that;
373: return (((null == _sql && null == key._sql) || _sql
374: .equals(key._sql))
375: && ((null == _resultSetType && null == key._resultSetType) || _resultSetType
376: .equals(key._resultSetType)) && ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency
377: .equals(key._resultSetConcurrency)));
378: } catch (ClassCastException e) {
379: return false;
380: } catch (NullPointerException e) {
381: return false;
382: }
383: }
384:
385: public int hashCode() {
386: return (null == _sql ? 0 : _sql.hashCode());
387: }
388:
389: public String toString() {
390: StringBuffer buf = new StringBuffer();
391: buf.append("PStmtKey: sql=");
392: buf.append(_sql);
393: buf.append(", resultSetType=");
394: buf.append(_resultSetType);
395: buf.append(", resultSetConcurrency=");
396: buf.append(_resultSetConcurrency);
397: return buf.toString();
398: }
399: }
400: }
|