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.ResultSet;
022 import java.sql.SQLException;
023 import java.sql.SQLWarning;
024 import java.sql.Statement;
025 import java.util.List;
026
027 /**
028 * A base delegating implementation of {@link Statement}.
029 * <p>
030 * All of the methods from the {@link Statement} interface
031 * simply check to see that the {@link Statement} is active,
032 * and call the corresponding method on the "delegate"
033 * provided in my constructor.
034 * <p>
035 * Extends AbandonedTrace to implement Statement tracking and
036 * logging of code which created the Statement. Tracking the
037 * Statement ensures that the Connection which created it can
038 * close any open Statement's on Connection close.
039 *
040 * @author Rodney Waldhoff
041 * @author Glenn L. Nielsen
042 * @author James House
043 * @author Dirk Verbeeck
044 * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
045 */
046 public class DelegatingStatement extends AbandonedTrace implements Statement {
047 /** My delegate. */
048 protected Statement _stmt = null;
049 /** The connection that created me. **/
050 protected DelegatingConnection _conn = null;
051
052 /**
053 * Create a wrapper for the Statement which traces this
054 * Statement to the Connection which created it and the
055 * code which created it.
056 *
057 * @param s the {@link Statement} to delegate all calls to.
058 * @param c the {@link DelegatingConnection} that created this statement.
059 */
060 public DelegatingStatement(DelegatingConnection c, Statement s) {
061 super(c);
062 _stmt = s;
063 _conn = c;
064 }
065
066 /**
067 * Returns my underlying {@link Statement}.
068 * @return my underlying {@link Statement}.
069 * @see #getInnermostDelegate
070 */
071 public Statement getDelegate() {
072 return _stmt;
073 }
074
075 /**
076 * This method considers two objects to be equal
077 * if the underlying jdbc objects are equal.
078 */
079 public boolean equals(Object obj) {
080 Statement delegate = getInnermostDelegate();
081 if (delegate == null) {
082 return false;
083 }
084 if (obj instanceof DelegatingStatement) {
085 DelegatingStatement s = (DelegatingStatement) obj;
086 return delegate.equals(s.getInnermostDelegate());
087 }
088 else {
089 return delegate.equals(obj);
090 }
091 }
092
093 public int hashCode() {
094 Object obj = getInnermostDelegate();
095 if (obj == null) {
096 return 0;
097 }
098 return obj.hashCode();
099 }
100
101 /**
102 * If my underlying {@link Statement} is not a
103 * <tt>DelegatingStatement</tt>, returns it,
104 * otherwise recursively invokes this method on
105 * my delegate.
106 * <p>
107 * Hence this method will return the first
108 * delegate that is not a <tt>DelegatingStatement</tt>
109 * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt>
110 * delegate can be found by transversing this chain.
111 * <p>
112 * This method is useful when you may have nested
113 * <tt>DelegatingStatement</tt>s, and you want to make
114 * sure to obtain a "genuine" {@link Statement}.
115 * @see #getDelegate
116 */
117 public Statement getInnermostDelegate() {
118 Statement s = _stmt;
119 while(s != null && s instanceof DelegatingStatement) {
120 s = ((DelegatingStatement)s).getDelegate();
121 if(this == s) {
122 return null;
123 }
124 }
125 return s;
126 }
127
128 /** Sets my delegate. */
129 public void setDelegate(Statement s) {
130 _stmt = s;
131 }
132
133 protected boolean _closed = false;
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 throw new AssertionError();
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 throw new AssertionError();
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); throw new AssertionError(); } }
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); throw new AssertionError(); } }
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 public boolean getMoreResults(int current) throws SQLException
310 { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
311
312 public ResultSet getGeneratedKeys() throws SQLException {
313 checkOpen();
314 try {
315 return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys());
316 } catch (SQLException e) {
317 handleException(e);
318 throw new AssertionError();
319 }
320 }
321
322 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
323 { checkOpen(); try { return _stmt.executeUpdate(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return 0; } }
324
325 public int executeUpdate(String sql, int columnIndexes[]) throws SQLException
326 { checkOpen(); try { return _stmt.executeUpdate(sql, columnIndexes); } catch (SQLException e) { handleException(e); return 0; } }
327
328 public int executeUpdate(String sql, String columnNames[]) throws SQLException
329 { checkOpen(); try { return _stmt.executeUpdate(sql, columnNames); } catch (SQLException e) { handleException(e); return 0; } }
330
331 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
332 { checkOpen(); try { return _stmt.execute(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return false; } }
333
334 public boolean execute(String sql, int columnIndexes[]) throws SQLException
335 { checkOpen(); try { return _stmt.execute(sql, columnIndexes); } catch (SQLException e) { handleException(e); return false; } }
336
337 public boolean execute(String sql, String columnNames[]) throws SQLException
338 { checkOpen(); try { return _stmt.execute(sql, columnNames); } catch (SQLException e) { handleException(e); return false; } }
339
340 public int getResultSetHoldability() throws SQLException
341 { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
342
343 /*
344 * Note was protected prior to JDBC 4
345 * TODO Consider adding build flags to make this protected unless we are
346 * using JDBC 4.
347 */
348 public boolean isClosed() throws SQLException {
349 return _closed;
350 }
351
352 /*
353
354 public boolean isWrapperFor(Class<?> iface) throws SQLException {
355 return iface.isAssignableFrom(getClass()) || _stmt.isWrapperFor(iface);
356 }
357
358 public <T> T unwrap(Class<T> iface) throws SQLException {
359 if (iface.isAssignableFrom(getClass())) {
360 return iface.cast(this);
361 } else if (iface.isAssignableFrom(_stmt.getClass())) {
362 return iface.cast(_stmt);
363 } else {
364 return _stmt.unwrap(iface);
365 }
366 }
367
368 public void setPoolable(boolean poolable) throws SQLException {
369 checkOpen();
370 try {
371 _stmt.setPoolable(poolable);
372 }
373 catch (SQLException e) {
374 handleException(e);
375 }
376 }
377
378 public boolean isPoolable() throws SQLException {
379 checkOpen();
380 try {
381 return _stmt.isPoolable();
382 }
383 catch (SQLException e) {
384 handleException(e);
385 return false;
386 }
387 }
388 */
389 }