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.cpdsadapter;
  17. import java.sql.Connection;
  18. import java.sql.PreparedStatement;
  19. import java.sql.SQLException;
  20. import java.util.Iterator;
  21. import java.util.Vector;
  22. import javax.sql.ConnectionEvent;
  23. import javax.sql.ConnectionEventListener;
  24. import javax.sql.PooledConnection;
  25. import org.apache.commons.dbcp.DelegatingConnection;
  26. import org.apache.commons.dbcp.DelegatingPreparedStatement;
  27. import org.apache.commons.dbcp.SQLNestedException;
  28. import org.apache.commons.pool.KeyedObjectPool;
  29. import org.apache.commons.pool.KeyedPoolableObjectFactory;
  30. /**
  31. * Implementation of PooledConnection that is returned by
  32. * PooledConnectionDataSource.
  33. *
  34. * @author John D. McNally
  35. * @version $Revision: 1.16 $ $Date: 2004/03/07 11:19:25 $
  36. */
  37. class PooledConnectionImpl
  38. implements PooledConnection, KeyedPoolableObjectFactory {
  39. private static final String CLOSED
  40. = "Attempted to use PooledConnection after closed() was called.";
  41. /**
  42. * The JDBC database connection that represents the physical db connection.
  43. */
  44. private Connection connection = null;
  45. /**
  46. * A DelegatingConnection used to create a PoolablePreparedStatementStub
  47. */
  48. private DelegatingConnection delegatingConnection = null;
  49. /**
  50. * The JDBC database logical connection.
  51. */
  52. private Connection logicalConnection = null;
  53. /**
  54. * ConnectionEventListeners
  55. */
  56. private Vector eventListeners;
  57. /**
  58. * flag set to true, once close() is called.
  59. */
  60. boolean isClosed;
  61. /** My pool of {*link PreparedStatement}s. */
  62. protected KeyedObjectPool pstmtPool = null;
  63. /**
  64. * Wrap the real connection.
  65. */
  66. PooledConnectionImpl(Connection connection, KeyedObjectPool pool) {
  67. this.connection = connection;
  68. if (connection instanceof DelegatingConnection) {
  69. this.delegatingConnection = (DelegatingConnection) connection;
  70. } else {
  71. this.delegatingConnection = new DelegatingConnection(connection);
  72. }
  73. eventListeners = new Vector();
  74. isClosed = false;
  75. if (pool != null) {
  76. pstmtPool = pool;
  77. pstmtPool.setFactory(this);
  78. }
  79. }
  80. /**
  81. * Add an event listener.
  82. */
  83. public void addConnectionEventListener(ConnectionEventListener listener) {
  84. if (!eventListeners.contains(listener)) {
  85. eventListeners.add(listener);
  86. }
  87. }
  88. /**
  89. * Closes the physical connection and marks this
  90. * <code>PooledConnection</code> so that it may not be used
  91. * to generate any more logical <code>Connection</code>s.
  92. *
  93. * @exception SQLException if an error occurs
  94. */
  95. public void close() throws SQLException {
  96. assertOpen();
  97. isClosed = true;
  98. try {
  99. if (pstmtPool != null) {
  100. try {
  101. pstmtPool.close();
  102. } finally {
  103. pstmtPool = null;
  104. }
  105. }
  106. } catch (RuntimeException e) {
  107. throw e;
  108. } catch (Exception e) {
  109. throw new SQLNestedException("Cannot close connection (return to pool failed)", e);
  110. } finally {
  111. try {
  112. connection.close();
  113. } finally {
  114. connection = null;
  115. }
  116. }
  117. }
  118. /**
  119. * Throws an SQLException, if isClosed() is true
  120. */
  121. private void assertOpen() throws SQLException {
  122. if (isClosed) {
  123. throw new SQLException(CLOSED);
  124. }
  125. }
  126. /**
  127. * Returns a JDBC connection.
  128. *
  129. * @return The database connection.
  130. */
  131. public Connection getConnection() throws SQLException {
  132. assertOpen();
  133. // make sure the last connection is marked as closed
  134. if (logicalConnection != null && !logicalConnection.isClosed()) {
  135. // should notify pool of error so the pooled connection can
  136. // be removed !FIXME!
  137. throw new SQLException("PooledConnection was reused, without"
  138. + "its previous Connection being closed.");
  139. }
  140. // the spec requires that this return a new Connection instance.
  141. logicalConnection = new ConnectionImpl(this, connection);
  142. return logicalConnection;
  143. }
  144. /**
  145. * Remove an event listener.
  146. */
  147. public void removeConnectionEventListener(
  148. ConnectionEventListener listener) {
  149. eventListeners.remove(listener);
  150. }
  151. /**
  152. * Closes the physical connection and checks that the logical connection
  153. * was closed as well.
  154. */
  155. protected void finalize() throws Throwable {
  156. // Closing the Connection ensures that if anyone tries to use it,
  157. // an error will occur.
  158. try {
  159. connection.close();
  160. } catch (Exception ignored) {
  161. }
  162. // make sure the last connection is marked as closed
  163. if (logicalConnection != null && !logicalConnection.isClosed()) {
  164. throw new SQLException("PooledConnection was gc'ed, without"
  165. + "its last Connection being closed.");
  166. }
  167. }
  168. /**
  169. * sends a connectionClosed event.
  170. */
  171. void notifyListeners() {
  172. ConnectionEvent event = new ConnectionEvent(this);
  173. Iterator i = eventListeners.iterator();
  174. while (i.hasNext()) {
  175. ((ConnectionEventListener) i.next()).connectionClosed(event);
  176. }
  177. }
  178. // -------------------------------------------------------------------
  179. // The following code implements a PreparedStatement pool
  180. /**
  181. * Create or obtain a {*link PreparedStatement} from my pool.
  182. * @return a {*link PoolablePreparedStatement}
  183. */
  184. PreparedStatement prepareStatement(String sql) throws SQLException {
  185. if (pstmtPool == null) {
  186. return connection.prepareStatement(sql);
  187. } else {
  188. try {
  189. return (PreparedStatement)
  190. pstmtPool.borrowObject(createKey(sql));
  191. } catch (RuntimeException e) {
  192. throw e;
  193. } catch (Exception e) {
  194. throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
  195. }
  196. }
  197. }
  198. /**
  199. * Create or obtain a {*link PreparedStatement} from my pool.
  200. * @return a {*link PoolablePreparedStatement}
  201. */
  202. PreparedStatement prepareStatement(String sql, int resultSetType,
  203. int resultSetConcurrency)
  204. throws SQLException {
  205. if (pstmtPool == null) {
  206. return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
  207. } else {
  208. try {
  209. return (PreparedStatement) pstmtPool.borrowObject(
  210. createKey(sql,resultSetType,resultSetConcurrency));
  211. } catch (RuntimeException e) {
  212. throw e;
  213. } catch (Exception e) {
  214. throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
  215. }
  216. }
  217. }
  218. /**
  219. * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
  220. */
  221. protected Object createKey(String sql, int resultSetType,
  222. int resultSetConcurrency) {
  223. return new PStmtKey(normalizeSQL(sql), resultSetType,
  224. resultSetConcurrency);
  225. }
  226. /**
  227. * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
  228. */
  229. protected Object createKey(String sql) {
  230. return new PStmtKey(normalizeSQL(sql));
  231. }
  232. /**
  233. * Normalize the given SQL statement, producing a
  234. * cannonical form that is semantically equivalent to the original.
  235. */
  236. protected String normalizeSQL(String sql) {
  237. return sql.trim();
  238. }
  239. /**
  240. * My {*link KeyedPoolableObjectFactory} method for creating
  241. * {*link PreparedStatement}s.
  242. * @param obj the key for the {*link PreparedStatement} to be created
  243. */
  244. public Object makeObject(Object obj) throws Exception {
  245. if (null == obj || !(obj instanceof PStmtKey)) {
  246. throw new IllegalArgumentException();
  247. } else {
  248. // _openPstmts++;
  249. PStmtKey key = (PStmtKey)obj;
  250. if (null == key._resultSetType
  251. && null == key._resultSetConcurrency) {
  252. return new PoolablePreparedStatementStub(
  253. connection.prepareStatement(key._sql),
  254. key, pstmtPool, delegatingConnection);
  255. } else {
  256. return new PoolablePreparedStatementStub(
  257. connection.prepareStatement(key._sql,
  258. key._resultSetType.intValue(),
  259. key._resultSetConcurrency.intValue()),
  260. key, pstmtPool, delegatingConnection);
  261. }
  262. }
  263. }
  264. /**
  265. * My {*link KeyedPoolableObjectFactory} method for destroying
  266. * {*link PreparedStatement}s.
  267. * @param key ignored
  268. * @param obj the {*link PreparedStatement} to be destroyed.
  269. */
  270. public void destroyObject(Object key, Object obj) throws Exception {
  271. //_openPstmts--;
  272. if (obj instanceof DelegatingPreparedStatement) {
  273. ((DelegatingPreparedStatement) obj).getInnermostDelegate().close();
  274. } else {
  275. ((PreparedStatement) obj).close();
  276. }
  277. }
  278. /**
  279. * My {*link KeyedPoolableObjectFactory} method for validating
  280. * {*link PreparedStatement}s.
  281. * @param key ignored
  282. * @param obj ignored
  283. * @return <tt>true</tt>
  284. */
  285. public boolean validateObject(Object key, Object obj) {
  286. return true;
  287. }
  288. /**
  289. * My {*link KeyedPoolableObjectFactory} method for activating
  290. * {*link PreparedStatement}s.
  291. * @param key ignored
  292. * @param obj ignored
  293. */
  294. public void activateObject(Object key, Object obj) throws Exception {
  295. ((PoolablePreparedStatementStub) obj).activate();
  296. }
  297. /**
  298. * My {*link KeyedPoolableObjectFactory} method for passivating
  299. * {*link PreparedStatement}s. Currently invokes {*link PreparedStatement#clearParameters}.
  300. * @param key ignored
  301. * @param obj a {*link PreparedStatement}
  302. */
  303. public void passivateObject(Object key, Object obj) throws Exception {
  304. ((PreparedStatement) obj).clearParameters();
  305. ((PoolablePreparedStatementStub) obj).passivate();
  306. }
  307. /**
  308. * A key uniquely identifying {*link PreparedStatement}s.
  309. */
  310. class PStmtKey {
  311. protected String _sql = null;
  312. protected Integer _resultSetType = null;
  313. protected Integer _resultSetConcurrency = null;
  314. PStmtKey(String sql) {
  315. _sql = sql;
  316. }
  317. PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
  318. _sql = sql;
  319. _resultSetType = new Integer(resultSetType);
  320. _resultSetConcurrency = new Integer(resultSetConcurrency);
  321. }
  322. public boolean equals(Object that) {
  323. try {
  324. PStmtKey key = (PStmtKey) that;
  325. return(((null == _sql && null == key._sql) || _sql.equals(key._sql)) &&
  326. ((null == _resultSetType && null == key._resultSetType) || _resultSetType.equals(key._resultSetType)) &&
  327. ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency.equals(key._resultSetConcurrency))
  328. );
  329. } catch (ClassCastException e) {
  330. return false;
  331. } catch (NullPointerException e) {
  332. return false;
  333. }
  334. }
  335. public int hashCode() {
  336. return(null == _sql ? 0 : _sql.hashCode());
  337. }
  338. public String toString() {
  339. StringBuffer buf = new StringBuffer();
  340. buf.append("PStmtKey: sql=");
  341. buf.append(_sql);
  342. buf.append(", resultSetType=");
  343. buf.append(_resultSetType);
  344. buf.append(", resultSetConcurrency=");
  345. buf.append(_resultSetConcurrency);
  346. return buf.toString();
  347. }
  348. }
  349. }