1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.dbcp;
19
20 import java.sql.Connection;
21 import java.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.sql.SQLWarning;
24 import java.sql.Statement;
25 import java.util.List;
26
27 /**
28 * A base delegating implementation of {@link Statement}.
29 * <p>
30 * All of the methods from the {@link Statement} interface
31 * simply check to see that the {@link Statement} is active,
32 * and call the corresponding method on the "delegate"
33 * provided in my constructor.
34 * <p>
35 * Extends AbandonedTrace to implement Statement tracking and
36 * logging of code which created the Statement. Tracking the
37 * Statement ensures that the Connection which created it can
38 * close any open Statement's on Connection close.
39 *
40 * @author Rodney Waldhoff
41 * @author Glenn L. Nielsen
42 * @author James House
43 * @author Dirk Verbeeck
44 * @version $Revision: 500687 $ $Date: 2007-01-27 16:33:47 -0700 (Sat, 27 Jan 2007) $
45 */
46 public class DelegatingStatement extends AbandonedTrace implements Statement {
47 /** My delegate. */
48 protected Statement _stmt = null;
49 /** The connection that created me. **/
50 protected DelegatingConnection _conn = null;
51
52 /**
53 * Create a wrapper for the Statement which traces this
54 * Statement to the Connection which created it and the
55 * code which created it.
56 *
57 * @param s the {@link Statement} to delegate all calls to.
58 * @param c the {@link DelegatingConnection} that created this statement.
59 */
60 public DelegatingStatement(DelegatingConnection c, Statement s) {
61 super(c);
62 _stmt = s;
63 _conn = c;
64 }
65
66 /**
67 * Returns my underlying {@link Statement}.
68 * @return my underlying {@link Statement}.
69 * @see #getInnermostDelegate
70 */
71 public Statement getDelegate() {
72 return _stmt;
73 }
74
75 public boolean equals(Object obj) {
76 Statement delegate = getInnermostDelegate();
77 if (delegate == null) {
78 return false;
79 }
80 if (obj instanceof DelegatingStatement) {
81 DelegatingStatement s = (DelegatingStatement) obj;
82 return delegate.equals(s.getInnermostDelegate());
83 }
84 else {
85 return delegate.equals(obj);
86 }
87 }
88
89 public int hashCode() {
90 Object obj = getInnermostDelegate();
91 if (obj == null) {
92 return 0;
93 }
94 return obj.hashCode();
95 }
96
97 /**
98 * If my underlying {@link Statement} is not a
99 * <tt>DelegatingStatement</tt>, returns it,
100 * otherwise recursively invokes this method on
101 * my delegate.
102 * <p>
103 * Hence this method will return the first
104 * delegate that is not a <tt>DelegatingStatement</tt>
105 * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt>
106 * delegate can be found by transversing this chain.
107 * <p>
108 * This method is useful when you may have nested
109 * <tt>DelegatingStatement</tt>s, and you want to make
110 * sure to obtain a "genuine" {@link Statement}.
111 * @see #getDelegate
112 */
113 public Statement getInnermostDelegate() {
114 Statement s = _stmt;
115 while(s != null && s instanceof DelegatingStatement) {
116 s = ((DelegatingStatement)s).getDelegate();
117 if(this == s) {
118 return null;
119 }
120 }
121 return s;
122 }
123
124 /** Sets my delegate. */
125 public void setDelegate(Statement s) {
126 _stmt = s;
127 }
128
129 protected boolean _closed = false;
130
131 protected boolean isClosed() {
132 return _closed;
133 }
134
135 protected void checkOpen() throws SQLException {
136 if(isClosed()) {
137 throw new SQLException
138 (this.getClass().getName() + " with address: \"" +
139 this.toString() + "\" is closed.");
140 }
141 }
142
143 /**
144 * Close this DelegatingStatement, and close
145 * any ResultSets that were not explicitly closed.
146 */
147 public void close() throws SQLException {
148 try {
149 try {
150 if (_conn != null) {
151 _conn.removeTrace(this);
152 _conn = null;
153 }
154
155 // The JDBC spec requires that a statment close any open
156 // ResultSet's when it is closed.
157 // FIXME The PreparedStatement we're wrapping should handle this for us.
158 // See bug 17301 for what could happen when ResultSets are closed twice.
159 List resultSets = getTrace();
160 if( resultSets != null) {
161 ResultSet[] set = (ResultSet[]) resultSets.toArray(new ResultSet[resultSets.size()]);
162 for (int i = 0; i < set.length; i++) {
163 set[i].close();
164 }
165 clearTrace();
166 }
167
168 _stmt.close();
169 }
170 catch (SQLException e) {
171 handleException(e);
172 }
173 }
174 finally {
175 _closed = true;
176 }
177 }
178
179 protected void handleException(SQLException e) throws SQLException {
180 if (_conn != null) {
181 _conn.handleException(e);
182 }
183 else {
184 throw e;
185 }
186 }
187
188 protected void activate() throws SQLException {
189 if(_stmt instanceof DelegatingStatement) {
190 ((DelegatingStatement)_stmt).activate();
191 }
192 }
193
194 protected void passivate() throws SQLException {
195 if(_stmt instanceof DelegatingStatement) {
196 ((DelegatingStatement)_stmt).passivate();
197 }
198 }
199
200 public Connection getConnection() throws SQLException {
201 checkOpen();
202 return _conn; // return the delegating connection that created this
203 }
204
205 public ResultSet executeQuery(String sql) throws SQLException {
206 checkOpen();
207 try {
208 return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql));
209 }
210 catch (SQLException e) {
211 handleException(e);
212 return null;
213 }
214 }
215
216 public ResultSet getResultSet() throws SQLException {
217 checkOpen();
218 try {
219 return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet());
220 }
221 catch (SQLException e) {
222 handleException(e);
223 return null;
224 }
225 }
226
227 public int executeUpdate(String sql) throws SQLException
228 { checkOpen(); try { return _stmt.executeUpdate(sql); } catch (SQLException e) { handleException(e); return 0; } }
229
230 public int getMaxFieldSize() throws SQLException
231 { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } }
232
233 public void setMaxFieldSize(int max) throws SQLException
234 { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } }
235
236 public int getMaxRows() throws SQLException
237 { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } }
238
239 public void setMaxRows(int max) throws SQLException
240 { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } }
241
242 public void setEscapeProcessing(boolean enable) throws SQLException
243 { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } }
244
245 public int getQueryTimeout() throws SQLException
246 { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } }
247
248 public void setQueryTimeout(int seconds) throws SQLException
249 { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } }
250
251 public void cancel() throws SQLException
252 { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } }
253
254 public SQLWarning getWarnings() throws SQLException
255 { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); return null; } }
256
257 public void clearWarnings() throws SQLException
258 { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } }
259
260 public void setCursorName(String name) throws SQLException
261 { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } }
262
263 public boolean execute(String sql) throws SQLException
264 { checkOpen(); try { return _stmt.execute(sql); } catch (SQLException e) { handleException(e); return false; } }
265
266 public int getUpdateCount() throws SQLException
267 { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } }
268
269 public boolean getMoreResults() throws SQLException
270 { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } }
271
272 public void setFetchDirection(int direction) throws SQLException
273 { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } }
274
275 public int getFetchDirection() throws SQLException
276 { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } }
277
278 public void setFetchSize(int rows) throws SQLException
279 { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } }
280
281 public int getFetchSize() throws SQLException
282 { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } }
283
284 public int getResultSetConcurrency() throws SQLException
285 { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } }
286
287 public int getResultSetType() throws SQLException
288 { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } }
289
290 public void addBatch(String sql) throws SQLException
291 { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } }
292
293 public void clearBatch() throws SQLException
294 { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } }
295
296 public int[] executeBatch() throws SQLException
297 { checkOpen(); try { return _stmt.executeBatch(); } catch (SQLException e) { handleException(e); return null; } }
298
299 /**
300 * Returns a String representation of this object.
301 *
302 * @return String
303 * @since 1.2.2
304 */
305 public String toString() {
306 return _stmt.toString();
307 }
308
309 // ------------------- JDBC 3.0 -----------------------------------------
310 // Will be commented by the build process on a JDBC 2.0 system
311
312 /* JDBC_3_ANT_KEY_BEGIN */
313
314 public boolean getMoreResults(int current) throws SQLException
315 { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
316
317 public ResultSet getGeneratedKeys() throws SQLException
318 { checkOpen(); try { return _stmt.getGeneratedKeys(); } catch (SQLException e) { handleException(e); return null; } }
319
320 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
321 { checkOpen(); try { return _stmt.executeUpdate(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return 0; } }
322
323 public int executeUpdate(String sql, int columnIndexes[]) throws SQLException
324 { checkOpen(); try { return _stmt.executeUpdate(sql, columnIndexes); } catch (SQLException e) { handleException(e); return 0; } }
325
326 public int executeUpdate(String sql, String columnNames[]) throws SQLException
327 { checkOpen(); try { return _stmt.executeUpdate(sql, columnNames); } catch (SQLException e) { handleException(e); return 0; } }
328
329 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
330 { checkOpen(); try { return _stmt.execute(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return false; } }
331
332 public boolean execute(String sql, int columnIndexes[]) throws SQLException
333 { checkOpen(); try { return _stmt.execute(sql, columnIndexes); } catch (SQLException e) { handleException(e); return false; } }
334
335 public boolean execute(String sql, String columnNames[]) throws SQLException
336 { checkOpen(); try { return _stmt.execute(sql, columnNames); } catch (SQLException e) { handleException(e); return false; } }
337
338 public int getResultSetHoldability() throws SQLException
339 { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
340
341 /* JDBC_3_ANT_KEY_END */
342 }