1 18 package org.ofbiz.entity.transaction; 19 20 import java.sql.Connection ; 21 import java.sql.SQLException ; 22 import java.sql.Timestamp ; 23 import java.util.HashMap ; 24 import java.util.Iterator ; 25 import java.util.LinkedList ; 26 import java.util.List ; 27 import java.util.Map ; 28 import javax.sql.XAConnection ; 29 import javax.transaction.*; 30 import javax.transaction.xa.XAException ; 31 import javax.transaction.xa.XAResource ; 32 33 import org.ofbiz.base.util.Debug; 34 import org.ofbiz.base.util.UtilDateTime; 35 import org.ofbiz.base.util.UtilValidate; 36 37 45 public class TransactionUtil implements Status { 46 public static final String module = TransactionUtil.class.getName(); 48 public static Map debugResMap = new HashMap (); 49 public static boolean debugResources = true; 50 51 56 public static boolean begin() throws GenericTransactionException { 57 return begin(0); 58 } 59 60 65 public static synchronized boolean begin(int timeout) throws GenericTransactionException { 66 UserTransaction ut = TransactionFactory.getUserTransaction(); 67 if (ut != null) { 68 try { 69 int currentStatus = ut.getStatus(); 70 Debug.logVerbose("[TransactionUtil.begin] current status : " + getTransactionStateString(currentStatus), module); 71 if (currentStatus == Status.STATUS_ACTIVE) { 72 Debug.logVerbose("[TransactionUtil.begin] active transaction in place, so no transaction begun", module); 73 return false; 74 } else if (currentStatus == Status.STATUS_MARKED_ROLLBACK) { 75 Exception e = getTransactionBeginStack(); 76 if (e != null) { 77 Debug.logWarning(e, "[TransactionUtil.begin] active transaction marked for rollback in place, so no transaction begun; this stack trace shows when the exception began: ", module); 78 } else { 79 Debug.logWarning("[TransactionUtil.begin] active transaction marked for rollback in place, so no transaction begun", module); 80 } 81 82 RollbackOnlyCause roc = getSetRollbackOnlyCause(); 83 if (roc != null && !roc.isEmpty()) { 85 throw new GenericTransactionException("The current transaction is marked for rollback, not beginning a new transaction and aborting current operation; the rollbackOnly was caused by: " + roc.getCauseMessage(), roc.getCauseThrowable()); 86 } else { 87 return false; 88 } 89 } 90 91 if (timeout > 0) { 93 ut.setTransactionTimeout(timeout); 94 Debug.logVerbose("[TransactionUtil.begin] set transaction timeout to : " + timeout + " seconds", module); 95 } 96 97 ut.begin(); 99 Debug.logVerbose("[TransactionUtil.begin] transaction begun", module); 100 101 if (timeout > 0) { 103 ut.setTransactionTimeout(0); 104 } 105 106 clearTransactionStamps(); 108 getTransactionStartStamp(); 110 setTransactionBeginStack(); 112 113 if (debugResources) { 115 DebugXaResource dxa = new DebugXaResource(); 116 try { 117 dxa.enlist(); 118 } catch (XAException e) { 119 Debug.logError(e, module); 120 } 121 } 122 123 return true; 124 } catch (NotSupportedException e) { 125 throw new GenericTransactionException("Not Supported error, could not begin transaction (probably a nesting problem)", e); 127 } catch (SystemException e) { 128 throw new GenericTransactionException("System error, could not begin transaction", e); 130 } 131 } else { 132 Debug.logInfo("[TransactionUtil.begin] no user transaction, so no transaction begun", module); 133 return false; 134 } 135 } 136 137 139 public static int getStatus() throws GenericTransactionException { 140 UserTransaction ut = TransactionFactory.getUserTransaction(); 141 if (ut != null) { 142 try { 143 return ut.getStatus(); 144 } catch (SystemException e) { 145 throw new GenericTransactionException("System error, could not get status", e); 146 } 147 } else { 148 return STATUS_NO_TRANSACTION; 149 } 150 } 151 152 public static boolean isTransactionInPlace() throws GenericTransactionException { 153 int status = getStatus(); 154 if (status == STATUS_NO_TRANSACTION) { 155 return false; 156 } else { 157 return true; 158 } 159 } 160 161 162 165 public static void commit(boolean beganTransaction) throws GenericTransactionException { 166 if (beganTransaction) { 167 TransactionUtil.commit(); 168 } 169 } 170 171 172 public static void commit() throws GenericTransactionException { 173 UserTransaction ut = TransactionFactory.getUserTransaction(); 174 175 if (ut != null) { 176 try { 177 int status = ut.getStatus(); 178 Debug.logVerbose("[TransactionUtil.commit] current status : " + getTransactionStateString(status), module); 179 180 if (status != STATUS_NO_TRANSACTION) { 181 ut.commit(); 182 183 clearTransactionStamps(); 185 clearTransactionBeginStack(); 187 clearSetRollbackOnlyCause(); 188 189 Debug.logVerbose("[TransactionUtil.commit] transaction committed", module); 190 } else { 191 Debug.logInfo("[TransactionUtil.commit] Not committing transaction, status is STATUS_NO_TRANSACTION", module); 192 } 193 } catch (RollbackException e) { 194 RollbackOnlyCause rollbackOnlyCause = getSetRollbackOnlyCause(); 195 196 if (rollbackOnlyCause != null) { 197 clearTransactionStamps(); 199 clearTransactionBeginStack(); 200 clearSetRollbackOnlyCause(); 201 202 Debug.logError(e, "Rollback Only was set when trying to commit transaction here; throwing rollbackOnly cause exception", module); 203 throw new GenericTransactionException("Roll back error, could not commit transaction, was rolled back instead because of: " + rollbackOnlyCause.getCauseMessage(), rollbackOnlyCause.getCauseThrowable()); 204 } else { 205 Throwable t = e.getCause() == null ? e : e.getCause(); 206 throw new GenericTransactionException("Roll back error (with no rollbackOnly cause found), could not commit transaction, was rolled back instead: " + t.toString(), t); 207 } 208 } catch (HeuristicMixedException e) { 209 Throwable t = e.getCause() == null ? e : e.getCause(); 210 throw new GenericTransactionException("Could not commit transaction, HeuristicMixed exception: " + t.toString(), t); 211 } catch (HeuristicRollbackException e) { 212 Throwable t = e.getCause() == null ? e : e.getCause(); 213 throw new GenericTransactionException("Could not commit transaction, HeuristicRollback exception: " + t.toString(), t); 214 } catch (SystemException e) { 215 Throwable t = e.getCause() == null ? e : e.getCause(); 216 throw new GenericTransactionException("System error, could not commit transaction: " + t.toString(), t); 217 } 218 } else { 219 Debug.logInfo("[TransactionUtil.commit] UserTransaction is null, not commiting", module); 220 } 221 } 222 223 224 public static void rollback(boolean beganTransaction) throws GenericTransactionException { 225 Debug.logWarning("WARNING: called rollback without debug/error info; it is recommended to always pass this to make otherwise tricky bugs much easier to track down.", module); 226 rollback(beganTransaction, null, null); 227 } 228 229 233 public static void rollback(boolean beganTransaction, String causeMessage, Throwable causeThrowable) throws GenericTransactionException { 234 if (beganTransaction) { 235 TransactionUtil.rollback(); 236 } else { 237 TransactionUtil.setRollbackOnly(causeMessage, causeThrowable); 238 } 239 } 240 241 242 public static void rollback() throws GenericTransactionException { 243 UserTransaction ut = TransactionFactory.getUserTransaction(); 244 245 if (ut != null) { 246 try { 247 int status = ut.getStatus(); 248 Debug.logVerbose("[TransactionUtil.rollback] current status : " + getTransactionStateString(status), module); 249 250 if (status != STATUS_NO_TRANSACTION) { 251 if (Debug.infoOn()) { 253 Exception newE = new Exception ("Stack Trace"); 254 Debug.logError(newE, "[TransactionUtil.rollback]", module); 255 } 256 257 clearTransactionStamps(); 259 clearTransactionBeginStack(); 261 clearSetRollbackOnlyCause(); 262 263 ut.rollback(); 264 Debug.logInfo("[TransactionUtil.rollback] transaction rolled back", module); 265 } else { 266 Debug.logInfo("[TransactionUtil.rollback] transaction not rolled back, status is STATUS_NO_TRANSACTION", module); 267 } 268 } catch (SystemException e) { 269 throw new GenericTransactionException("System error, could not rollback transaction", e); 271 } 272 } else { 273 Debug.logInfo("[TransactionUtil.rollback] No UserTransaction, transaction not rolled back", module); 274 } 275 } 276 277 278 public static void setRollbackOnly(String causeMessage, Throwable causeThrowable) throws GenericTransactionException { 279 UserTransaction ut = TransactionFactory.getUserTransaction(); 280 if (ut != null) { 281 try { 282 int status = ut.getStatus(); 283 Debug.logVerbose("[TransactionUtil.setRollbackOnly] current code : " + getTransactionStateString(status), module); 284 285 if (status != STATUS_NO_TRANSACTION) { 286 if (status != STATUS_MARKED_ROLLBACK) { 287 if (Debug.warningOn()) Debug.logWarning(new Exception (causeMessage), "[TransactionUtil.setRollbackOnly] Calling transaction setRollbackOnly; this stack trace shows where this is happening:", module); 288 ut.setRollbackOnly(); 289 setSetRollbackOnlyCause(causeMessage, causeThrowable); 290 } else { 291 Debug.logInfo("[TransactionUtil.setRollbackOnly] transaction rollback only not set, rollback only is already set.", module); 292 } 293 } else { 294 Debug.logInfo("[TransactionUtil.setRollbackOnly] transaction rollback only not set, status is STATUS_NO_TRANSACTION", module); 295 } 296 } catch (SystemException e) { 297 throw new GenericTransactionException("System error, could not set rollback only on transaction", e); 299 } 300 } else { 301 Debug.logInfo("[TransactionUtil.setRollbackOnly] No UserTransaction, transaction rollback only not set", module); 302 } 303 } 304 305 public static Transaction suspend() throws GenericTransactionException { 306 try { 307 if (TransactionUtil.getStatus() == TransactionUtil.STATUS_ACTIVE) { 308 TransactionManager txMgr = TransactionFactory.getTransactionManager(); 309 if (txMgr != null ) { 310 pushTransactionBeginStackSave(clearTransactionBeginStack()); 311 pushSetRollbackOnlyCauseSave(clearSetRollbackOnlyCause()); 312 Transaction trans = txMgr.suspend(); 313 pushSuspendedTransaction(trans); 314 return trans; 315 } else { 316 return null; 317 } 318 } else { 319 Debug.logWarning("No transaction active, so not suspending.", module); 320 return null; 321 } 322 } catch (SystemException e) { 323 throw new GenericTransactionException("System error, could not suspend transaction", e); 324 } 325 } 326 327 public static void resume(Transaction parentTx) throws GenericTransactionException { 328 if (parentTx == null) return; 329 try { 330 TransactionManager txMgr = TransactionFactory.getTransactionManager(); 331 if (txMgr != null ) { 332 setTransactionBeginStack(popTransactionBeginStackSave()); 333 setSetRollbackOnlyCause(popSetRollbackOnlyCauseSave()); 334 txMgr.resume(parentTx); 335 removeSuspendedTransaction(parentTx); 336 } 337 } catch (InvalidTransactionException e) { 338 350 throw new GenericTransactionException("System error, could not resume transaction", e); 351 } catch (SystemException e) { 352 throw new GenericTransactionException("System error, could not resume transaction", e); 353 } 354 } 355 356 357 public static void setTransactionTimeout(int seconds) throws GenericTransactionException { 358 UserTransaction ut = TransactionFactory.getUserTransaction(); 359 if (ut != null) { 360 try { 361 ut.setTransactionTimeout(seconds); 362 } catch (SystemException e) { 363 throw new GenericTransactionException("System error, could not set transaction timeout", e); 364 } 365 } 366 } 367 368 369 public static Connection enlistConnection(XAConnection xacon) throws GenericTransactionException { 370 if (xacon == null) { 371 return null; 372 } 373 try { 374 XAResource resource = xacon.getXAResource(); 375 TransactionUtil.enlistResource(resource); 376 return xacon.getConnection(); 377 } catch (SQLException e) { 378 throw new GenericTransactionException("SQL error, could not enlist connection in transaction even though transactions are available", e); 379 } 380 } 381 382 public static void enlistResource(XAResource resource) throws GenericTransactionException { 383 if (resource == null) { 384 return; 385 } 386 387 try { 388 TransactionManager tm = TransactionFactory.getTransactionManager(); 389 if (tm != null && tm.getStatus() == STATUS_ACTIVE) { 390 Transaction tx = tm.getTransaction(); 391 if (tx != null) { 392 tx.enlistResource(resource); 393 } 394 } 395 } catch (RollbackException e) { 396 throw new GenericTransactionException("Roll Back error, could not enlist resource in transaction even though transactions are available, current transaction rolled back", e); 398 } catch (SystemException e) { 399 throw new GenericTransactionException("System error, could not enlist resource in transaction even though transactions are available", e); 401 } 402 } 403 404 public static String getTransactionStateString(int state) { 405 switch (state) { 406 case Status.STATUS_ACTIVE: 407 return "Transaction Active (" + state + ")"; 408 case Status.STATUS_COMMITTED: 409 return "Transaction Committed (" + state + ")"; 410 case Status.STATUS_COMMITTING: 411 return "Transaction Committing (" + state + ")"; 412 case Status.STATUS_MARKED_ROLLBACK: 413 return "Transaction Marked Rollback (" + state + ")"; 414 case Status.STATUS_NO_TRANSACTION: 415 return "No Transaction (" + state + ")"; 416 case Status.STATUS_PREPARED: 417 return "Transaction Prepared (" + state + ")"; 418 case Status.STATUS_PREPARING: 419 return "Transaction Preparing (" + state + ")"; 420 case Status.STATUS_ROLLEDBACK: 421 return "Transaction Rolledback (" + state + ")"; 422 case Status.STATUS_ROLLING_BACK: 423 return "Transaction Rolling Back (" + state + ")"; 424 case Status.STATUS_UNKNOWN: 425 return "Transaction Status Unknown (" + state + ")"; 426 default: 427 return "Not a valid state code (" + state + ")"; 428 } 429 } 430 431 public static void logRunningTx() { 432 if (debugResources) { 433 if (debugResMap != null && debugResMap.size() > 0) { 434 Iterator i = debugResMap.keySet().iterator(); 435 while (i.hasNext()) { 436 Object o = i.next(); 437 DebugXaResource dxa = (DebugXaResource) debugResMap.get(o); 438 dxa.log(); 439 } 440 } 441 } 442 } 443 444 public static void registerSynchronization(Synchronization sync) throws GenericTransactionException { 445 if (sync == null) { 446 return; 447 } 448 449 try { 450 TransactionManager tm = TransactionFactory.getTransactionManager(); 451 if (tm != null && tm.getStatus() == STATUS_ACTIVE) { 452 Transaction tx = tm.getTransaction(); 453 if (tx != null) { 454 tx.registerSynchronization(sync); 455 } 456 } 457 } catch (RollbackException e) { 458 throw new GenericTransactionException("Roll Back error, could not register synchronization in transaction even though transactions are available, current transaction rolled back", e); 460 } catch (SystemException e) { 461 throw new GenericTransactionException("System error, could not register synchronization in transaction even though transactions are available", e); 463 } 464 } 465 466 private static ThreadLocal suspendedTxStack = new ThreadLocal (); 469 470 471 public static int cleanSuspendedTransactions() throws GenericTransactionException { 472 Transaction trans = null; 473 int num = 0; 474 while ((trans = popSuspendedTransaction()) != null) { 475 resume(trans); 476 rollback(); 477 num++; 478 } 479 return num; 480 } 481 public static boolean suspendedTransactionsHeld() { 482 List tl = (List ) suspendedTxStack.get(); 483 if (tl != null && tl.size() > 0) { 484 return true; 485 } else { 486 return false; 487 } 488 } 489 protected static void pushSuspendedTransaction(Transaction t) { 490 List tl = (List ) suspendedTxStack.get(); 491 if (tl == null) { 492 tl = new LinkedList (); 493 suspendedTxStack.set(tl); 494 } 495 tl.add(0, t); 496 } 497 protected static Transaction popSuspendedTransaction() { 498 List tl = (List ) suspendedTxStack.get(); 499 if (tl != null && tl.size() > 0) { 500 return (Transaction) tl.remove(0); 501 } else { 502 return null; 503 } 504 } 505 protected static void removeSuspendedTransaction(Transaction t) { 506 List tl = (List ) suspendedTxStack.get(); 507 if (tl != null && tl.size() > 0) { 508 tl.remove(t); 509 } 510 } 511 512 private static ThreadLocal transactionBeginStack = new ThreadLocal (); 515 private static ThreadLocal transactionBeginStackSave = new ThreadLocal (); 516 517 private static void pushTransactionBeginStackSave(Exception e) { 518 List el = (List ) transactionBeginStackSave.get(); 519 if (el == null) { 520 el = new LinkedList (); 521 transactionBeginStackSave.set(el); 522 } 523 el.add(0, e); 524 } 525 private static Exception popTransactionBeginStackSave() { 526 List el = (List ) transactionBeginStackSave.get(); 527 if (el != null && el.size() > 0) { 528 return (Exception ) el.remove(0); 529 } else { 530 return null; 531 } 532 } 533 534 private static void setTransactionBeginStack() { 535 Exception e = new Exception ("Tx Stack Placeholder"); 536 setTransactionBeginStack(e); 537 } 538 private static void setTransactionBeginStack(Exception newExc) { 539 if (transactionBeginStack.get() != null) { 540 Exception e = (Exception ) transactionBeginStack.get(); 541 Debug.logWarning(e, "WARNING: In setTransactionBeginStack a stack placeholder was already in place, here is where the transaction began: ", module); 542 Exception e2 = new Exception ("Current Stack Trace"); 543 Debug.logWarning(e2, "WARNING: In setTransactionBeginStack a stack placeholder was already in place, here is the current location: ", module); 544 } 545 transactionBeginStack.set(newExc); 546 } 547 private static Exception clearTransactionBeginStack() { 548 Exception e = (Exception ) transactionBeginStack.get(); 549 if (e == null) { 550 Exception e2 = new Exception ("Current Stack Trace"); 551 Debug.logWarning(e2, "WARNING: In clearTransactionBeginStack no stack placeholder was in place, here is the current location: ", module); 552 return null; 553 } else { 554 transactionBeginStack.set(null); 555 return e; 556 } 557 } 558 public static Exception getTransactionBeginStack() { 559 Exception e = (Exception ) transactionBeginStack.get(); 560 if (e == null) { 561 Exception e2 = new Exception ("Current Stack Trace"); 562 Debug.logWarning(e2, "WARNING: In getTransactionBeginStack no stack placeholder was in place, here is the current location: ", module); 563 } 564 return e; 565 } 566 567 private static class RollbackOnlyCause { 570 protected String causeMessage; 571 protected Throwable causeThrowable; 572 public RollbackOnlyCause(String causeMessage, Throwable causeThrowable) { 573 this.causeMessage = causeMessage; 574 this.causeThrowable = causeThrowable; 575 } 576 public String getCauseMessage() { return this.causeMessage + (this.causeThrowable == null ? "" : this.causeThrowable.toString()); } 577 public Throwable getCauseThrowable() { return this.causeThrowable; } 578 public void logError(String message) { Debug.logError(this.getCauseThrowable(), (message == null ? "" : message) + this.getCauseMessage(), module); } 579 public boolean isEmpty() { return (UtilValidate.isEmpty(this.getCauseMessage()) && this.getCauseThrowable() == null); } 580 } 581 582 private static ThreadLocal setRollbackOnlyCause = new ThreadLocal (); 583 private static ThreadLocal setRollbackOnlyCauseSave = new ThreadLocal (); 584 585 private static void pushSetRollbackOnlyCauseSave(RollbackOnlyCause e) { 586 List el = (List ) setRollbackOnlyCauseSave.get(); 587 if (el == null) { 588 el = new LinkedList (); 589 setRollbackOnlyCauseSave.set(el); 590 } 591 el.add(0, e); 592 } 593 private static RollbackOnlyCause popSetRollbackOnlyCauseSave() { 594 List el = (List ) setRollbackOnlyCauseSave.get(); 595 if (el != null && el.size() > 0) { 596 return (RollbackOnlyCause) el.remove(0); 597 } else { 598 return null; 599 } 600 } 601 602 private static void setSetRollbackOnlyCause(String causeMessage, Throwable causeThrowable) { 603 RollbackOnlyCause roc = new RollbackOnlyCause(causeMessage, causeThrowable); 604 setSetRollbackOnlyCause(roc); 605 } 606 private static void setSetRollbackOnlyCause(RollbackOnlyCause newRoc) { 607 if (setRollbackOnlyCause.get() != null) { 608 RollbackOnlyCause roc = (RollbackOnlyCause) setRollbackOnlyCause.get(); 609 roc.logError("WARNING: In setSetRollbackOnlyCause a stack placeholder was already in place, here is the original rollbackOnly cause: "); 610 Exception e2 = new Exception ("Current Stack Trace"); 611 Debug.logWarning(e2, "WARNING: In setSetRollbackOnlyCause a stack placeholder was already in place, here is the current location: ", module); 612 } 613 setRollbackOnlyCause.set(newRoc); 614 } 615 private static RollbackOnlyCause clearSetRollbackOnlyCause() { 616 RollbackOnlyCause roc = (RollbackOnlyCause) setRollbackOnlyCause.get(); 617 if (roc == null) { 618 624 return null; 625 } else { 626 setRollbackOnlyCause.set(null); 627 return roc; 628 } 629 } 630 public static RollbackOnlyCause getSetRollbackOnlyCause() { 631 if (setRollbackOnlyCause.get() == null) { 632 Exception e = new Exception ("Current Stack Trace"); 633 Debug.logWarning(e, "WARNING: In getSetRollbackOnlyCause no stack placeholder was in place, here is the current location: ", module); 634 } 635 return (RollbackOnlyCause) setRollbackOnlyCause.get(); 636 } 637 638 private static ThreadLocal transactionStartStamp = new ThreadLocal (); 641 private static ThreadLocal transactionLastNowStamp = new ThreadLocal (); 642 643 public static Timestamp getTransactionStartStamp() { 644 Timestamp curStamp = (Timestamp ) transactionStartStamp.get(); 645 if (curStamp == null) { 646 curStamp = UtilDateTime.nowTimestamp(); 647 transactionStartStamp.set(curStamp); 648 649 try { 651 registerSynchronization(new StampClearSync()); 652 } catch (GenericTransactionException e) { 653 Debug.logError(e, "Error registering StampClearSync synchronization, stamps will still be reset if begin/commit/rollback are call through TransactionUtil, but not if otherwise", module); 654 } 655 } 656 return curStamp; 657 } 658 659 public static Timestamp getTransactionUniqueNowStamp() { 660 Timestamp lastNowStamp = (Timestamp ) transactionLastNowStamp.get(); 661 Timestamp nowTimestamp = UtilDateTime.nowTimestamp(); 662 663 if (lastNowStamp != null && (lastNowStamp.equals(nowTimestamp) || lastNowStamp.after(nowTimestamp))) { 665 nowTimestamp = new Timestamp (lastNowStamp.getTime() + 1); 666 } 667 668 transactionLastNowStamp.set(nowTimestamp); 669 return nowTimestamp; 670 } 671 672 protected static void clearTransactionStamps() { 673 transactionStartStamp.set(null); 674 transactionLastNowStamp.set(null); 675 } 676 677 public static class StampClearSync implements Synchronization { 678 public void afterCompletion(int status) { 679 TransactionUtil.clearTransactionStamps(); 680 } 681 682 public void beforeCompletion() { 683 } 684 } 685 } 686 | Popular Tags |