1 56 package org.objectstyle.cayenne.access; 57 58 import java.sql.Connection ; 59 import java.sql.SQLException ; 60 import java.util.ArrayList ; 61 import java.util.Collection ; 62 import java.util.Iterator ; 63 import java.util.List ; 64 65 import org.apache.log4j.Level; 66 import org.apache.log4j.Logger; 67 import org.objectstyle.cayenne.CayenneException; 68 import org.objectstyle.cayenne.CayenneRuntimeException; 69 import org.objectstyle.cayenne.query.QueryExecutionPlan; 70 71 77 public abstract class Transaction { 78 private static final Logger logObj = Logger.getLogger(Transaction.class); 79 80 private static final Transaction NO_TRANSACTION = new Transaction() { 81 public void begin() { 82 83 } 84 85 public void addConnection(Connection connection) { 86 87 } 88 89 public void commit() { 90 91 } 92 93 public void rollback() { 94 95 } 96 }; 97 98 public static final int STATUS_ACTIVE = 1; 99 public static final int STATUS_COMMITTING = 2; 100 public static final int STATUS_COMMITTED = 3; 101 public static final int STATUS_ROLLEDBACK = 4; 102 public static final int STATUS_ROLLING_BACK = 5; 103 public static final int STATUS_NO_TRANSACTION = 6; 104 public static final int STATUS_MARKED_ROLLEDBACK = 7; 105 106 protected List connections; 107 protected int status; 108 protected TransactionDelegate delegate; 109 protected Level logLevel; 110 111 static String decodeStatus(int status) { 112 switch (status) { 113 case STATUS_ACTIVE : 114 return "STATUS_ACTIVE"; 115 case STATUS_COMMITTING : 116 return "STATUS_COMMITTING"; 117 case STATUS_COMMITTED : 118 return "STATUS_COMMITTED"; 119 case STATUS_ROLLEDBACK : 120 return "STATUS_ROLLEDBACK"; 121 case STATUS_ROLLING_BACK : 122 return "STATUS_ROLLING_BACK"; 123 case STATUS_NO_TRANSACTION : 124 return "STATUS_NO_TRANSACTION"; 125 case STATUS_MARKED_ROLLEDBACK : 126 return "STATUS_MARKED_ROLLEDBACK"; 127 default : 128 return "Unknown Status - " + status; 129 } 130 } 131 132 137 public static Transaction internalTransaction(TransactionDelegate delegate) { 138 return new InternalTransaction(delegate); 139 } 140 141 146 public static Transaction externalTransaction(TransactionDelegate delegate) { 147 return new ExternalTransaction(delegate); 148 } 149 150 155 public static Transaction noTransaction() { 156 return NO_TRANSACTION; 157 } 158 159 162 protected Transaction() { 163 status = STATUS_NO_TRANSACTION; 164 } 165 166 172 public void performQueries( 173 QueryEngine engine, 174 Collection queries, 175 OperationObserver observer) 176 throws CayenneRuntimeException { 177 178 try { 179 engine.performQueries(queries, observer, this); 181 182 if (!observer.isIteratedResult() 186 && (getStatus() == Transaction.STATUS_ACTIVE)) { 187 commit(); 188 } 189 } 190 catch (Exception ex) { 191 setRollbackOnly(); 192 193 if (ex instanceof CayenneRuntimeException) { 195 throw (CayenneRuntimeException) ex; 196 } 197 else { 198 throw new CayenneRuntimeException(ex); 199 } 200 } 201 finally { 202 if (getStatus() == Transaction.STATUS_MARKED_ROLLEDBACK) { 203 try { 204 rollback(); 205 } 206 catch (Exception rollbackEx) { 207 } 208 } 209 } 210 } 211 212 213 220 public void performQuery( 221 PersistenceContext context, 222 QueryExecutionPlan query, 223 OperationObserver observer) 224 throws CayenneRuntimeException { 225 226 try { 227 context.performQuery(query, observer, this); 229 230 if (!observer.isIteratedResult() 234 && (getStatus() == Transaction.STATUS_ACTIVE)) { 235 commit(); 236 } 237 } 238 catch (Exception ex) { 239 setRollbackOnly(); 240 241 if (ex instanceof CayenneRuntimeException) { 243 throw (CayenneRuntimeException) ex; 244 } 245 else { 246 throw new CayenneRuntimeException(ex); 247 } 248 } 249 finally { 250 if (getStatus() == Transaction.STATUS_MARKED_ROLLEDBACK) { 251 try { 252 rollback(); 253 } 254 catch (Exception rollbackEx) { 255 } 256 } 257 } 258 } 259 260 public Level getLogLevel() { 261 return logLevel != null ? logLevel : QueryLogger.DEFAULT_LOG_LEVEL; 262 } 263 264 public void setLogLevel(Level logLevel) { 265 this.logLevel = logLevel; 266 } 267 268 public TransactionDelegate getDelegate() { 269 return delegate; 270 } 271 272 public void setDelegate(TransactionDelegate delegate) { 273 this.delegate = delegate; 274 } 275 276 public int getStatus() { 277 return status; 278 } 279 280 public synchronized void setRollbackOnly() { 281 setStatus(STATUS_MARKED_ROLLEDBACK); 282 } 283 284 public synchronized void setStatus(int status) { 285 if (delegate != null 286 && status == STATUS_MARKED_ROLLEDBACK 287 && !delegate.willMarkAsRollbackOnly(this)) { 288 return; 289 } 290 291 this.status = status; 292 } 293 294 298 public abstract void begin(); 299 300 public abstract void addConnection(Connection connection) 301 throws IllegalStateException , SQLException , CayenneException; 302 303 public abstract void commit() 304 throws IllegalStateException , SQLException , CayenneException; 305 306 public abstract void rollback() 307 throws IllegalStateException , SQLException , CayenneException; 308 309 static class ExternalTransaction extends Transaction { 313 ExternalTransaction() { 314 } 315 316 ExternalTransaction(TransactionDelegate delegate) { 317 setDelegate(delegate); 318 } 319 320 public synchronized void begin() { 321 if (status != STATUS_NO_TRANSACTION) { 322 throw new IllegalStateException ( 323 "Transaction must have 'STATUS_NO_TRANSACTION' to begin. " 324 + "Current status: " 325 + decodeStatus(status)); 326 } 327 328 status = STATUS_ACTIVE; 329 connections = new ArrayList (2); 332 } 333 334 public synchronized void addConnection(Connection connection) 335 throws IllegalStateException , SQLException , CayenneException { 336 337 if (status == STATUS_NO_TRANSACTION) { 339 begin(); 340 } 341 342 if (delegate != null && !delegate.willAddConnection(this, connection)) { 343 return; 344 } 345 346 if (status != STATUS_ACTIVE) { 347 throw new IllegalStateException ( 348 "Transaction must have 'STATUS_ACTIVE' to add a connection. " 349 + "Current status: " 350 + decodeStatus(status)); 351 } 352 353 if (!connections.contains(connection)) { 354 fixConnectionState(connection); 356 connections.add(connection); 357 } 358 } 359 360 public void commit() 361 throws IllegalStateException , SQLException , CayenneException { 362 363 if (status == STATUS_NO_TRANSACTION) { 364 return; 365 } 366 367 if (delegate != null && !delegate.willCommit(this)) { 368 return; 369 } 370 371 try { 372 if (status != STATUS_ACTIVE) { 373 throw new IllegalStateException ( 374 "Transaction must have 'STATUS_ACTIVE' to be committed. " 375 + "Current status: " 376 + decodeStatus(status)); 377 } 378 379 processCommit(); 380 381 status = STATUS_COMMITTED; 382 if (delegate != null) { 383 delegate.didCommit(this); 384 } 385 } 386 finally { 387 close(); 388 } 389 } 390 391 public void rollback() 392 throws IllegalStateException , SQLException , CayenneException { 393 394 if (status == STATUS_NO_TRANSACTION 395 || status == STATUS_ROLLEDBACK 396 || status == STATUS_ROLLING_BACK) { 397 return; 398 } 399 400 if (delegate != null && !delegate.willRollback(this)) { 401 return; 402 } 403 404 try { 405 if (status != STATUS_ACTIVE) { 406 throw new IllegalStateException ( 407 "Transaction must have 'STATUS_ACTIVE' to be rolled back. " 408 + "Current status: " 409 + decodeStatus(status)); 410 } 411 412 processRollback(); 413 414 status = STATUS_ROLLEDBACK; 415 if (delegate != null) { 416 delegate.didRollback(this); 417 } 418 } 419 finally { 420 close(); 421 } 422 } 423 424 protected void fixConnectionState(Connection connection) throws SQLException { 425 } 427 428 protected void processCommit() throws SQLException , CayenneException { 429 QueryLogger.logCommitTransaction( 430 getLogLevel(), 431 "no commit - transaction controlled externally."); 432 } 433 434 protected void processRollback() throws SQLException , CayenneException { 435 QueryLogger.logRollbackTransaction( 436 getLogLevel(), 437 "no rollback - transaction controlled externally."); 438 } 439 440 443 protected void close() { 444 if (connections == null || connections.isEmpty()) { 445 return; 446 } 447 448 Iterator it = connections.iterator(); 449 while (it.hasNext()) { 450 try { 451 452 ((Connection ) it.next()).close(); 453 } 454 catch (Throwable th) { 455 } 458 } 459 } 460 } 461 462 static class InternalTransaction extends ExternalTransaction { 464 465 InternalTransaction(TransactionDelegate delegate) { 466 super(delegate); 467 } 468 469 public void begin() { 470 super.begin(); 471 QueryLogger.logBeginTransaction(getLogLevel(), "transaction started."); 472 } 473 474 protected void fixConnectionState(Connection connection) throws SQLException { 475 if (connection.getAutoCommit()) { 476 479 481 try { 482 connection.setAutoCommit(false); 483 } 484 catch (SQLException cay179Ex) { 485 logObj.info("Can't set connection autocommit to false, ignoring. Message: " + cay179Ex.getMessage()); 486 } 487 } 488 } 489 490 protected void processCommit() throws SQLException , CayenneException { 491 status = STATUS_COMMITTING; 492 493 if (connections != null && connections.size() > 0) { 494 Throwable deferredException = null; 495 Iterator it = connections.iterator(); 496 while (it.hasNext()) { 497 Connection connection = (Connection ) it.next(); 498 try { 499 500 if (deferredException == null) { 501 connection.commit(); 502 } 503 else { 504 connection.rollback(); 507 } 508 509 } 510 catch (Throwable th) { 511 setRollbackOnly(); 515 516 deferredException = th; 519 } 520 } 521 522 if (deferredException != null) { 523 QueryLogger.logRollbackTransaction( 524 getLogLevel(), 525 "transaction rolledback."); 526 if (deferredException instanceof SQLException ) { 527 throw (SQLException ) deferredException; 528 } 529 else { 530 throw new CayenneException(deferredException); 531 } 532 } 533 else { 534 QueryLogger.logCommitTransaction( 535 getLogLevel(), 536 "transaction committed."); 537 } 538 } 539 } 540 541 protected void processRollback() throws SQLException , CayenneException { 542 status = STATUS_ROLLING_BACK; 543 544 if (connections != null && connections.size() > 0) { 545 Throwable deferredException = null; 546 547 Iterator it = connections.iterator(); 548 while (it.hasNext()) { 549 Connection connection = (Connection ) it.next(); 550 551 try { 552 connection.rollback(); 554 } 555 catch (Throwable th) { 556 deferredException = th; 559 } 560 } 561 562 if (deferredException != null) { 563 if (deferredException instanceof SQLException ) { 564 throw (SQLException ) deferredException; 565 } 566 else { 567 throw new CayenneException(deferredException); 568 } 569 } 570 } 571 } 572 } 573 } 574 | Popular Tags |