1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.dbcp;
  17. import java.sql.*;
  18. import java.util.NoSuchElementException;
  19. import org.apache.commons.pool.*;
  20. /**
  21. * A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
  22. * <p>
  23. * My {@link #prepareStatement} methods, rather than creating a new {@link PreparedStatement}
  24. * each time, may actually pull the {@link PreparedStatement} from a pool of unused statements.
  25. * The {@link PreparedStatement#close} method of the returned {@link PreparedStatement} doesn't
  26. * actually close the statement, but rather returns it to my pool. (See {@link PoolablePreparedStatement}.)
  27. *
  28. * @see PoolablePreparedStatement
  29. * @author Rodney Waldhoff
  30. * @author Dirk Verbeeck
  31. * @version $Revision: 1.14 $ $Date: 2004/03/07 15:26:38 $
  32. */
  33. public class PoolingConnection extends DelegatingConnection implements Connection, KeyedPoolableObjectFactory {
  34. /** My pool of {@link PreparedStatement}s. */
  35. protected KeyedObjectPool _pstmtPool = null;
  36. /**
  37. * Constructor.
  38. * @param c the underlying {@link Connection}.
  39. */
  40. public PoolingConnection(Connection c) {
  41. super(c);
  42. }
  43. /**
  44. * Constructor.
  45. * @param c the underlying {@link Connection}.
  46. * @param maxSleepingPerKey the maximum number of {@link PreparedStatement}s that may sit idle in my pool (per type)
  47. */
  48. public PoolingConnection(Connection c, KeyedObjectPool pool) {
  49. super(c);
  50. _pstmtPool = pool;
  51. }
  52. /**
  53. * Close and free all {@link PreparedStatement}s from my pool, and
  54. * close my underlying connection.
  55. */
  56. public synchronized void close() throws SQLException {
  57. if(null != _pstmtPool) {
  58. KeyedObjectPool oldpool = _pstmtPool;
  59. _pstmtPool = null;
  60. try {
  61. oldpool.close();
  62. } catch(RuntimeException e) {
  63. throw e;
  64. } catch(SQLException e) {
  65. throw e;
  66. } catch(Exception e) {
  67. throw new SQLNestedException("Cannot close connection", e);
  68. }
  69. }
  70. getInnermostDelegate().close();
  71. }
  72. /**
  73. * Create or obtain a {@link PreparedStatement} from my pool.
  74. * @return a {@link PoolablePreparedStatement}
  75. */
  76. public synchronized PreparedStatement prepareStatement(String sql) throws SQLException {
  77. try {
  78. return(PreparedStatement)(_pstmtPool.borrowObject(createKey(sql)));
  79. } catch(NoSuchElementException e) {
  80. throw new SQLNestedException("MaxOpenPreparedStatements limit reached", e);
  81. } catch(RuntimeException e) {
  82. throw e;
  83. } catch(Exception e) {
  84. throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
  85. }
  86. }
  87. /**
  88. * Create or obtain a {@link PreparedStatement} from my pool.
  89. * @return a {@link PoolablePreparedStatement}
  90. */
  91. public synchronized PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  92. try {
  93. return(PreparedStatement)(_pstmtPool.borrowObject(createKey(sql,resultSetType,resultSetConcurrency)));
  94. } catch(NoSuchElementException e) {
  95. throw new SQLNestedException("MaxOpenPreparedStatements limit reached", e);
  96. } catch(RuntimeException e) {
  97. throw e;
  98. } catch(Exception e) {
  99. throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
  100. }
  101. }
  102. // ------------------- JDBC 3.0 -----------------------------------------
  103. // Will be commented by the build process on a JDBC 2.0 system
  104. /* JDBC_3_ANT_KEY_BEGIN */
  105. // TODO: possible enhancement, cache these preparedStatements as well
  106. // public PreparedStatement prepareStatement(String sql, int resultSetType,
  107. // int resultSetConcurrency,
  108. // int resultSetHoldability)
  109. // throws SQLException {
  110. // return super.prepareStatement(
  111. // sql, resultSetType, resultSetConcurrency, resultSetHoldability);
  112. // }
  113. //
  114. // public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
  115. // throws SQLException {
  116. // return super.prepareStatement(sql, autoGeneratedKeys);
  117. // }
  118. //
  119. // public PreparedStatement prepareStatement(String sql, int columnIndexes[])
  120. // throws SQLException {
  121. // return super.prepareStatement(sql, columnIndexes);
  122. // }
  123. //
  124. // public PreparedStatement prepareStatement(String sql, String columnNames[])
  125. // throws SQLException {
  126. // return super.prepareStatement(sql, columnNames);
  127. // }
  128. /* JDBC_3_ANT_KEY_END */
  129. /**
  130. * Create a PStmtKey for the given arguments.
  131. */
  132. protected Object createKey(String sql, int resultSetType, int resultSetConcurrency) {
  133. String catalog = null;
  134. try {
  135. catalog = getCatalog();
  136. } catch (Exception e) {}
  137. return new PStmtKey(normalizeSQL(sql), catalog, resultSetType, resultSetConcurrency);
  138. }
  139. /**
  140. * Create a PStmtKey for the given arguments.
  141. */
  142. protected Object createKey(String sql) {
  143. String catalog = null;
  144. try {
  145. catalog = getCatalog();
  146. } catch (Exception e) {}
  147. return new PStmtKey(normalizeSQL(sql), catalog);
  148. }
  149. /**
  150. * Normalize the given SQL statement, producing a
  151. * cannonical form that is semantically equivalent to the original.
  152. */
  153. protected String normalizeSQL(String sql) {
  154. return sql.trim();
  155. }
  156. /**
  157. * My {@link KeyedPoolableObjectFactory} method for creating
  158. * {@link PreparedStatement}s.
  159. * @param obj the key for the {@link PreparedStatement} to be created
  160. */
  161. public Object makeObject(Object obj) throws Exception {
  162. if(null == obj || !(obj instanceof PStmtKey)) {
  163. throw new IllegalArgumentException();
  164. } else {
  165. // _openPstmts++;
  166. PStmtKey key = (PStmtKey)obj;
  167. if(null == key._resultSetType && null == key._resultSetConcurrency) {
  168. return new PoolablePreparedStatement(getDelegate().prepareStatement(key._sql),key,_pstmtPool,this);
  169. } else {
  170. return new PoolablePreparedStatement(getDelegate().prepareStatement(key._sql,key._resultSetType.intValue(),key._resultSetConcurrency.intValue()),key,_pstmtPool,this);
  171. }
  172. }
  173. }
  174. /**
  175. * My {@link KeyedPoolableObjectFactory} method for destroying
  176. * {@link PreparedStatement}s.
  177. * @param key ignored
  178. * @param obj the {@link PreparedStatement} to be destroyed.
  179. */
  180. public void destroyObject(Object key, Object obj) throws Exception {
  181. //_openPstmts--;
  182. if(obj instanceof DelegatingPreparedStatement) {
  183. ((DelegatingPreparedStatement)obj).getInnermostDelegate().close();
  184. } else {
  185. ((PreparedStatement)obj).close();
  186. }
  187. }
  188. /**
  189. * My {@link KeyedPoolableObjectFactory} method for validating
  190. * {@link PreparedStatement}s.
  191. * @param key ignored
  192. * @param obj ignored
  193. * @return <tt>true</tt>
  194. */
  195. public boolean validateObject(Object key, Object obj) {
  196. return true;
  197. }
  198. /**
  199. * My {@link KeyedPoolableObjectFactory} method for activating
  200. * {@link PreparedStatement}s. (Currently a no-op.)
  201. * @param key ignored
  202. * @param obj ignored
  203. */
  204. public void activateObject(Object key, Object obj) throws Exception {
  205. ((DelegatingPreparedStatement)obj).activate();
  206. }
  207. /**
  208. * My {@link KeyedPoolableObjectFactory} method for passivating
  209. * {@link PreparedStatement}s. Currently invokes {@link PreparedStatement#clearParameters}.
  210. * @param key ignored
  211. * @param obj a {@link PreparedStatement}
  212. */
  213. public void passivateObject(Object key, Object obj) throws Exception {
  214. ((PreparedStatement)obj).clearParameters();
  215. ((DelegatingPreparedStatement)obj).passivate();
  216. }
  217. public String toString() {
  218. return "PoolingConnection: " + _pstmtPool.toString();
  219. }
  220. /**
  221. * A key uniquely identifiying {@link PreparedStatement}s.
  222. */
  223. class PStmtKey {
  224. protected String _sql = null;
  225. protected Integer _resultSetType = null;
  226. protected Integer _resultSetConcurrency = null;
  227. protected String _catalog = null;
  228. PStmtKey(String sql) {
  229. _sql = sql;
  230. }
  231. PStmtKey(String sql, String catalog) {
  232. _sql = sql;
  233. _catalog = catalog;
  234. }
  235. PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
  236. _sql = sql;
  237. _resultSetType = new Integer(resultSetType);
  238. _resultSetConcurrency = new Integer(resultSetConcurrency);
  239. }
  240. PStmtKey(String sql, String catalog, int resultSetType, int resultSetConcurrency) {
  241. _sql = sql;
  242. _catalog = catalog;
  243. _resultSetType = new Integer(resultSetType);
  244. _resultSetConcurrency = new Integer(resultSetConcurrency);
  245. }
  246. public boolean equals(Object that) {
  247. try {
  248. PStmtKey key = (PStmtKey)that;
  249. return( ((null == _sql && null == key._sql) || _sql.equals(key._sql)) &&
  250. ((null == _catalog && null == key._catalog) || _catalog.equals(key._catalog)) &&
  251. ((null == _resultSetType && null == key._resultSetType) || _resultSetType.equals(key._resultSetType)) &&
  252. ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency.equals(key._resultSetConcurrency))
  253. );
  254. } catch(ClassCastException e) {
  255. return false;
  256. } catch(NullPointerException e) {
  257. return false;
  258. }
  259. }
  260. public int hashCode() {
  261. if (_catalog==null)
  262. return(null == _sql ? 0 : _sql.hashCode());
  263. else
  264. return(null == _sql ? _catalog.hashCode() : (_catalog + _sql).hashCode());
  265. }
  266. public String toString() {
  267. StringBuffer buf = new StringBuffer();
  268. buf.append("PStmtKey: sql=");
  269. buf.append(_sql);
  270. buf.append(", catalog=");
  271. buf.append(_catalog);
  272. buf.append(", resultSetType=");
  273. buf.append(_resultSetType);
  274. buf.append(", resultSetConcurrency=");
  275. buf.append(_resultSetConcurrency);
  276. return buf.toString();
  277. }
  278. }
  279. }