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 */ 017package org.apache.commons.dbcp2; 018 019import java.sql.Connection; 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.sql.SQLWarning; 023import java.sql.Statement; 024import java.util.List; 025 026/** 027 * A base delegating implementation of {@link Statement}. 028 * <p> 029 * All of the methods from the {@link Statement} interface 030 * simply check to see that the {@link Statement} is active, 031 * and call the corresponding method on the "delegate" 032 * provided in my constructor. 033 * <p> 034 * Extends AbandonedTrace to implement Statement tracking and 035 * logging of code which created the Statement. Tracking the 036 * Statement ensures that the Connection which created it can 037 * close any open Statement's on Connection close. 038 * 039 * @author Rodney Waldhoff 040 * @author Glenn L. Nielsen 041 * @author James House 042 * @author Dirk Verbeeck 043 * @since 2.0 044 */ 045public class DelegatingStatement extends AbandonedTrace implements Statement { 046 /** My delegate. */ 047 private Statement _stmt = null; 048 /** The connection that created me. **/ 049 private DelegatingConnection<?> _conn = null; 050 051 /** 052 * Create a wrapper for the Statement which traces this 053 * Statement to the Connection which created it and the 054 * code which created it. 055 * 056 * @param s the {@link Statement} to delegate all calls to. 057 * @param c the {@link DelegatingConnection} that created this statement. 058 */ 059 public DelegatingStatement(final DelegatingConnection<?> c, final Statement s) { 060 super(c); 061 _stmt = s; 062 _conn = c; 063 } 064 065 /** 066 * Returns my underlying {@link Statement}. 067 * @return my underlying {@link Statement}. 068 * @see #getInnermostDelegate 069 */ 070 public Statement getDelegate() { 071 return _stmt; 072 } 073 074 075 /** 076 * If my underlying {@link Statement} is not a 077 * {@code DelegatingStatement}, returns it, 078 * otherwise recursively invokes this method on 079 * my delegate. 080 * <p> 081 * Hence this method will return the first 082 * delegate that is not a {@code DelegatingStatement} 083 * or {@code null} when no non-{@code DelegatingStatement} 084 * delegate can be found by traversing this chain. 085 * <p> 086 * This method is useful when you may have nested 087 * {@code DelegatingStatement}s, and you want to make 088 * sure to obtain a "genuine" {@link Statement}. 089 * @see #getDelegate 090 */ 091 public Statement getInnermostDelegate() { 092 Statement s = _stmt; 093 while(s != null && s instanceof DelegatingStatement) { 094 s = ((DelegatingStatement)s).getDelegate(); 095 if(this == s) { 096 return null; 097 } 098 } 099 return s; 100 } 101 102 /** Sets my delegate. */ 103 public void setDelegate(final Statement s) { 104 _stmt = s; 105 } 106 107 private boolean _closed = false; 108 109 protected boolean isClosedInternal() { 110 return _closed; 111 } 112 113 protected void setClosedInternal(final boolean closed) { 114 this._closed = closed; 115 } 116 117 protected void checkOpen() throws SQLException { 118 if(isClosed()) { 119 throw new SQLException 120 (this.getClass().getName() + " with address: \"" + 121 this.toString() + "\" is closed."); 122 } 123 } 124 125 /** 126 * Close this DelegatingStatement, and close 127 * any ResultSets that were not explicitly closed. 128 */ 129 @Override 130 public void close() throws SQLException { 131 if (isClosed()) { 132 return; 133 } 134 try { 135 try { 136 if (_conn != null) { 137 _conn.removeTrace(this); 138 _conn = null; 139 } 140 141 // The JDBC spec requires that a statement close any open 142 // ResultSet's when it is closed. 143 // FIXME The PreparedStatement we're wrapping should handle this for us. 144 // See bug 17301 for what could happen when ResultSets are closed twice. 145 final List<AbandonedTrace> resultSets = getTrace(); 146 if( resultSets != null) { 147 final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 148 for (final ResultSet element : set) { 149 element.close(); 150 } 151 clearTrace(); 152 } 153 154 if (_stmt != null) { 155 _stmt.close(); 156 } 157 } 158 catch (final SQLException e) { 159 handleException(e); 160 } 161 } 162 finally { 163 _closed = true; 164 _stmt = null; 165 } 166 } 167 168 protected void handleException(final SQLException e) throws SQLException { 169 if (_conn != null) { 170 _conn.handleException(e); 171 } 172 else { 173 throw e; 174 } 175 } 176 177 protected void activate() throws SQLException { 178 if(_stmt instanceof DelegatingStatement) { 179 ((DelegatingStatement)_stmt).activate(); 180 } 181 } 182 183 protected void passivate() throws SQLException { 184 if(_stmt instanceof DelegatingStatement) { 185 ((DelegatingStatement)_stmt).passivate(); 186 } 187 } 188 189 @Override 190 public Connection getConnection() throws SQLException { 191 checkOpen(); 192 return getConnectionInternal(); // return the delegating connection that created this 193 } 194 195 protected DelegatingConnection<?> getConnectionInternal() { 196 return _conn; 197 } 198 199 @Override 200 public ResultSet executeQuery(final String sql) throws SQLException { 201 checkOpen(); 202 if (_conn != null) { 203 _conn.setLastUsed(); 204 } 205 try { 206 return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql)); 207 } 208 catch (final SQLException e) { 209 handleException(e); 210 throw new AssertionError(); 211 } 212 } 213 214 @Override 215 public ResultSet getResultSet() throws SQLException { 216 checkOpen(); 217 try { 218 return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet()); 219 } 220 catch (final SQLException e) { 221 handleException(e); 222 throw new AssertionError(); 223 } 224 } 225 226 @Override 227 public int executeUpdate(final String sql) throws SQLException { 228 checkOpen(); 229 if (_conn != null) { 230 _conn.setLastUsed(); 231 } 232 try { 233 return _stmt.executeUpdate(sql); 234 } catch (final SQLException e) { 235 handleException(e); return 0; 236 } 237 } 238 239 @Override 240 public int getMaxFieldSize() throws SQLException 241 { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (final SQLException e) { handleException(e); return 0; } } 242 243 @Override 244 public void setMaxFieldSize(final int max) throws SQLException 245 { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (final SQLException e) { handleException(e); } } 246 247 @Override 248 public int getMaxRows() throws SQLException 249 { checkOpen(); try { return _stmt.getMaxRows(); } catch (final SQLException e) { handleException(e); return 0; } } 250 251 @Override 252 public void setMaxRows(final int max) throws SQLException 253 { checkOpen(); try { _stmt.setMaxRows(max); } catch (final SQLException e) { handleException(e); } } 254 255 @Override 256 public void setEscapeProcessing(final boolean enable) throws SQLException 257 { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (final SQLException e) { handleException(e); } } 258 259 @Override 260 public int getQueryTimeout() throws SQLException 261 { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (final SQLException e) { handleException(e); return 0; } } 262 263 @Override 264 public void setQueryTimeout(final int seconds) throws SQLException 265 { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (final SQLException e) { handleException(e); } } 266 267 @Override 268 public void cancel() throws SQLException 269 { checkOpen(); try { _stmt.cancel(); } catch (final SQLException e) { handleException(e); } } 270 271 @Override 272 public SQLWarning getWarnings() throws SQLException 273 { checkOpen(); try { return _stmt.getWarnings(); } catch (final SQLException e) { handleException(e); throw new AssertionError(); } } 274 275 @Override 276 public void clearWarnings() throws SQLException 277 { checkOpen(); try { _stmt.clearWarnings(); } catch (final SQLException e) { handleException(e); } } 278 279 @Override 280 public void setCursorName(final String name) throws SQLException 281 { checkOpen(); try { _stmt.setCursorName(name); } catch (final SQLException e) { handleException(e); } } 282 283 @Override 284 public boolean execute(final String sql) throws SQLException { 285 checkOpen(); 286 if (_conn != null) { 287 _conn.setLastUsed(); 288 } 289 try { 290 return _stmt.execute(sql); 291 } catch (final SQLException e) { 292 handleException(e); 293 return false; 294 } 295 } 296 297 @Override 298 public int getUpdateCount() throws SQLException 299 { checkOpen(); try { return _stmt.getUpdateCount(); } catch (final SQLException e) { handleException(e); return 0; } } 300 301 @Override 302 public boolean getMoreResults() throws SQLException 303 { checkOpen(); try { return _stmt.getMoreResults(); } catch (final SQLException e) { handleException(e); return false; } } 304 305 @Override 306 public void setFetchDirection(final int direction) throws SQLException 307 { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (final SQLException e) { handleException(e); } } 308 309 @Override 310 public int getFetchDirection() throws SQLException 311 { checkOpen(); try { return _stmt.getFetchDirection(); } catch (final SQLException e) { handleException(e); return 0; } } 312 313 @Override 314 public void setFetchSize(final int rows) throws SQLException 315 { checkOpen(); try { _stmt.setFetchSize(rows); } catch (final SQLException e) { handleException(e); } } 316 317 @Override 318 public int getFetchSize() throws SQLException 319 { checkOpen(); try { return _stmt.getFetchSize(); } catch (final SQLException e) { handleException(e); return 0; } } 320 321 @Override 322 public int getResultSetConcurrency() throws SQLException 323 { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (final SQLException e) { handleException(e); return 0; } } 324 325 @Override 326 public int getResultSetType() throws SQLException 327 { checkOpen(); try { return _stmt.getResultSetType(); } catch (final SQLException e) { handleException(e); return 0; } } 328 329 @Override 330 public void addBatch(final String sql) throws SQLException 331 { checkOpen(); try { _stmt.addBatch(sql); } catch (final SQLException e) { handleException(e); } } 332 333 @Override 334 public void clearBatch() throws SQLException 335 { checkOpen(); try { _stmt.clearBatch(); } catch (final SQLException e) { handleException(e); } } 336 337 @Override 338 public int[] executeBatch() throws SQLException { 339 checkOpen(); 340 if (_conn != null) { 341 _conn.setLastUsed(); 342 } 343 try { 344 return _stmt.executeBatch(); 345 } catch (final SQLException e) { 346 handleException(e); 347 throw new AssertionError(); 348 } 349 } 350 351 /** 352 * Returns a String representation of this object. 353 * 354 * @return String 355 */ 356 @Override 357 public String toString() { 358 return _stmt == null ? "NULL" : _stmt.toString(); 359 } 360 361 @Override 362 public boolean getMoreResults(final int current) throws SQLException 363 { checkOpen(); try { return _stmt.getMoreResults(current); } catch (final SQLException e) { handleException(e); return false; } } 364 365 @Override 366 public ResultSet getGeneratedKeys() throws SQLException { 367 checkOpen(); 368 try { 369 return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys()); 370 } catch (final SQLException e) { 371 handleException(e); 372 throw new AssertionError(); 373 } 374 } 375 376 @Override 377 public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { 378 checkOpen(); 379 if (_conn != null) { 380 _conn.setLastUsed(); 381 } 382 try { 383 return _stmt.executeUpdate(sql, autoGeneratedKeys); 384 } catch (final SQLException e) { 385 handleException(e); 386 return 0; 387 } 388 } 389 390 @Override 391 public int executeUpdate(final String sql, final int columnIndexes[]) throws SQLException { 392 checkOpen(); 393 if (_conn != null) { 394 _conn.setLastUsed(); 395 } 396 try { 397 return _stmt.executeUpdate(sql, columnIndexes); 398 } catch (final SQLException e) { 399 handleException(e); 400 return 0; 401 } 402 } 403 404 @Override 405 public int executeUpdate(final String sql, final String columnNames[]) throws SQLException { 406 checkOpen(); 407 if (_conn != null) { 408 _conn.setLastUsed(); 409 } 410 try { 411 return _stmt.executeUpdate(sql, columnNames); 412 } catch (final SQLException e) { 413 handleException(e); 414 return 0; 415 } 416 } 417 418 @Override 419 public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { 420 checkOpen(); 421 if (_conn != null) { 422 _conn.setLastUsed(); 423 } 424 try { 425 return _stmt.execute(sql, autoGeneratedKeys); 426 } catch (final SQLException e) { 427 handleException(e); 428 return false; 429 } 430 } 431 432 @Override 433 public boolean execute(final String sql, final int columnIndexes[]) throws SQLException { 434 checkOpen(); 435 if (_conn != null) { 436 _conn.setLastUsed(); 437 } 438 try { 439 return _stmt.execute(sql, columnIndexes); 440 } catch (final SQLException e) { 441 handleException(e); 442 return false; 443 } 444 } 445 446 @Override 447 public boolean execute(final String sql, final String columnNames[]) throws SQLException { 448 checkOpen(); 449 if (_conn != null) { 450 _conn.setLastUsed(); 451 } 452 try { 453 return _stmt.execute(sql, columnNames); 454 } catch (final SQLException e) { 455 handleException(e); 456 return false; 457 } 458 } 459 460 @Override 461 public int getResultSetHoldability() throws SQLException 462 { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (final SQLException e) { handleException(e); return 0; } } 463 464 /* 465 * Note was protected prior to JDBC 4 466 */ 467 @Override 468 public boolean isClosed() throws SQLException { 469 return _closed; 470 } 471 472 473 @Override 474 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 475 if (iface.isAssignableFrom(getClass())) { 476 return true; 477 } else if (iface.isAssignableFrom(_stmt.getClass())) { 478 return true; 479 } else { 480 return _stmt.isWrapperFor(iface); 481 } 482 } 483 484 @Override 485 public <T> T unwrap(final Class<T> iface) throws SQLException { 486 if (iface.isAssignableFrom(getClass())) { 487 return iface.cast(this); 488 } else if (iface.isAssignableFrom(_stmt.getClass())) { 489 return iface.cast(_stmt); 490 } else { 491 return _stmt.unwrap(iface); 492 } 493 } 494 495 @Override 496 public void setPoolable(final boolean poolable) throws SQLException { 497 checkOpen(); 498 try { 499 _stmt.setPoolable(poolable); 500 } 501 catch (final SQLException e) { 502 handleException(e); 503 } 504 } 505 506 @Override 507 public boolean isPoolable() throws SQLException { 508 checkOpen(); 509 try { 510 return _stmt.isPoolable(); 511 } 512 catch (final SQLException e) { 513 handleException(e); 514 return false; 515 } 516 } 517 518 @Override 519 public void closeOnCompletion() throws SQLException { 520 checkOpen(); 521 try { 522 _stmt.closeOnCompletion(); 523 } catch (final SQLException e) { 524 handleException(e); 525 } 526 } 527 528 @Override 529 public boolean isCloseOnCompletion() throws SQLException { 530 checkOpen(); 531 try { 532 return _stmt.isCloseOnCompletion(); 533 } catch (final SQLException e) { 534 handleException(e); 535 return false; 536 } 537 } 538 539 @Override 540 protected void finalize() throws Throwable { 541 // This is required because of statement pooling. The poolable 542 // statements will always be strongly held by the statement pool. If the 543 // delegating statements that wrap the poolable statement are not 544 // strongly held they will be garbage collected but at that point the 545 // poolable statements need to be returned to the pool else there will 546 // be a leak of statements from the pool. Closing this statement will 547 // close all the wrapped statements and return any poolable statements 548 // to the pool. 549 close(); 550 super.finalize(); 551 } 552}