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.CallableStatement; 021 import java.sql.Connection; 022 import java.sql.DatabaseMetaData; 023 import java.sql.PreparedStatement; 024 import java.sql.SQLException; 025 import java.sql.SQLWarning; 026 import java.sql.Statement; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.Map; 030 import java.sql.ResultSet; 031 /* JDBC_4_ANT_KEY_BEGIN */ 032 import java.sql.Array; 033 import java.sql.Blob; 034 import java.sql.ClientInfoStatus; 035 import java.sql.Clob; 036 import java.sql.NClob; 037 import java.sql.SQLClientInfoException; 038 import java.sql.SQLXML; 039 import java.sql.Struct; 040 import java.util.Collections; 041 import java.util.Properties; 042 /* JDBC_4_ANT_KEY_END */ 043 044 /** 045 * A base delegating implementation of {@link Connection}. 046 * <p> 047 * All of the methods from the {@link Connection} interface 048 * simply check to see that the {@link Connection} is active, 049 * and call the corresponding method on the "delegate" 050 * provided in my constructor. 051 * <p> 052 * Extends AbandonedTrace to implement Connection tracking and 053 * logging of code which created the Connection. Tracking the 054 * Connection ensures that the AbandonedObjectPool can close 055 * this connection and recycle it if its pool of connections 056 * is nearing exhaustion and this connection's last usage is 057 * older than the removeAbandonedTimeout. 058 * 059 * @author Rodney Waldhoff 060 * @author Glenn L. Nielsen 061 * @author James House 062 * @author Dirk Verbeeck 063 * @version $Revision: 896719 $ $Date: 2010-01-06 18:42:22 -0500 (Wed, 06 Jan 2010) $ 064 */ 065 public class DelegatingConnection extends AbandonedTrace 066 implements Connection { 067 068 /* JDBC_4_ANT_KEY_BEGIN */ 069 private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = 070 Collections.<String, ClientInfoStatus>emptyMap(); 071 /* JDBC_4_ANT_KEY_END */ 072 073 /** My delegate {@link Connection}. */ 074 protected Connection _conn = null; 075 076 protected boolean _closed = false; 077 078 /** 079 * Create a wrapper for the Connection which traces this 080 * Connection in the AbandonedObjectPool. 081 * 082 * @param c the {@link Connection} to delegate all calls to. 083 */ 084 public DelegatingConnection(Connection c) { 085 super(); 086 _conn = c; 087 } 088 089 /** 090 * Create a wrapper for the Connection which traces 091 * the Statements created so that any unclosed Statements 092 * can be closed when this Connection is closed. 093 * 094 * @param c the {@link Connection} to delegate all calls to. 095 * @param config the configuration for tracing abandoned objects 096 */ 097 public DelegatingConnection(Connection c, AbandonedConfig config) { 098 super(config); 099 _conn = c; 100 } 101 102 /** 103 * Returns a string representation of the metadata associated with 104 * the innnermost delegate connection. 105 * 106 * @since 1.2.2 107 */ 108 public String toString() { 109 String s = null; 110 111 Connection c = this.getInnermostDelegateInternal(); 112 if (c != null) { 113 try { 114 if (c.isClosed()) { 115 s = "connection is closed"; 116 } 117 else { 118 DatabaseMetaData meta = c.getMetaData(); 119 if (meta != null) { 120 StringBuffer sb = new StringBuffer(); 121 sb.append(meta.getURL()); 122 sb.append(", UserName="); 123 sb.append(meta.getUserName()); 124 sb.append(", "); 125 sb.append(meta.getDriverName()); 126 s = sb.toString(); 127 } 128 } 129 } 130 catch (SQLException ex) { 131 // Ignore 132 } 133 } 134 135 if (s == null) { 136 s = super.toString(); 137 } 138 139 return s; 140 } 141 142 /** 143 * Returns my underlying {@link Connection}. 144 * @return my underlying {@link Connection}. 145 */ 146 public Connection getDelegate() { 147 return getDelegateInternal(); 148 } 149 150 /** 151 * Should be final but can't be for compatibility with previous releases. 152 */ 153 protected Connection getDelegateInternal() { 154 return _conn; 155 } 156 157 /** 158 * Compares innermost delegate to the given connection. 159 * 160 * @param c connection to compare innermost delegate with 161 * @return true if innermost delegate equals <code>c</code> 162 * @since 1.2.2 163 */ 164 public boolean innermostDelegateEquals(Connection c) { 165 Connection innerCon = getInnermostDelegateInternal(); 166 if (innerCon == null) { 167 return c == null; 168 } else { 169 return innerCon.equals(c); 170 } 171 } 172 173 /** 174 * This method considers two objects to be equal 175 * if the underlying jdbc objects are equal. 176 */ 177 public boolean equals(Object obj) { 178 if (obj == null) { 179 return false; 180 } 181 if (obj == this) { 182 return true; 183 } 184 Connection delegate = getInnermostDelegateInternal(); 185 if (delegate == null) { 186 return false; 187 } 188 if (obj instanceof DelegatingConnection) { 189 DelegatingConnection c = (DelegatingConnection) obj; 190 return c.innermostDelegateEquals(delegate); 191 } 192 else { 193 return delegate.equals(obj); 194 } 195 } 196 197 public int hashCode() { 198 Object obj = getInnermostDelegateInternal(); 199 if (obj == null) { 200 return 0; 201 } 202 return obj.hashCode(); 203 } 204 205 206 /** 207 * If my underlying {@link Connection} is not a 208 * <tt>DelegatingConnection</tt>, returns it, 209 * otherwise recursively invokes this method on 210 * my delegate. 211 * <p> 212 * Hence this method will return the first 213 * delegate that is not a <tt>DelegatingConnection</tt>, 214 * or <tt>null</tt> when no non-<tt>DelegatingConnection</tt> 215 * delegate can be found by traversing this chain. 216 * <p> 217 * This method is useful when you may have nested 218 * <tt>DelegatingConnection</tt>s, and you want to make 219 * sure to obtain a "genuine" {@link Connection}. 220 */ 221 public Connection getInnermostDelegate() { 222 return getInnermostDelegateInternal(); 223 } 224 225 protected final Connection getInnermostDelegateInternal() { 226 Connection c = _conn; 227 while(c != null && c instanceof DelegatingConnection) { 228 c = ((DelegatingConnection)c).getDelegateInternal(); 229 if(this == c) { 230 return null; 231 } 232 } 233 return c; 234 } 235 236 /** Sets my delegate. */ 237 public void setDelegate(Connection c) { 238 _conn = c; 239 } 240 241 /** 242 * Closes the underlying connection, and close 243 * any Statements that were not explicitly closed. 244 */ 245 public void close() throws SQLException { 246 passivate(); 247 _conn.close(); 248 } 249 250 protected void handleException(SQLException e) throws SQLException { 251 throw e; 252 } 253 254 public Statement createStatement() throws SQLException { 255 checkOpen(); 256 try { 257 return new DelegatingStatement(this, _conn.createStatement()); 258 } 259 catch (SQLException e) { 260 handleException(e); 261 return null; 262 } 263 } 264 265 public Statement createStatement(int resultSetType, 266 int resultSetConcurrency) throws SQLException { 267 checkOpen(); 268 try { 269 return new DelegatingStatement 270 (this, _conn.createStatement(resultSetType,resultSetConcurrency)); 271 } 272 catch (SQLException e) { 273 handleException(e); 274 return null; 275 } 276 } 277 278 public PreparedStatement prepareStatement(String sql) throws SQLException { 279 checkOpen(); 280 try { 281 return new DelegatingPreparedStatement 282 (this, _conn.prepareStatement(sql)); 283 } 284 catch (SQLException e) { 285 handleException(e); 286 return null; 287 } 288 } 289 290 public PreparedStatement prepareStatement(String sql, 291 int resultSetType, 292 int resultSetConcurrency) throws SQLException { 293 checkOpen(); 294 try { 295 return new DelegatingPreparedStatement 296 (this, _conn.prepareStatement 297 (sql,resultSetType,resultSetConcurrency)); 298 } 299 catch (SQLException e) { 300 handleException(e); 301 return null; 302 } 303 } 304 305 public CallableStatement prepareCall(String sql) throws SQLException { 306 checkOpen(); 307 try { 308 return new DelegatingCallableStatement(this, _conn.prepareCall(sql)); 309 } 310 catch (SQLException e) { 311 handleException(e); 312 return null; 313 } 314 } 315 316 public CallableStatement prepareCall(String sql, 317 int resultSetType, 318 int resultSetConcurrency) throws SQLException { 319 checkOpen(); 320 try { 321 return new DelegatingCallableStatement 322 (this, _conn.prepareCall(sql, resultSetType,resultSetConcurrency)); 323 } 324 catch (SQLException e) { 325 handleException(e); 326 return null; 327 } 328 } 329 330 public void clearWarnings() throws SQLException 331 { checkOpen(); try { _conn.clearWarnings(); } catch (SQLException e) { handleException(e); } } 332 333 public void commit() throws SQLException 334 { checkOpen(); try { _conn.commit(); } catch (SQLException e) { handleException(e); } } 335 336 public boolean getAutoCommit() throws SQLException 337 { checkOpen(); try { return _conn.getAutoCommit(); } catch (SQLException e) { handleException(e); return false; } 338 } 339 public String getCatalog() throws SQLException 340 { checkOpen(); try { return _conn.getCatalog(); } catch (SQLException e) { handleException(e); return null; } } 341 342 public DatabaseMetaData getMetaData() throws SQLException { 343 checkOpen(); 344 try { 345 return new DelegatingDatabaseMetaData(this, _conn.getMetaData()); 346 } catch (SQLException e) { 347 handleException(e); 348 return null; 349 } 350 } 351 352 public int getTransactionIsolation() throws SQLException 353 { checkOpen(); try { return _conn.getTransactionIsolation(); } catch (SQLException e) { handleException(e); return -1; } } 354 355 public Map getTypeMap() throws SQLException 356 { checkOpen(); try { return _conn.getTypeMap(); } catch (SQLException e) { handleException(e); return null; } } 357 358 public SQLWarning getWarnings() throws SQLException 359 { checkOpen(); try { return _conn.getWarnings(); } catch (SQLException e) { handleException(e); return null; } } 360 361 public boolean isReadOnly() throws SQLException 362 { checkOpen(); try { return _conn.isReadOnly(); } catch (SQLException e) { handleException(e); return false; } } 363 364 public String nativeSQL(String sql) throws SQLException 365 { checkOpen(); try { return _conn.nativeSQL(sql); } catch (SQLException e) { handleException(e); return null; } } 366 367 public void rollback() throws SQLException 368 { checkOpen(); try { _conn.rollback(); } catch (SQLException e) { handleException(e); } } 369 370 public void setAutoCommit(boolean autoCommit) throws SQLException 371 { checkOpen(); try { _conn.setAutoCommit(autoCommit); } catch (SQLException e) { handleException(e); } } 372 373 public void setCatalog(String catalog) throws SQLException 374 { checkOpen(); try { _conn.setCatalog(catalog); } catch (SQLException e) { handleException(e); } } 375 376 public void setReadOnly(boolean readOnly) throws SQLException 377 { checkOpen(); try { _conn.setReadOnly(readOnly); } catch (SQLException e) { handleException(e); } } 378 379 public void setTransactionIsolation(int level) throws SQLException 380 { checkOpen(); try { _conn.setTransactionIsolation(level); } catch (SQLException e) { handleException(e); } } 381 382 public void setTypeMap(Map map) throws SQLException 383 { checkOpen(); try { _conn.setTypeMap(map); } catch (SQLException e) { handleException(e); } } 384 385 public boolean isClosed() throws SQLException { 386 return _closed || _conn.isClosed(); 387 } 388 389 protected void checkOpen() throws SQLException { 390 if(_closed) { 391 if (null != _conn) { 392 String label = ""; 393 try { 394 label = _conn.toString(); 395 } catch (Exception ex) { 396 // ignore, leave label empty 397 } 398 throw new SQLException 399 ("Connection " + label + " is closed."); 400 } else { 401 throw new SQLException 402 ("Connection is null."); 403 } 404 } 405 } 406 407 protected void activate() { 408 _closed = false; 409 setLastUsed(); 410 if(_conn instanceof DelegatingConnection) { 411 ((DelegatingConnection)_conn).activate(); 412 } 413 } 414 415 protected void passivate() throws SQLException { 416 try { 417 // The JDBC spec requires that a Connection close any open 418 // Statement's when it is closed. 419 // DBCP-288. Not all the traced objects will be statements 420 List traces = getTrace(); 421 if(traces != null) { 422 Iterator traceIter = traces.iterator(); 423 while (traceIter.hasNext()) { 424 Object trace = traceIter.next(); 425 if (trace instanceof Statement) { 426 ((Statement) trace).close(); 427 } else if (trace instanceof ResultSet) { 428 // DBCP-265: Need to close the result sets that are 429 // generated via DatabaseMetaData 430 ((ResultSet) trace).close(); 431 } 432 } 433 clearTrace(); 434 } 435 setLastUsed(0); 436 if(_conn instanceof DelegatingConnection) { 437 ((DelegatingConnection)_conn).passivate(); 438 } 439 } 440 finally { 441 _closed = true; 442 } 443 } 444 445 public int getHoldability() throws SQLException 446 { checkOpen(); try { return _conn.getHoldability(); } catch (SQLException e) { handleException(e); return 0; } } 447 448 public void setHoldability(int holdability) throws SQLException 449 { checkOpen(); try { _conn.setHoldability(holdability); } catch (SQLException e) { handleException(e); } } 450 451 public java.sql.Savepoint setSavepoint() throws SQLException 452 { checkOpen(); try { return _conn.setSavepoint(); } catch (SQLException e) { handleException(e); return null; } } 453 454 public java.sql.Savepoint setSavepoint(String name) throws SQLException 455 { checkOpen(); try { return _conn.setSavepoint(name); } catch (SQLException e) { handleException(e); return null; } } 456 457 public void rollback(java.sql.Savepoint savepoint) throws SQLException 458 { checkOpen(); try { _conn.rollback(savepoint); } catch (SQLException e) { handleException(e); } } 459 460 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException 461 { checkOpen(); try { _conn.releaseSavepoint(savepoint); } catch (SQLException e) { handleException(e); } } 462 463 public Statement createStatement(int resultSetType, 464 int resultSetConcurrency, 465 int resultSetHoldability) throws SQLException { 466 checkOpen(); 467 try { 468 return new DelegatingStatement(this, _conn.createStatement( 469 resultSetType, resultSetConcurrency, resultSetHoldability)); 470 } 471 catch (SQLException e) { 472 handleException(e); 473 return null; 474 } 475 } 476 477 public PreparedStatement prepareStatement(String sql, int resultSetType, 478 int resultSetConcurrency, 479 int resultSetHoldability) throws SQLException { 480 checkOpen(); 481 try { 482 return new DelegatingPreparedStatement(this, _conn.prepareStatement( 483 sql, resultSetType, resultSetConcurrency, resultSetHoldability)); 484 } 485 catch (SQLException e) { 486 handleException(e); 487 return null; 488 } 489 } 490 491 public CallableStatement prepareCall(String sql, int resultSetType, 492 int resultSetConcurrency, 493 int resultSetHoldability) throws SQLException { 494 checkOpen(); 495 try { 496 return new DelegatingCallableStatement(this, _conn.prepareCall( 497 sql, resultSetType, resultSetConcurrency, resultSetHoldability)); 498 } 499 catch (SQLException e) { 500 handleException(e); 501 return null; 502 } 503 } 504 505 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { 506 checkOpen(); 507 try { 508 return new DelegatingPreparedStatement(this, _conn.prepareStatement( 509 sql, autoGeneratedKeys)); 510 } 511 catch (SQLException e) { 512 handleException(e); 513 return null; 514 } 515 } 516 517 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException { 518 checkOpen(); 519 try { 520 return new DelegatingPreparedStatement(this, _conn.prepareStatement( 521 sql, columnIndexes)); 522 } 523 catch (SQLException e) { 524 handleException(e); 525 return null; 526 } 527 } 528 529 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException { 530 checkOpen(); 531 try { 532 return new DelegatingPreparedStatement(this, _conn.prepareStatement( 533 sql, columnNames)); 534 } 535 catch (SQLException e) { 536 handleException(e); 537 return null; 538 } 539 } 540 541 /* JDBC_4_ANT_KEY_BEGIN */ 542 543 public boolean isWrapperFor(Class<?> iface) throws SQLException { 544 return iface.isAssignableFrom(getClass()) || _conn.isWrapperFor(iface); 545 } 546 547 public <T> T unwrap(Class<T> iface) throws SQLException { 548 if (iface.isAssignableFrom(getClass())) { 549 return iface.cast(this); 550 } else if (iface.isAssignableFrom(_conn.getClass())) { 551 return iface.cast(_conn); 552 } else { 553 return _conn.unwrap(iface); 554 } 555 } 556 557 public Array createArrayOf(String typeName, Object[] elements) throws SQLException { 558 checkOpen(); 559 try { 560 return _conn.createArrayOf(typeName, elements); 561 } 562 catch (SQLException e) { 563 handleException(e); 564 return null; 565 } 566 } 567 568 public Blob createBlob() throws SQLException { 569 checkOpen(); 570 try { 571 return _conn.createBlob(); 572 } 573 catch (SQLException e) { 574 handleException(e); 575 return null; 576 } 577 } 578 579 public Clob createClob() throws SQLException { 580 checkOpen(); 581 try { 582 return _conn.createClob(); 583 } 584 catch (SQLException e) { 585 handleException(e); 586 return null; 587 } 588 } 589 590 public NClob createNClob() throws SQLException { 591 checkOpen(); 592 try { 593 return _conn.createNClob(); 594 } 595 catch (SQLException e) { 596 handleException(e); 597 return null; 598 } 599 } 600 601 public SQLXML createSQLXML() throws SQLException { 602 checkOpen(); 603 try { 604 return _conn.createSQLXML(); 605 } 606 catch (SQLException e) { 607 handleException(e); 608 return null; 609 } 610 } 611 612 public Struct createStruct(String typeName, Object[] attributes) throws SQLException { 613 checkOpen(); 614 try { 615 return _conn.createStruct(typeName, attributes); 616 } 617 catch (SQLException e) { 618 handleException(e); 619 return null; 620 } 621 } 622 623 public boolean isValid(int timeout) throws SQLException { 624 checkOpen(); 625 try { 626 return _conn.isValid(timeout); 627 } 628 catch (SQLException e) { 629 handleException(e); 630 return false; 631 } 632 } 633 634 public void setClientInfo(String name, String value) throws SQLClientInfoException { 635 try { 636 checkOpen(); 637 _conn.setClientInfo(name, value); 638 } 639 catch (SQLClientInfoException e) { 640 throw e; 641 } 642 catch (SQLException e) { 643 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e); 644 } 645 } 646 647 public void setClientInfo(Properties properties) throws SQLClientInfoException { 648 try { 649 checkOpen(); 650 _conn.setClientInfo(properties); 651 } 652 catch (SQLClientInfoException e) { 653 throw e; 654 } 655 catch (SQLException e) { 656 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e); 657 } 658 } 659 660 public Properties getClientInfo() throws SQLException { 661 checkOpen(); 662 try { 663 return _conn.getClientInfo(); 664 } 665 catch (SQLException e) { 666 handleException(e); 667 return null; 668 } 669 } 670 671 public String getClientInfo(String name) throws SQLException { 672 checkOpen(); 673 try { 674 return _conn.getClientInfo(name); 675 } 676 catch (SQLException e) { 677 handleException(e); 678 return null; 679 } 680 } 681 /* JDBC_4_ANT_KEY_END */ 682 }