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.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023:
024: import java.util.NoSuchElementException;
025:
026: import org.apache.commons.pool.KeyedObjectPool;
027: import org.apache.commons.pool.KeyedPoolableObjectFactory;
028:
029: /**
030: * A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
031: * <p>
032: * My {@link #prepareStatement} methods, rather than creating a new {@link PreparedStatement}
033: * each time, may actually pull the {@link PreparedStatement} from a pool of unused statements.
034: * The {@link PreparedStatement#close} method of the returned {@link PreparedStatement} doesn't
035: * actually close the statement, but rather returns it to my pool. (See {@link PoolablePreparedStatement}.)
036: *
037: * @see PoolablePreparedStatement
038: * @author Rodney Waldhoff
039: * @author Dirk Verbeeck
040: * @version $Revision: 498524 $ $Date: 2007-01-21 21:44:45 -0700 (Sun, 21 Jan 2007) $
041: */
042: public class PoolingConnection extends DelegatingConnection implements
043: Connection, KeyedPoolableObjectFactory {
044: /** My pool of {@link PreparedStatement}s. */
045: protected KeyedObjectPool _pstmtPool = null;
046:
047: /**
048: * Constructor.
049: * @param c the underlying {@link Connection}.
050: */
051: public PoolingConnection(Connection c) {
052: super (c);
053: }
054:
055: /**
056: * Constructor.
057: * @param c the underlying {@link Connection}.
058: * @param pool {@link KeyedObjectPool} of {@link PreparedStatement}s
059: */
060: public PoolingConnection(Connection c, KeyedObjectPool pool) {
061: super (c);
062: _pstmtPool = pool;
063: }
064:
065: /**
066: * Close and free all {@link PreparedStatement}s from my pool, and
067: * close my underlying connection.
068: */
069: public synchronized void close() throws SQLException {
070: if (null != _pstmtPool) {
071: KeyedObjectPool oldpool = _pstmtPool;
072: _pstmtPool = null;
073: try {
074: oldpool.close();
075: } catch (RuntimeException e) {
076: throw e;
077: } catch (SQLException e) {
078: throw e;
079: } catch (Exception e) {
080: throw new SQLNestedException("Cannot close connection",
081: e);
082: }
083: }
084: getInnermostDelegate().close();
085: }
086:
087: /**
088: * Create or obtain a {@link PreparedStatement} from my pool.
089: * @return a {@link PoolablePreparedStatement}
090: */
091: public PreparedStatement prepareStatement(String sql)
092: throws SQLException {
093: try {
094: return (PreparedStatement) (_pstmtPool
095: .borrowObject(createKey(sql)));
096: } catch (NoSuchElementException e) {
097: throw new SQLNestedException(
098: "MaxOpenPreparedStatements limit reached", e);
099: } catch (RuntimeException e) {
100: throw e;
101: } catch (Exception e) {
102: throw new SQLNestedException(
103: "Borrow prepareStatement from pool failed", e);
104: }
105: }
106:
107: /**
108: * Create or obtain a {@link PreparedStatement} from my pool.
109: * @return a {@link PoolablePreparedStatement}
110: */
111: public PreparedStatement prepareStatement(String sql,
112: int resultSetType, int resultSetConcurrency)
113: throws SQLException {
114: try {
115: return (PreparedStatement) (_pstmtPool
116: .borrowObject(createKey(sql, resultSetType,
117: resultSetConcurrency)));
118: } catch (NoSuchElementException e) {
119: throw new SQLNestedException(
120: "MaxOpenPreparedStatements limit reached", e);
121: } catch (RuntimeException e) {
122: throw e;
123: } catch (Exception e) {
124: throw new SQLNestedException(
125: "Borrow prepareStatement from pool failed", e);
126: }
127: }
128:
129: // ------------------- JDBC 3.0 -----------------------------------------
130: // Will be commented by the build process on a JDBC 2.0 system
131:
132: /* JDBC_3_ANT_KEY_BEGIN */
133:
134: // TODO: possible enhancement, cache these preparedStatements as well
135: // public PreparedStatement prepareStatement(String sql, int resultSetType,
136: // int resultSetConcurrency,
137: // int resultSetHoldability)
138: // throws SQLException {
139: // return super.prepareStatement(
140: // sql, resultSetType, resultSetConcurrency, resultSetHoldability);
141: // }
142: //
143: // public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
144: // throws SQLException {
145: // return super.prepareStatement(sql, autoGeneratedKeys);
146: // }
147: //
148: // public PreparedStatement prepareStatement(String sql, int columnIndexes[])
149: // throws SQLException {
150: // return super.prepareStatement(sql, columnIndexes);
151: // }
152: //
153: // public PreparedStatement prepareStatement(String sql, String columnNames[])
154: // throws SQLException {
155: // return super.prepareStatement(sql, columnNames);
156: // }
157: /* JDBC_3_ANT_KEY_END */
158:
159: /**
160: * Create a PStmtKey for the given arguments.
161: */
162: protected Object createKey(String sql, int resultSetType,
163: int resultSetConcurrency) {
164: String catalog = null;
165: try {
166: catalog = getCatalog();
167: } catch (Exception e) {
168: }
169: return new PStmtKey(normalizeSQL(sql), catalog, resultSetType,
170: resultSetConcurrency);
171: }
172:
173: /**
174: * Create a PStmtKey for the given arguments.
175: */
176: protected Object createKey(String sql) {
177: String catalog = null;
178: try {
179: catalog = getCatalog();
180: } catch (Exception e) {
181: }
182: return new PStmtKey(normalizeSQL(sql), catalog);
183: }
184:
185: /**
186: * Normalize the given SQL statement, producing a
187: * cannonical form that is semantically equivalent to the original.
188: */
189: protected String normalizeSQL(String sql) {
190: return sql.trim();
191: }
192:
193: /**
194: * My {@link KeyedPoolableObjectFactory} method for creating
195: * {@link PreparedStatement}s.
196: * @param obj the key for the {@link PreparedStatement} to be created
197: */
198: public Object makeObject(Object obj) throws Exception {
199: if (null == obj || !(obj instanceof PStmtKey)) {
200: throw new IllegalArgumentException();
201: } else {
202: // _openPstmts++;
203: PStmtKey key = (PStmtKey) obj;
204: if (null == key._resultSetType
205: && null == key._resultSetConcurrency) {
206: return new PoolablePreparedStatement(getDelegate()
207: .prepareStatement(key._sql), key, _pstmtPool,
208: this );
209: } else {
210: return new PoolablePreparedStatement(getDelegate()
211: .prepareStatement(key._sql,
212: key._resultSetType.intValue(),
213: key._resultSetConcurrency.intValue()),
214: key, _pstmtPool, this );
215: }
216: }
217: }
218:
219: /**
220: * My {@link KeyedPoolableObjectFactory} method for destroying
221: * {@link PreparedStatement}s.
222: * @param key ignored
223: * @param obj the {@link PreparedStatement} to be destroyed.
224: */
225: public void destroyObject(Object key, Object obj) throws Exception {
226: //_openPstmts--;
227: if (obj instanceof DelegatingPreparedStatement) {
228: ((DelegatingPreparedStatement) obj).getInnermostDelegate()
229: .close();
230: } else {
231: ((PreparedStatement) obj).close();
232: }
233: }
234:
235: /**
236: * My {@link KeyedPoolableObjectFactory} method for validating
237: * {@link PreparedStatement}s.
238: * @param key ignored
239: * @param obj ignored
240: * @return <tt>true</tt>
241: */
242: public boolean validateObject(Object key, Object obj) {
243: return true;
244: }
245:
246: /**
247: * My {@link KeyedPoolableObjectFactory} method for activating
248: * {@link PreparedStatement}s. (Currently a no-op.)
249: * @param key ignored
250: * @param obj ignored
251: */
252: public void activateObject(Object key, Object obj) throws Exception {
253: ((DelegatingPreparedStatement) obj).activate();
254: }
255:
256: /**
257: * My {@link KeyedPoolableObjectFactory} method for passivating
258: * {@link PreparedStatement}s. Currently invokes {@link PreparedStatement#clearParameters}.
259: * @param key ignored
260: * @param obj a {@link PreparedStatement}
261: */
262: public void passivateObject(Object key, Object obj)
263: throws Exception {
264: ((PreparedStatement) obj).clearParameters();
265: ((DelegatingPreparedStatement) obj).passivate();
266: }
267:
268: public String toString() {
269: return "PoolingConnection: " + _pstmtPool.toString();
270: }
271:
272: /**
273: * A key uniquely identifiying {@link PreparedStatement}s.
274: */
275: class PStmtKey {
276: protected String _sql = null;
277: protected Integer _resultSetType = null;
278: protected Integer _resultSetConcurrency = null;
279: protected String _catalog = null;
280:
281: PStmtKey(String sql) {
282: _sql = sql;
283: }
284:
285: PStmtKey(String sql, String catalog) {
286: _sql = sql;
287: _catalog = catalog;
288: }
289:
290: PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
291: _sql = sql;
292: _resultSetType = new Integer(resultSetType);
293: _resultSetConcurrency = new Integer(resultSetConcurrency);
294: }
295:
296: PStmtKey(String sql, String catalog, int resultSetType,
297: int resultSetConcurrency) {
298: _sql = sql;
299: _catalog = catalog;
300: _resultSetType = new Integer(resultSetType);
301: _resultSetConcurrency = new Integer(resultSetConcurrency);
302: }
303:
304: public boolean equals(Object that) {
305: try {
306: PStmtKey key = (PStmtKey) that;
307: return (((null == _sql && null == key._sql) || _sql
308: .equals(key._sql))
309: && ((null == _catalog && null == key._catalog) || _catalog
310: .equals(key._catalog))
311: && ((null == _resultSetType && null == key._resultSetType) || _resultSetType
312: .equals(key._resultSetType)) && ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency
313: .equals(key._resultSetConcurrency)));
314: } catch (ClassCastException e) {
315: return false;
316: } catch (NullPointerException e) {
317: return false;
318: }
319: }
320:
321: public int hashCode() {
322: if (_catalog == null)
323: return (null == _sql ? 0 : _sql.hashCode());
324: else
325: return (null == _sql ? _catalog.hashCode()
326: : (_catalog + _sql).hashCode());
327: }
328:
329: public String toString() {
330: StringBuffer buf = new StringBuffer();
331: buf.append("PStmtKey: sql=");
332: buf.append(_sql);
333: buf.append(", catalog=");
334: buf.append(_catalog);
335: buf.append(", resultSetType=");
336: buf.append(_resultSetType);
337: buf.append(", resultSetConcurrency=");
338: buf.append(_resultSetConcurrency);
339: return buf.toString();
340: }
341: }
342: }
|