1 22 package org.jboss.resource.adapter.jdbc; 23 24 import java.io.PrintWriter ; 25 import java.sql.CallableStatement ; 26 import java.sql.Connection ; 27 import java.sql.PreparedStatement ; 28 import java.sql.SQLException ; 29 import java.sql.Savepoint ; 30 import java.sql.Statement ; 31 import java.util.ArrayList ; 32 import java.util.Collection ; 33 import java.util.HashSet ; 34 import java.util.Iterator ; 35 import java.util.Properties ; 36 import java.util.Set ; 37 38 import javax.resource.ResourceException ; 39 import javax.resource.spi.ConnectionEvent ; 40 import javax.resource.spi.ConnectionEventListener ; 41 import javax.resource.spi.ConnectionRequestInfo ; 42 import javax.resource.spi.ManagedConnection ; 43 import javax.resource.spi.ManagedConnectionMetaData ; 44 import javax.resource.spi.ResourceAdapterInternalException ; 45 import javax.security.auth.Subject ; 46 47 import org.jboss.logging.Logger; 48 import org.jboss.resource.JBossResourceException; 49 50 import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean; 51 52 60 61 public abstract class BaseWrapperManagedConnection implements ManagedConnection 62 { 63 protected final BaseWrapperManagedConnectionFactory mcf; 64 65 protected final Connection con; 66 67 protected final Properties props; 68 69 private final int transactionIsolation; 70 71 private final boolean readOnly; 72 73 private final Collection cels = new ArrayList (); 74 75 private final Set handles = new HashSet (); 76 77 private PreparedStatementCache psCache = null; 78 79 protected final Object stateLock = new Object (); 80 81 protected boolean inManagedTransaction = false; 82 83 protected SynchronizedBoolean inLocalTransaction = new SynchronizedBoolean(false); 84 85 protected boolean jdbcAutoCommit = true; 86 87 protected boolean underlyingAutoCommit = true; 88 89 protected boolean jdbcReadOnly; 90 91 protected boolean underlyingReadOnly; 92 93 protected int jdbcTransactionIsolation; 94 95 protected boolean destroyed = false; 96 97 public BaseWrapperManagedConnection(final BaseWrapperManagedConnectionFactory mcf, final Connection con, 98 final Properties props, final int transactionIsolation, final int psCacheSize) throws SQLException 99 { 100 this.mcf = mcf; 101 this.con = con; 102 this.props = props; 103 104 if (psCacheSize > 0) 105 psCache = new PreparedStatementCache(psCacheSize); 106 107 if (transactionIsolation == -1) 108 this.transactionIsolation = con.getTransactionIsolation(); 109 110 else 111 { 112 this.transactionIsolation = transactionIsolation; 113 con.setTransactionIsolation(transactionIsolation); 114 } 115 116 readOnly = con.isReadOnly(); 117 118 if (mcf.getNewConnectionSQL() != null) 119 { 120 Statement s = con.createStatement(); 121 try 122 { 123 s.execute(mcf.getNewConnectionSQL()); 124 } 125 finally 126 { 127 s.close(); 128 } 129 } 130 131 underlyingReadOnly = readOnly; 132 jdbcReadOnly = readOnly; 133 jdbcTransactionIsolation = this.transactionIsolation; 134 } 135 136 public void addConnectionEventListener(ConnectionEventListener cel) 137 { 138 synchronized (cels) 139 { 140 cels.add(cel); 141 } 142 } 143 144 public void removeConnectionEventListener(ConnectionEventListener cel) 145 { 146 synchronized (cels) 147 { 148 cels.remove(cel); 149 } 150 } 151 152 public void associateConnection(Object handle) throws ResourceException 153 { 154 if (!(handle instanceof WrappedConnection)) 155 throw new JBossResourceException("Wrong kind of connection handle to associate" + handle); 156 ((WrappedConnection) handle).setManagedConnection(this); 157 synchronized (handles) 158 { 159 handles.add(handle); 160 } 161 } 162 163 public PrintWriter getLogWriter() throws ResourceException 164 { 165 return null; 167 } 168 169 public ManagedConnectionMetaData getMetaData() throws ResourceException 170 { 171 return null; 173 } 174 175 public void setLogWriter(PrintWriter param1) throws ResourceException 176 { 177 } 179 180 public void cleanup() throws ResourceException 181 { 182 synchronized (handles) 183 { 184 for (Iterator i = handles.iterator(); i.hasNext();) 185 { 186 WrappedConnection lc = (WrappedConnection) i.next(); 187 lc.setManagedConnection(null); 188 } 189 handles.clear(); 190 } 191 synchronized (stateLock) 193 { 194 jdbcAutoCommit = true; 195 jdbcReadOnly = readOnly; 196 if (jdbcTransactionIsolation != transactionIsolation) 197 { 198 try 199 { 200 con.setTransactionIsolation(jdbcTransactionIsolation); 201 jdbcTransactionIsolation = transactionIsolation; 202 } 203 catch (SQLException e) 204 { 205 mcf.log.warn("Error resetting transaction isolation ", e); 206 } 207 } 208 } 209 } 210 211 public Object getConnection(Subject subject, ConnectionRequestInfo cri) throws ResourceException 212 { 213 checkIdentity(subject, cri); 214 WrappedConnection lc = new WrappedConnection(this); 215 synchronized (handles) 216 { 217 handles.add(lc); 218 } 219 return lc; 220 } 221 222 public void destroy() throws ResourceException 223 { 224 synchronized (stateLock) 225 { 226 destroyed = true; 227 } 228 229 cleanup(); 230 try 231 { 232 con.close(); 233 } 234 catch (SQLException ignored) 235 { 236 getLog().trace("Ignored error during close: ", ignored); 237 } 238 } 239 240 public boolean checkValid() 241 { 242 SQLException e = mcf.isValidConnection(con); 243 244 if (e == null) 245 return true; 247 else 248 { 249 getLog().warn("Destroying connection that is not valid, due to the following exception: " + con, e); 250 broadcastConnectionError(e); 251 return false; 252 } 253 } 254 255 public Properties getProperties() 256 { 257 return this.props; 258 259 } 260 261 void closeHandle(WrappedConnection handle) 262 { 263 synchronized (stateLock) 264 { 265 if (destroyed) 266 return; 267 } 268 269 synchronized (handles) 270 { 271 handles.remove(handle); 272 } 273 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.CONNECTION_CLOSED); 274 ce.setConnectionHandle(handle); 275 Collection copy = null; 276 synchronized (cels) 277 { 278 copy = new ArrayList (cels); 279 } 280 for (Iterator i = copy.iterator(); i.hasNext();) 281 { 282 ConnectionEventListener cel = (ConnectionEventListener ) i.next(); 283 cel.connectionClosed(ce); 284 } 285 } 286 287 Throwable connectionError(Throwable t) 288 { 289 if(t instanceof SQLException ) 290 { 291 if(mcf.isStaleConnection((SQLException )t)) 292 { 293 t = new StaleConnectionException((SQLException )t); 294 295 }else 296 { 297 if(mcf.isExceptionFatal((SQLException )t)) 298 { 299 broadcastConnectionError(t); 300 301 } 302 } 303 } 304 else 305 { 306 broadcastConnectionError(t); 307 } 308 309 return t; 310 } 311 312 313 protected void broadcastConnectionError(Throwable e) 314 { 315 synchronized (stateLock) 316 { 317 if (destroyed) 318 { 319 Logger log = getLog(); 320 if (log.isTraceEnabled()) 321 log.trace("Not broadcasting error, already destroyed " + this, e); 322 return; 323 } 324 } 325 326 Exception ex = null; 327 if (e instanceof Exception ) 328 ex = (Exception ) e; 329 else 330 ex = new ResourceAdapterInternalException ("Unexpected error", e); 331 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, ex); 332 Collection copy = null; 333 synchronized (cels) 334 { 335 copy = new ArrayList (cels); 336 } 337 for (Iterator i = copy.iterator(); i.hasNext();) 338 { 339 ConnectionEventListener cel = (ConnectionEventListener ) i.next(); 340 try 341 { 342 cel.connectionErrorOccurred(ce); 343 } 344 catch (Throwable t) 345 { 346 getLog().warn("Error notifying of connection error for listener: " + cel, t); 347 } 348 } 349 } 350 351 Connection getConnection() throws SQLException 352 { 353 if (con == null) 354 throw new SQLException ("Connection has been destroyed!!!"); 355 return con; 356 } 357 358 PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException 359 { 360 if (psCache != null) 361 { 362 PreparedStatementCache.Key key = new PreparedStatementCache.Key(sql, 363 PreparedStatementCache.Key.PREPARED_STATEMENT, resultSetType, resultSetConcurrency); 364 CachedPreparedStatement cachedps = (CachedPreparedStatement) psCache.get(key); 365 if (cachedps != null) 366 { 367 if (canUse(cachedps)) 368 cachedps.inUse(); 369 else 370 return doPrepareStatement(sql, resultSetType, resultSetConcurrency); 371 } 372 else 373 { 374 PreparedStatement ps = doPrepareStatement(sql, resultSetType, resultSetConcurrency); 375 cachedps = new CachedPreparedStatement(ps); 376 psCache.insert(key, cachedps); 377 } 378 return cachedps; 379 } 380 else 381 return doPrepareStatement(sql, resultSetType, resultSetConcurrency); 382 } 383 384 PreparedStatement doPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException 385 { 386 return con.prepareStatement(sql, resultSetType, resultSetConcurrency); 387 } 388 389 CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException 390 { 391 if (psCache != null) 392 { 393 PreparedStatementCache.Key key = new PreparedStatementCache.Key(sql, 394 PreparedStatementCache.Key.CALLABLE_STATEMENT, resultSetType, resultSetConcurrency); 395 CachedCallableStatement cachedps = (CachedCallableStatement) psCache.get(key); 396 if (cachedps != null) 397 { 398 if (canUse(cachedps)) 399 cachedps.inUse(); 400 else 401 return doPrepareCall(sql, resultSetType, resultSetConcurrency); 402 } 403 else 404 { 405 CallableStatement cs = doPrepareCall(sql, resultSetType, resultSetConcurrency); 406 cachedps = new CachedCallableStatement(cs); 407 psCache.insert(key, cachedps); 408 } 409 return cachedps; 410 } 411 else 412 return doPrepareCall(sql, resultSetType, resultSetConcurrency); 413 } 414 415 CallableStatement doPrepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException 416 { 417 return con.prepareCall(sql, resultSetType, resultSetConcurrency); 418 } 419 420 boolean canUse(CachedPreparedStatement cachedps) 421 { 422 if (cachedps.isInUse() == false) 424 return true; 425 426 if (underlyingAutoCommit == true) 429 return false; 430 431 return mcf.sharePS; 433 } 434 435 protected Logger getLog() 436 { 437 return mcf.log; 438 } 439 440 private void checkIdentity(Subject subject, ConnectionRequestInfo cri) throws ResourceException 441 { 442 Properties newProps = mcf.getConnectionProperties(subject, cri); 443 if (!props.equals(newProps)) 444 { 445 throw new JBossResourceException("Wrong credentials passed to getConnection!"); 446 } } 448 449 456 void checkTransaction() throws SQLException 457 { 458 synchronized (stateLock) 459 { 460 if (inManagedTransaction) 461 return; 462 463 if (jdbcAutoCommit != underlyingAutoCommit) 465 { 466 con.setAutoCommit(jdbcAutoCommit); 467 underlyingAutoCommit = jdbcAutoCommit; 468 } 469 } 470 471 if (jdbcAutoCommit == false && inLocalTransaction.set(true) == false) 472 { 473 ArrayList copy; 474 synchronized (cels) 475 { 476 copy = new ArrayList (cels); 477 } 478 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.LOCAL_TRANSACTION_STARTED); 479 for (int i = 0; i < copy.size(); ++i) 480 { 481 ConnectionEventListener cel = (ConnectionEventListener ) copy.get(i); 482 try 483 { 484 cel.localTransactionStarted(ce); 485 } 486 catch (Throwable t) 487 { 488 getLog().trace("Error notifying of connection committed for listener: " + cel, t); 489 } 490 } 491 } 492 493 checkState(); 494 } 495 496 protected void checkState() throws SQLException 497 { 498 synchronized (stateLock) 499 { 500 if (jdbcReadOnly != underlyingReadOnly) 502 { 503 con.setReadOnly(jdbcReadOnly); 504 underlyingReadOnly = jdbcReadOnly; 505 } 506 } 507 } 508 509 boolean isJdbcAutoCommit() 510 { 511 return inManagedTransaction ? false : jdbcAutoCommit; 512 } 513 514 void setJdbcAutoCommit(final boolean jdbcAutoCommit) throws SQLException 515 { 516 synchronized (stateLock) 517 { 518 if (inManagedTransaction) 519 throw new SQLException ("You cannot set autocommit during a managed transaction!"); 520 this.jdbcAutoCommit = jdbcAutoCommit; 521 } 522 523 if (jdbcAutoCommit && inLocalTransaction.set(false)) 524 { 525 ArrayList copy; 526 synchronized (cels) 527 { 528 copy = new ArrayList (cels); 529 } 530 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED); 531 for (int i = 0; i < copy.size(); ++i) 532 { 533 ConnectionEventListener cel = (ConnectionEventListener ) copy.get(i); 534 try 535 { 536 cel.localTransactionCommitted(ce); 537 } 538 catch (Throwable t) 539 { 540 getLog().trace("Error notifying of connection committed for listener: " + cel, t); 541 } 542 } 543 } 544 } 545 546 boolean isJdbcReadOnly() 547 { 548 return jdbcReadOnly; 549 } 550 551 void setJdbcReadOnly(final boolean readOnly) throws SQLException 552 { 553 synchronized (stateLock) 554 { 555 if (inManagedTransaction) 556 throw new SQLException ("You cannot set read only during a managed transaction!"); 557 this.jdbcReadOnly = readOnly; 558 } 559 } 560 561 int getJdbcTransactionIsolation() 562 { 563 return jdbcTransactionIsolation; 564 } 565 566 void setJdbcTransactionIsolation(final int isolationLevel) throws SQLException 567 { 568 synchronized (stateLock) 569 { 570 this.jdbcTransactionIsolation = isolationLevel; 571 con.setTransactionIsolation(jdbcTransactionIsolation); 572 } 573 } 574 575 void jdbcCommit() throws SQLException 576 { 577 synchronized (stateLock) 578 { 579 if (inManagedTransaction) 580 throw new SQLException ("You cannot commit during a managed transaction!"); 581 if (jdbcAutoCommit) 582 throw new SQLException ("You cannot commit with autocommit set!"); 583 } 584 con.commit(); 585 586 if (inLocalTransaction.set(false)) 587 { 588 ArrayList copy; 589 synchronized (cels) 590 { 591 copy = new ArrayList (cels); 592 } 593 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED); 594 for (int i = 0; i < copy.size(); ++i) 595 { 596 ConnectionEventListener cel = (ConnectionEventListener ) copy.get(i); 597 try 598 { 599 cel.localTransactionCommitted(ce); 600 } 601 catch (Throwable t) 602 { 603 getLog().trace("Error notifying of connection committed for listener: " + cel, t); 604 } 605 } 606 } 607 } 608 609 void jdbcRollback() throws SQLException 610 { 611 synchronized (stateLock) 612 { 613 if (inManagedTransaction) 614 throw new SQLException ("You cannot rollback during a managed transaction!"); 615 if (jdbcAutoCommit) 616 throw new SQLException ("You cannot rollback with autocommit set!"); 617 } 618 con.rollback(); 619 620 if (inLocalTransaction.set(false)) 621 { 622 ArrayList copy; 623 synchronized (cels) 624 { 625 copy = new ArrayList (cels); 626 } 627 ConnectionEvent ce = new ConnectionEvent (this, ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK); 628 for (int i = 0; i < copy.size(); ++i) 629 { 630 ConnectionEventListener cel = (ConnectionEventListener ) copy.get(i); 631 try 632 { 633 cel.localTransactionRolledback(ce); 634 } 635 catch (Throwable t) 636 { 637 getLog().trace("Error notifying of connection rollback for listener: " + cel, t); 638 } 639 } 640 } 641 } 642 643 void jdbcRollback(Savepoint savepoint) throws SQLException 644 { 645 synchronized (stateLock) 646 { 647 if (inManagedTransaction) 648 throw new SQLException ("You cannot rollback during a managed transaction!"); 649 if (jdbcAutoCommit) 650 throw new SQLException ("You cannot rollback with autocommit set!"); 651 } 652 con.rollback(savepoint); 653 } 654 655 int getTrackStatements() 656 { 657 return mcf.trackStatements; 658 } 659 660 boolean isTransactionQueryTimeout() 661 { 662 return mcf.isTransactionQueryTimeout; 663 } 664 665 int getQueryTimeout() 666 { 667 return mcf.getQueryTimeout(); 668 } 669 670 protected void checkException(SQLException e) throws ResourceException 671 { 672 connectionError(e); 673 throw new JBossResourceException("SQLException", e); 674 } 675 } 676 | Popular Tags |