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.Connection;
  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.sql.SQLWarning;
  21. import java.sql.Statement;
  22. import java.util.List;
  23. /**
  24. * A base delegating implementation of {@link Statement}.
  25. * <p>
  26. * All of the methods from the {@link Statement} interface
  27. * simply check to see that the {@link Statement} is active,
  28. * and call the corresponding method on the "delegate"
  29. * provided in my constructor.
  30. * <p>
  31. * Extends AbandonedTrace to implement Statement tracking and
  32. * logging of code which created the Statement. Tracking the
  33. * Statement ensures that the Connection which created it can
  34. * close any open Statement's on Connection close.
  35. *
  36. * @author Rodney Waldhoff
  37. * @author Glenn L. Nielsen
  38. * @author James House
  39. * @author Dirk Verbeeck
  40. * @version $Revision: 1.17 $ $Date: 2004/03/06 13:35:31 $
  41. */
  42. public class DelegatingStatement extends AbandonedTrace implements Statement {
  43. /** My delegate. */
  44. protected Statement _stmt = null;
  45. /** The connection that created me. **/
  46. protected DelegatingConnection _conn = null;
  47. /**
  48. * Create a wrapper for the Statement which traces this
  49. * Statement to the Connection which created it and the
  50. * code which created it.
  51. *
  52. * @param s the {@link Statement} to delegate all calls to.
  53. * @param c the {@link DelegatingConnection} that created this statement.
  54. */
  55. public DelegatingStatement(DelegatingConnection c, Statement s) {
  56. super(c);
  57. _stmt = s;
  58. _conn = c;
  59. }
  60. /**
  61. * Returns my underlying {@link Statement}.
  62. * @return my underlying {@link Statement}.
  63. * @see #getInnermostDelegate
  64. */
  65. public Statement getDelegate() {
  66. return _stmt;
  67. }
  68. public boolean equals(Object obj) {
  69. Statement delegate = getInnermostDelegate();
  70. if (delegate == null) {
  71. return false;
  72. }
  73. if (obj instanceof DelegatingStatement) {
  74. DelegatingStatement s = (DelegatingStatement) obj;
  75. return delegate.equals(s.getInnermostDelegate());
  76. }
  77. else {
  78. return delegate.equals(obj);
  79. }
  80. }
  81. public int hashCode() {
  82. Object obj = getInnermostDelegate();
  83. if (obj == null) {
  84. return 0;
  85. }
  86. return obj.hashCode();
  87. }
  88. /**
  89. * If my underlying {@link Statement} is not a
  90. * <tt>DelegatingStatement</tt>, returns it,
  91. * otherwise recursively invokes this method on
  92. * my delegate.
  93. * <p>
  94. * Hence this method will return the first
  95. * delegate that is not a <tt>DelegatingStatement</tt>
  96. * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt>
  97. * delegate can be found by transversing this chain.
  98. * <p>
  99. * This method is useful when you may have nested
  100. * <tt>DelegatingStatement</tt>s, and you want to make
  101. * sure to obtain a "genuine" {@link Statement}.
  102. * @see #getDelegate
  103. */
  104. public Statement getInnermostDelegate() {
  105. Statement s = _stmt;
  106. while(s != null && s instanceof DelegatingStatement) {
  107. s = ((DelegatingStatement)s).getDelegate();
  108. if(this == s) {
  109. return null;
  110. }
  111. }
  112. return s;
  113. }
  114. /** Sets my delegate. */
  115. public void setDelegate(Statement s) {
  116. _stmt = s;
  117. }
  118. protected boolean _closed = false;
  119. protected boolean isClosed() {
  120. return _closed;
  121. }
  122. protected void checkOpen() throws SQLException {
  123. if(isClosed()) {
  124. throw new SQLException(this.getClass().getName() + " is closed.");
  125. }
  126. }
  127. /**
  128. * Close this DelegatingStatement, and close
  129. * any ResultSets that were not explicitly closed.
  130. */
  131. public void close() throws SQLException {
  132. try {
  133. try {
  134. if (_conn != null) {
  135. _conn.removeTrace(this);
  136. _conn = null;
  137. }
  138. // The JDBC spec requires that a statment close any open
  139. // ResultSet's when it is closed.
  140. // FIXME The PreparedStatement we're wrapping should handle this for us.
  141. // See bug 17301 for what could happen when ResultSets are closed twice.
  142. List resultSets = getTrace();
  143. if( resultSets != null) {
  144. ResultSet[] set = (ResultSet[]) resultSets.toArray(new ResultSet[resultSets.size()]);
  145. for (int i = 0; i < set.length; i++) {
  146. set[i].close();
  147. }
  148. clearTrace();
  149. }
  150. _stmt.close();
  151. }
  152. catch (SQLException e) {
  153. handleException(e);
  154. }
  155. }
  156. finally {
  157. _closed = true;
  158. }
  159. }
  160. protected void handleException(SQLException e) throws SQLException {
  161. if (_conn != null) {
  162. _conn.handleException(e);
  163. }
  164. else {
  165. throw e;
  166. }
  167. }
  168. protected void activate() throws SQLException {
  169. if(_stmt instanceof DelegatingStatement) {
  170. ((DelegatingStatement)_stmt).activate();
  171. }
  172. }
  173. protected void passivate() throws SQLException {
  174. if(_stmt instanceof DelegatingStatement) {
  175. ((DelegatingStatement)_stmt).passivate();
  176. }
  177. }
  178. public Connection getConnection() throws SQLException {
  179. checkOpen();
  180. return _conn; // return the delegating connection that created this
  181. }
  182. public ResultSet executeQuery(String sql) throws SQLException {
  183. checkOpen();
  184. try {
  185. return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql));
  186. }
  187. catch (SQLException e) {
  188. handleException(e);
  189. return null;
  190. }
  191. }
  192. public ResultSet getResultSet() throws SQLException {
  193. checkOpen();
  194. try {
  195. return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet());
  196. }
  197. catch (SQLException e) {
  198. handleException(e);
  199. return null;
  200. }
  201. }
  202. public int executeUpdate(String sql) throws SQLException
  203. { checkOpen(); try { return _stmt.executeUpdate(sql); } catch (SQLException e) { handleException(e); return 0; } }
  204. public int getMaxFieldSize() throws SQLException
  205. { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } }
  206. public void setMaxFieldSize(int max) throws SQLException
  207. { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } }
  208. public int getMaxRows() throws SQLException
  209. { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } }
  210. public void setMaxRows(int max) throws SQLException
  211. { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } }
  212. public void setEscapeProcessing(boolean enable) throws SQLException
  213. { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } }
  214. public int getQueryTimeout() throws SQLException
  215. { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } }
  216. public void setQueryTimeout(int seconds) throws SQLException
  217. { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } }
  218. public void cancel() throws SQLException
  219. { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } }
  220. public SQLWarning getWarnings() throws SQLException
  221. { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); return null; } }
  222. public void clearWarnings() throws SQLException
  223. { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } }
  224. public void setCursorName(String name) throws SQLException
  225. { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } }
  226. public boolean execute(String sql) throws SQLException
  227. { checkOpen(); try { return _stmt.execute(sql); } catch (SQLException e) { handleException(e); return false; } }
  228. public int getUpdateCount() throws SQLException
  229. { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } }
  230. public boolean getMoreResults() throws SQLException
  231. { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } }
  232. public void setFetchDirection(int direction) throws SQLException
  233. { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } }
  234. public int getFetchDirection() throws SQLException
  235. { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } }
  236. public void setFetchSize(int rows) throws SQLException
  237. { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } }
  238. public int getFetchSize() throws SQLException
  239. { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } }
  240. public int getResultSetConcurrency() throws SQLException
  241. { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } }
  242. public int getResultSetType() throws SQLException
  243. { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } }
  244. public void addBatch(String sql) throws SQLException
  245. { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } }
  246. public void clearBatch() throws SQLException
  247. { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } }
  248. public int[] executeBatch() throws SQLException
  249. { checkOpen(); try { return _stmt.executeBatch(); } catch (SQLException e) { handleException(e); return null; } }
  250. // ------------------- JDBC 3.0 -----------------------------------------
  251. // Will be commented by the build process on a JDBC 2.0 system
  252. /* JDBC_3_ANT_KEY_BEGIN */
  253. public boolean getMoreResults(int current) throws SQLException
  254. { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
  255. public ResultSet getGeneratedKeys() throws SQLException
  256. { checkOpen(); try { return _stmt.getGeneratedKeys(); } catch (SQLException e) { handleException(e); return null; } }
  257. public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
  258. { checkOpen(); try { return _stmt.executeUpdate(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return 0; } }
  259. public int executeUpdate(String sql, int columnIndexes[]) throws SQLException
  260. { checkOpen(); try { return _stmt.executeUpdate(sql, columnIndexes); } catch (SQLException e) { handleException(e); return 0; } }
  261. public int executeUpdate(String sql, String columnNames[]) throws SQLException
  262. { checkOpen(); try { return _stmt.executeUpdate(sql, columnNames); } catch (SQLException e) { handleException(e); return 0; } }
  263. public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
  264. { checkOpen(); try { return _stmt.execute(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return false; } }
  265. public boolean execute(String sql, int columnIndexes[]) throws SQLException
  266. { checkOpen(); try { return _stmt.execute(sql, columnIndexes); } catch (SQLException e) { handleException(e); return false; } }
  267. public boolean execute(String sql, String columnNames[]) throws SQLException
  268. { checkOpen(); try { return _stmt.execute(sql, columnNames); } catch (SQLException e) { handleException(e); return false; } }
  269. public int getResultSetHoldability() throws SQLException
  270. { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
  271. /* JDBC_3_ANT_KEY_END */
  272. }