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 }