1 22 23 24 package com.mchange.v2.c3p0.impl; 25 26 import java.util.*; 27 import java.sql.*; 28 import javax.sql.*; 29 import com.mchange.v2.c3p0.*; 30 import com.mchange.v2.c3p0.stmt.*; 31 import com.mchange.v2.c3p0.util.*; 32 import com.mchange.v2.log.*; 33 34 import java.lang.reflect.Method ; 35 import com.mchange.v2.lang.ObjectUtils; 36 import com.mchange.v2.sql.SqlUtils; 37 38 public final class NewPooledConnection extends AbstractC3P0PooledConnection{ 39 40 private final static MLogger logger = MLog.getLogger( NewPooledConnection.class ); 41 42 private final static SQLException NORMAL_CLOSE_PLACEHOLDER = new SQLException("This pooled Connection was explicitly close()ed by " + 43 "a client, not invalidated due to an error."); 44 45 final Connection physicalConnection; 47 final ConnectionTester connectionTester; 48 final boolean autoCommitOnClose; 49 final boolean forceIgnoreUnresolvedTransactions; 50 final String preferredTestQuery; 51 final boolean supports_setHoldability; 52 final boolean supports_setReadOnly; 53 final boolean supports_setTypeMap; 54 final int dflt_txn_isolation; 55 final String dflt_catalog; 56 final int dflt_holdability; 57 final boolean dflt_readOnly; 58 final Map dflt_typeMap; 59 final ConnectionEventSupport ces; 60 61 GooGooStatementCache scache = null; 63 Throwable invalidatingException = null; 64 int connection_status = ConnectionTester.CONNECTION_IS_OKAY; 65 Set uncachedActiveStatements = new HashSet(); Map resultSetsForStatements = new HashMap(); Set metaDataResultSets = new HashSet(); 68 Set rawConnectionResultSets = null; boolean connection_error_signaled = false; 70 71 volatile NewProxyConnection exposedProxy = null; 73 volatile boolean isolation_lvl_nondefault = false; 74 volatile boolean catalog_nondefault = false; 75 volatile boolean holdability_nondefault = false; 76 volatile boolean readOnly_nondefault = false; 77 volatile boolean typeMap_nondefault = false; 78 79 public NewPooledConnection(Connection con, 81 ConnectionTester connectionTester, 82 boolean autoCommitOnClose, 83 boolean forceIgnoreUnresolvedTransactions, 84 String preferredTestQuery, 85 ConnectionCustomizer cc, 86 String pdsIdt) throws SQLException 87 { 88 try 89 { 90 if (cc != null) 91 cc.onAcquire( con, pdsIdt ); 92 } 93 catch (Exception e) 94 { throw SqlUtils.toSQLException(e); } 95 96 this.physicalConnection = con; 97 this.connectionTester = connectionTester; 98 this.autoCommitOnClose = autoCommitOnClose; 99 this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions; 100 this.preferredTestQuery = preferredTestQuery; 101 this.supports_setHoldability = C3P0ImplUtils.supportsMethod(con, "setHoldability", new Class []{ Integer .class }); 102 this.supports_setReadOnly = C3P0ImplUtils.supportsMethod(con, "setReadOnly", new Class []{ Boolean .class }); 103 this.supports_setTypeMap = C3P0ImplUtils.supportsMethod(con, "setTypeMap", new Class []{ Map.class }); 104 this.dflt_txn_isolation = con.getTransactionIsolation(); 105 this.dflt_catalog = con.getCatalog(); 106 this.dflt_holdability = (supports_setHoldability ? con.getHoldability() : ResultSet.CLOSE_CURSORS_AT_COMMIT); 107 this.dflt_readOnly = (supports_setReadOnly ? carefulCheckReadOnly(con) : false); 108 this.dflt_typeMap = (supports_setTypeMap && (carefulCheckTypeMap(con) == null) ? null : Collections.EMPTY_MAP); 109 this.ces = new ConnectionEventSupport(this); 110 } 111 112 private static boolean carefulCheckReadOnly(Connection con) 113 { 114 try { return con.isReadOnly(); } 115 catch (Exception e) 116 { 117 if (false) 118 { 119 if (logger.isLoggable(MLevel.FINER)) 120 logger.log(MLevel.FINER, con + " threw an Exception when we tried to check its default " + 121 "read only state. This is not usually a problem! It just means the Connection " + 122 "doesn't support the readOnly property, and c3p0 works around this.", e); 123 } 124 return false; 125 } 126 } 127 128 private static Map carefulCheckTypeMap(Connection con) 129 { 130 try { return con.getTypeMap(); } 131 catch (Exception e) 132 { 133 if (false) 134 { 135 if (logger.isLoggable(MLevel.FINER)) 136 logger.log(MLevel.FINER, con + " threw an Exception when we tried to check its default " + 137 "type map. This is not usually a problem! It just means the Connection " + 138 "doesn't support the typeMap property, and c3p0 works around this.", e); 139 } 140 return null; 141 } 142 } 143 144 public synchronized Connection getConnection() throws SQLException 145 { 146 try 147 { 148 if ( exposedProxy == null ) 150 { 151 exposedProxy = new NewProxyConnection( physicalConnection, this ); 152 } 153 else 154 { 155 159 if ( logger.isLoggable( MLevel.WARNING ) ) 160 logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " + 161 "it had already provided a client with a Connection that has not yet been " + 162 "closed. This probably indicates a bug in the connection pool!!!"); 163 164 } 165 return exposedProxy; 166 } 167 catch ( Exception e ) 168 { 169 SQLException sqle = handleThrowable( e ); 170 throw sqle; 171 } 172 } 173 174 public synchronized int getConnectionStatus() 175 { return connection_status; } 176 177 public synchronized void closeAll() throws SQLException 178 { 179 try 180 { 181 closeAllCachedStatements(); 182 } 183 catch ( Exception e ) 184 { 185 SQLException sqle = handleThrowable( e ); 186 throw sqle; 187 } 188 } 189 190 public synchronized void close() throws SQLException 191 { close( null ); } 192 193 public void addConnectionEventListener(ConnectionEventListener cel) 194 { ces.addConnectionEventListener( cel ); } 195 196 public void removeConnectionEventListener(ConnectionEventListener cel) 197 { ces.removeConnectionEventListener( cel ); } 198 199 public synchronized void initStatementCache( GooGooStatementCache scache ) 201 { this.scache = scache; } 202 203 public synchronized GooGooStatementCache getStatementCache() 204 { return scache; } 205 206 void markNewTxnIsolation( int lvl ) { 209 this.isolation_lvl_nondefault = (lvl != dflt_txn_isolation); 210 } 212 213 void markNewCatalog( String catalog ) { 215 this.catalog_nondefault = ObjectUtils.eqOrBothNull(catalog, dflt_catalog); 216 } 217 218 void markNewHoldability( int holdability ) { 220 this.holdability_nondefault = (holdability != dflt_holdability); 221 } 222 223 void markNewReadOnly( boolean readOnly ) { 225 this.readOnly_nondefault = (readOnly != dflt_readOnly); 226 } 227 228 void markNewTypeMap( Map typeMap ) { 230 this.typeMap_nondefault = (typeMap != dflt_typeMap); 231 } 232 233 synchronized Object checkoutStatement( Method stmtProducingMethod, Object [] args ) throws SQLException 234 { return scache.checkoutStatement( physicalConnection, stmtProducingMethod, args ); } 235 236 synchronized void checkinStatement( Statement stmt ) throws SQLException 237 { 238 cleanupStatementResultSets( stmt ); 239 scache.checkinStatement( stmt ); 240 } 241 242 synchronized void markActiveUncachedStatement( Statement stmt ) 243 { uncachedActiveStatements.add( stmt ); } 244 245 synchronized void markInactiveUncachedStatement( Statement stmt ) 246 { 247 cleanupStatementResultSets( stmt ); 248 uncachedActiveStatements.remove( stmt ); 249 } 250 251 synchronized void markActiveResultSetForStatement( Statement stmt, ResultSet rs ) 252 { 253 Set rss = resultSets( stmt, true ); 254 rss.add( rs ); 255 } 256 257 synchronized void markInactiveResultSetForStatement( Statement stmt, ResultSet rs ) 258 { 259 Set rss = resultSets( stmt, false ); 260 if (rss == null) 261 { 262 if (logger.isLoggable( MLevel.FINE )) 263 logger.fine( "ResultSet " + rs + " was apparently closed after the Statement that created it had already been closed." ); 264 } 265 else if ( ! rss.remove( rs ) ) 266 throw new InternalError ("Marking a ResultSet inactive that we did not know was opened!"); 267 } 268 269 synchronized void markActiveRawConnectionResultSet( ResultSet rs ) 270 { 271 if (rawConnectionResultSets == null) 272 rawConnectionResultSets = new HashSet(); 273 rawConnectionResultSets.add( rs ); 274 } 275 276 synchronized void markInactiveRawConnectionResultSet( ResultSet rs ) 277 { 278 if ( ! rawConnectionResultSets.remove( rs ) ) 279 throw new InternalError ("Marking a raw Connection ResultSet inactive that we did not know was opened!"); 280 } 281 282 synchronized void markActiveMetaDataResultSet( ResultSet rs ) 283 { metaDataResultSets.add( rs ); } 284 285 synchronized void markInactiveMetaDataResultSet( ResultSet rs ) 286 { metaDataResultSets.remove( rs ); } 287 288 synchronized void markClosedProxyConnection( NewProxyConnection npc, boolean txn_known_resolved ) 289 { 290 try 291 { 292 if (npc != exposedProxy) 293 throw new InternalError ("C3P0 Error: An exposed proxy asked a PooledConnection that was not its parents to clean up its resources!"); 294 295 List closeExceptions = new LinkedList(); 296 cleanupResultSets( closeExceptions ); 297 cleanupUncachedStatements( closeExceptions ); 298 checkinAllCachedStatements( closeExceptions ); 299 if ( closeExceptions.size() > 0 ) 300 { 301 if ( logger.isLoggable( MLevel.INFO ) ) 303 logger.info("[c3p0] The following Exceptions occurred while trying to clean up a Connection's stranded resources:"); 304 for ( Iterator ii = closeExceptions.iterator(); ii.hasNext(); ) 305 { 306 Throwable t = (Throwable ) ii.next(); 307 if ( logger.isLoggable( MLevel.INFO ) ) 310 logger.log( MLevel.INFO, "[c3p0 -- conection resource close Exception]", t ); 311 } 312 } 313 reset( txn_known_resolved ); 314 } 315 catch (SQLException e) { 317 if (Debug.DEBUG && logger.isLoggable( MLevel.FINE )) 319 logger.log(MLevel.FINE, "An exception occurred while reseting a closed Connection. Invalidating Connection.", e); 320 321 updateConnectionStatus( ConnectionTester.CONNECTION_IS_INVALID ); 322 fireConnectionErrorOccurred( e ); 323 } 324 finally 325 { 326 fireConnectionClosed(); 327 } 328 } 329 330 private void reset( boolean txn_known_resolved ) throws SQLException 331 { 332 C3P0ImplUtils.resetTxnState( physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, txn_known_resolved ); 333 if (isolation_lvl_nondefault) 334 { 335 physicalConnection.setTransactionIsolation( dflt_txn_isolation ); 336 isolation_lvl_nondefault = false; 337 } 339 if (catalog_nondefault) 340 { 341 physicalConnection.setCatalog( dflt_catalog ); 342 catalog_nondefault = false; 343 } 344 if (holdability_nondefault) { 346 physicalConnection.setHoldability( dflt_holdability ); 347 holdability_nondefault = false; 348 } 349 if (readOnly_nondefault) 350 { 351 physicalConnection.setReadOnly( dflt_readOnly ); 352 readOnly_nondefault = false; 353 } 354 if (typeMap_nondefault) 355 { 356 physicalConnection.setTypeMap( dflt_typeMap ); 357 typeMap_nondefault = false; 358 } 359 } 360 361 synchronized boolean isStatementCaching() 362 { return scache != null; } 363 364 synchronized SQLException handleThrowable( Throwable t ) 365 { 366 if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER )) 367 logger.log( MLevel.FINER, this + " handling a throwable.", t ); 368 369 SQLException sqle = SqlUtils.toSQLException( t ); 370 372 int status; 373 if (connectionTester instanceof FullQueryConnectionTester) 374 status = ((FullQueryConnectionTester) connectionTester).statusOnException( physicalConnection, sqle, preferredTestQuery ); 375 else 376 status = connectionTester.statusOnException( physicalConnection, sqle ); 377 378 updateConnectionStatus( status ); 379 if (status != ConnectionTester.CONNECTION_IS_OKAY) 380 { 381 if (Debug.DEBUG) 382 { 383 if ( logger.isLoggable( MLevel.FINE ) ) 386 logger.log(MLevel.FINE, this + " invalidated by Exception.", t); 387 } 388 389 413 414 if (! connection_error_signaled) 415 { 416 ces.fireConnectionErrorOccurred( sqle ); 417 connection_error_signaled = true; 418 } 419 else 420 { 421 if ( logger.isLoggable( MLevel.WARNING ) ) 424 { 425 logger.log(MLevel.WARNING, "[c3p0] A PooledConnection that has already signalled a Connection error is still in use!"); 426 logger.log(MLevel.WARNING, "[c3p0] Another error has occurred [ " + t + " ] which will not be reported to listeners!", t); 427 } 428 } 429 } 430 return sqle; 431 } 432 433 434 436 private void fireConnectionClosed() 437 { 438 exposedProxy = null; 439 ces.fireConnectionClosed(); 440 } 441 442 private void fireConnectionErrorOccurred(SQLException error) 443 { ces.fireConnectionErrorOccurred( error ); } 444 445 447 454 private void close( Throwable cause ) throws SQLException 455 { 456 if ( this.invalidatingException == null ) 457 { 458 List closeExceptions = new LinkedList(); 459 460 cleanupResultSets( closeExceptions ); 462 463 cleanupUncachedStatements( closeExceptions ); 465 466 try 468 { closeAllCachedStatements(); } 469 catch ( SQLException e ) 470 { closeExceptions.add(e); } 471 472 try 474 { physicalConnection.close(); } 475 catch ( SQLException e ) 476 { 477 if (logger.isLoggable( MLevel.FINER )) 478 logger.log( MLevel.FINER, "Failed to close physical Connection: " + physicalConnection, e ); 479 480 closeExceptions.add(e); 481 } 482 483 if ( connection_status == ConnectionTester.CONNECTION_IS_OKAY ) 485 connection_status = ConnectionTester.CONNECTION_IS_INVALID; 486 if ( cause == null ) 487 { 488 this.invalidatingException = NORMAL_CLOSE_PLACEHOLDER; 489 490 if ( logger.isLoggable( MLevel.FINEST ) ) 491 logger.log( MLevel.FINEST, this + " closed by a client.", new Exception ("DEBUG -- CLOSE BY CLIENT STACK TRACE") ); 492 493 logCloseExceptions( null, closeExceptions ); 494 495 if (closeExceptions.size() > 0) 496 throw new SQLException("Some resources failed to close properly while closing " + this); 497 } 498 else 499 { 500 this.invalidatingException = cause; 501 if (Debug.TRACE >= Debug.TRACE_MED) 502 logCloseExceptions( cause, closeExceptions ); 503 else 504 logCloseExceptions( cause, null ); 505 } 506 } 507 } 508 509 private void cleanupResultSets( List closeExceptions ) 510 { 511 cleanupAllStatementResultSets( closeExceptions ); 512 cleanupUnclosedResultSetsSet( metaDataResultSets, closeExceptions ); 513 if ( rawConnectionResultSets != null ) 514 cleanupUnclosedResultSetsSet( rawConnectionResultSets, closeExceptions ); 515 } 516 517 private void cleanupUnclosedResultSetsSet( Set rsSet, List closeExceptions ) 518 { 519 for ( Iterator ii = rsSet.iterator(); ii.hasNext(); ) 520 { 521 ResultSet rs = (ResultSet) ii.next(); 522 try 523 { rs.close(); } 524 catch ( SQLException e ) 525 { closeExceptions.add(e); } 526 527 ii.remove(); 528 } 529 } 530 531 private void cleanupStatementResultSets( Statement stmt ) 532 { 533 Set rss = resultSets( stmt, false ); 534 if ( rss != null ) 535 { 536 for ( Iterator ii = rss.iterator(); ii.hasNext(); ) 537 { 538 try 539 { ((ResultSet) ii.next()).close(); } 540 catch ( Exception e ) 541 { 542 if ( logger.isLoggable( MLevel.INFO ) ) 545 logger.log(MLevel.INFO, "ResultSet close() failed.", e); 546 } 547 } 548 } 549 resultSetsForStatements.remove( stmt ); 550 } 551 552 private void cleanupAllStatementResultSets( List closeExceptions ) 553 { 554 for ( Iterator ii = resultSetsForStatements.keySet().iterator(); ii.hasNext(); ) 555 { 556 Object stmt = ii.next(); 557 Set rss = (Set) resultSetsForStatements.get( stmt ); 558 for (Iterator jj = rss.iterator(); jj.hasNext(); ) 559 { 560 ResultSet rs = (ResultSet) jj.next(); 561 try 562 { rs.close(); } 563 catch ( SQLException e ) 564 { closeExceptions.add(e); } 565 } 566 } 567 resultSetsForStatements.clear(); 568 } 569 570 private void cleanupUncachedStatements( List closeExceptions ) 571 { 572 for ( Iterator ii = uncachedActiveStatements.iterator(); ii.hasNext(); ) 573 { 574 Statement stmt = (Statement) ii.next(); 575 try 576 { stmt.close(); } 577 catch ( SQLException e ) 578 { closeExceptions.add(e); } 579 580 ii.remove(); 581 } 582 } 583 584 private void checkinAllCachedStatements( List closeExceptions ) 585 { 586 try 587 { 588 if (scache != null) 589 scache.checkinAll( physicalConnection ); 590 } 591 catch ( SQLException e ) 592 { closeExceptions.add(e); } 593 } 594 595 private void closeAllCachedStatements() throws SQLException 596 { 597 if (scache != null) 598 scache.closeAll( physicalConnection ); 599 } 600 601 private void updateConnectionStatus(int status) 602 { 603 switch ( this.connection_status ) 604 { 605 case ConnectionTester.DATABASE_IS_INVALID: 606 break; 608 case ConnectionTester.CONNECTION_IS_INVALID: 609 if (status == ConnectionTester.DATABASE_IS_INVALID) 610 this.connection_status = status; 611 break; 612 case ConnectionTester.CONNECTION_IS_OKAY: 613 if (status != ConnectionTester.CONNECTION_IS_OKAY) 614 this.connection_status = status; 615 break; 616 default: 617 throw new InternalError (this + " -- Illegal Connection Status: " + this.connection_status); 618 } 619 } 620 621 private Set resultSets( Statement stmt, boolean create ) 622 { 623 Set out = (Set) resultSetsForStatements.get( stmt ); 624 if ( out == null && create ) 625 { 626 out = new HashSet(); 627 resultSetsForStatements.put( stmt, out ); 628 } 629 return out; 630 } 631 632 Connection getPhysicalConnection() 634 { return physicalConnection; } 635 636 private static void logCloseExceptions( Throwable cause, Collection exceptions ) 638 { 639 if ( logger.isLoggable( MLevel.INFO ) ) 640 { 641 if (cause != null) 642 { 643 logger.log(MLevel.INFO, "[c3p0] A PooledConnection died due to the following error!", cause); 646 } 647 if ( exceptions != null && exceptions.size() > 0) 648 { 649 if ( cause == null ) 650 logger.info("[c3p0] Exceptions occurred while trying to close a PooledConnection's resources normally."); 651 else 653 logger.info("[c3p0] Exceptions occurred while trying to close a Broken PooledConnection."); 654 for ( Iterator ii = exceptions.iterator(); ii.hasNext(); ) 656 { 657 Throwable t = (Throwable ) ii.next(); 658 logger.log(MLevel.INFO, "[c3p0] NewPooledConnection close Exception.", t); 661 } 662 } 663 } 664 } 665 } 666 | Popular Tags |