[JIRA] Closed: (OFBIZ-742) Resumed transaction gets new Start Timestamp

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[JIRA] Closed: (OFBIZ-742) Resumed transaction gets new Start Timestamp

JIRA jira@ofbiz.org
     [ http://jira.undersunconsulting.com/browse/OFBIZ-742?page=all ]
     
David E. Jones closed OFBIZ-742:
--------------------------------

      Assign To: David E. Jones  (was: Jira Administrator)
     Resolution: Fixed
    Fix Version: SVN

This is now in place in SVN rev 442396. Thanks for creating and submitting this contribution Karl.

> Resumed transaction gets new Start Timestamp
> --------------------------------------------
>
>          Key: OFBIZ-742
>          URL: http://jira.undersunconsulting.com/browse/OFBIZ-742
>      Project: [OFBiz] Open For Business
>         Type: Bug
>   Components: entity
>  Environment: Ofbiz: 5.8.6.7/6.7.0.6
>     Reporter: Karl Eilebrecht
>     Assignee: David E. Jones
>     Priority: Minor
>      Fix For: SVN
>  Attachments: TransactionUtil.java, TransactionUtil.patch
>
>
> A transaction TX1 has been suspended by a Thread.
> This thread uses now a new Transaction TX2 to do something and ends this transaction.
> After that the Thread resumes TX1 and goes on.
> At this time TX1 has a no start time stamp. The start time stamp will be set to NOW during the
> next call of the method getTransactionStartStamp() – probably unexpected by the caller.
> When does this occure?
> Usually you don’t see this, because suspending a transaction is rare in ofbiz.
> But there is one common example:
> When the bank behind getNextSeqId()-Id runs out of Ids, it gets filled using a new
> transaction (suspending the original one).
> If you store two entities in this transaction before and after the filling of the bank they
> will get two different CREATED_TX_STAMP values.
> Possible fix:
> =============
> I modified the TransactionUtils (should be ok for any versions since Build 5.8.6.7)
> ======================================================================================================
> /*
>  * $Id: TransactionUtil.java 6391 2005-12-21 00:53:00Z jonesde $
>  *
>  *  Copyright (c) 2001-2005 The Open For Business Project - www.ofbiz.org
>  *
>  *  Permission is hereby granted, free of charge, to any person obtaining a
>  *  copy of this software and associated documentation files (the "Software"),
>  *  to deal in the Software without restriction, including without limitation
>  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
>  *  and/or sell copies of the Software, and to permit persons to whom the
>  *  Software is furnished to do so, subject to the following conditions:
>  *
>  *  The above copyright notice and this permission notice shall be included
>  *  in all copies or substantial portions of the Software.
>  *
>  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
>  *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
>  *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
>  *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
>  *  OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
>  *  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>  */
> package org.ofbiz.entity.transaction;
> import java.sql.Connection;
> import java.sql.SQLException;
> import java.sql.Timestamp;
> import java.util.HashMap;
> import java.util.Iterator;
> import java.util.LinkedList;
> import java.util.List;
> import java.util.Map;
> import javax.sql.XAConnection;
> import javax.transaction.HeuristicMixedException;
> import javax.transaction.HeuristicRollbackException;
> import javax.transaction.InvalidTransactionException;
> import javax.transaction.NotSupportedException;
> import javax.transaction.RollbackException;
> import javax.transaction.Status;
> import javax.transaction.Synchronization;
> import javax.transaction.SystemException;
> import javax.transaction.Transaction;
> import javax.transaction.TransactionManager;
> import javax.transaction.UserTransaction;
> import javax.transaction.xa.XAException;
> import javax.transaction.xa.XAResource;
> import org.apache.commons.collections.map.ListOrderedMap;
> import org.ofbiz.base.util.Debug;
> import org.ofbiz.base.util.UtilDateTime;
> import org.ofbiz.base.util.UtilValidate;
> /**
>  * <p>Transaction Utility to help with some common transaction tasks
>  * <p>Provides a wrapper around the transaction objects to allow for changes in underlying implementations in the future.
>  *
>  * @author     <a href="mailto:[hidden email]">David E. Jones</a>
>  * @author     keywork/ke, 2006-02-16: fixed the "resumed transaction gets new start time stamp"-bug
>  * @version    $Rev: 6391 $
>  * @since      2.0
>  */
> public class TransactionUtil implements Status {
>     // Debug module name
>     public static final String module = TransactionUtil.class.getName();
>     public static Map debugResMap = new HashMap();
>     public static boolean debugResources = true;
>     /** Begins a transaction in the current thread IF transactions are available; only
>      * tries if the current transaction status is ACTIVE, if not active it returns false.
>      * If and on only if it begins a transaction it will return true. In other words, if
>      * a transaction is already in place it will return false and do nothing.
>      */
>     public static boolean begin() throws GenericTransactionException {
>         return begin(0);
>     }
>     /** Begins a transaction in the current thread IF transactions are available; only
>      * tries if the current transaction status is ACTIVE, if not active it returns false.
>      * If and on only if it begins a transaction it will return true. In other words, if
>      * a transaction is already in place it will return false and do nothing.
>      */
>     public static synchronized boolean begin(int timeout) throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 int currentStatus = ut.getStatus();
>                 Debug.logVerbose("[TransactionUtil.begin] current status : " + getTransactionStateString(currentStatus), module);
>                 if (currentStatus == Status.STATUS_ACTIVE) {
>                     Debug.logVerbose("[TransactionUtil.begin] active transaction in place, so no transaction begun", module);
>                     return false;
>                 } else if (currentStatus == Status.STATUS_MARKED_ROLLBACK) {
>                     Exception e = getTransactionBeginStack();
>                     if (e != null) {
>                         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);
>                     } else {
>                         Debug.logWarning("[TransactionUtil.begin] active transaction marked for rollback in place, so no transaction begun", module);
>                     }
>                     RollbackOnlyCause roc = getSetRollbackOnlyCause();
>                     // do we have a cause? if so, throw special exception
>                     if (roc != null && !roc.isEmpty()) {
>                         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());
>                     } else {
>                         return false;
>                     }
>                 }
>                 // set the timeout for THIS transaction
>                 if (timeout > 0) {
>                     ut.setTransactionTimeout(timeout);
>                     Debug.logVerbose("[TransactionUtil.begin] set transaction timeout to : " + timeout + " seconds", module);
>                 }
>                 // begin the transaction
>                 ut.begin();
>                 Debug.logVerbose("[TransactionUtil.begin] transaction begun", module);
>                 // reset the timeout to the default
>                 if (timeout > 0) {
>                     ut.setTransactionTimeout(0);
>                 }
>                 // reset the transaction stamps, just in case...
>                 clearTransactionStamps();
>                 // initialize the start stamp
>                 getTransactionStartStamp();
>                 // set the tx begin stack placeholder
>                 setTransactionBeginStack();
>                 // initialize the debug resource
>                 if (debugResources) {
>                     DebugXaResource dxa = new DebugXaResource();
>                     try {
>                         dxa.enlist();
>                     } catch (XAException e) {
>                         Debug.logError(e, module);
>                     }
>                 }
>                 return true;
>             } catch (NotSupportedException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("Not Supported error, could not begin transaction (probably a nesting problem)", e);
>             } catch (SystemException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("System error, could not begin transaction", e);
>             }
>         } else {
>             Debug.logInfo("[TransactionUtil.begin] no user transaction, so no transaction begun", module);
>             return false;
>         }
>     }
>     /** Gets the status of the transaction in the current thread IF
>      * transactions are available, otherwise returns STATUS_NO_TRANSACTION */
>     public static int getStatus() throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 return ut.getStatus();
>             } catch (SystemException e) {
>                 throw new GenericTransactionException("System error, could not get status", e);
>             }
>         } else {
>             return STATUS_NO_TRANSACTION;
>         }
>     }
>     public static boolean isTransactionInPlace() throws GenericTransactionException {
>         int status = getStatus();
>         if (status == STATUS_NO_TRANSACTION) {
>             return false;
>         } else {
>             return true;
>         }
>     }
>     /** Commits the transaction in the current thread IF transactions are available
>      *  AND if beganTransaction is true
>      */
>     public static void commit(boolean beganTransaction) throws GenericTransactionException {
>         if (beganTransaction) {
>             TransactionUtil.commit();
>         }
>     }
>     /** Commits the transaction in the current thread IF transactions are available */
>     public static void commit() throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 int status = ut.getStatus();
>                 Debug.logVerbose("[TransactionUtil.commit] current status : " + getTransactionStateString(status), module);
>                 if (status != STATUS_NO_TRANSACTION) {
>                     ut.commit();
>                     // clear out the stamps to keep it clean
>                     clearTransactionStamps();
>                     // clear out the stack too
>                     clearTransactionBeginStack();
>                     clearSetRollbackOnlyCause();
>                     Debug.logVerbose("[TransactionUtil.commit] transaction committed", module);
>                 } else {
>                     Debug.logInfo("[TransactionUtil.commit] Not committing transaction, status is STATUS_NO_TRANSACTION", module);
>                 }
>             } catch (RollbackException e) {
>                 if (Debug.infoOn()) Thread.dumpStack();
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("Roll back error, could not commit transaction, was rolled back instead", e);
>             } catch (HeuristicMixedException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("Could not commit transaction, HeuristicMixed exception", e);
>             } catch (HeuristicRollbackException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("Could not commit transaction, HeuristicRollback exception", e);
>             } catch (SystemException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("System error, could not commit transaction", e);
>             }
>         } else {
>             Debug.logInfo("[TransactionUtil.commit] UserTransaction is null, not commiting", module);
>         }
>     }
>     /** @deprecated */
>     public static void rollback(boolean beganTransaction) throws GenericTransactionException {
>         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);
>         rollback(beganTransaction, null, null);
>     }
>    
>     /** Rolls back transaction in the current thread IF transactions are available
>      *  AND if beganTransaction is true; if beganTransaction is not true,
>      *  setRollbackOnly is called to insure that the transaction will be rolled back
>      */
>     public static void rollback(boolean beganTransaction, String causeMessage, Throwable causeThrowable) throws GenericTransactionException {
>         if (beganTransaction) {
>             TransactionUtil.rollback();
>         } else {
>             TransactionUtil.setRollbackOnly(causeMessage, causeThrowable);
>         }
>     }
>     /** Rolls back transaction in the current thread IF transactions are available */
>     public static void rollback() throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 int status = ut.getStatus();
>                 Debug.logVerbose("[TransactionUtil.rollback] current status : " + getTransactionStateString(status), module);
>                 if (status != STATUS_NO_TRANSACTION) {
>                     //if (Debug.infoOn()) Thread.dumpStack();
>                     if (Debug.infoOn()) {
>                         Exception newE = new Exception("Stack Trace");
>                         Debug.logError(newE, "[TransactionUtil.rollback]", module);
>                     }
>                     // clear out the stamps to keep it clean
>                     clearTransactionStamps();
>                     // clear out the stack too
>                     clearTransactionBeginStack();
>                     clearSetRollbackOnlyCause();
>                     ut.rollback();
>                     Debug.logInfo("[TransactionUtil.rollback] transaction rolled back", module);
>                 } else {
>                     Debug.logInfo("[TransactionUtil.rollback] transaction not rolled back, status is STATUS_NO_TRANSACTION", module);
>                 }
>             } catch (SystemException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("System error, could not rollback transaction", e);
>             }
>         } else {
>             Debug.logInfo("[TransactionUtil.rollback] No UserTransaction, transaction not rolled back", module);
>         }
>     }
>     /** Makes a rollback the only possible outcome of the transaction in the current thread IF transactions are available */
>     public static void setRollbackOnly(String causeMessage, Throwable causeThrowable) throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 int status = ut.getStatus();
>                 Debug.logVerbose("[TransactionUtil.setRollbackOnly] current code : " + getTransactionStateString(status), module);
>                 if (status != STATUS_NO_TRANSACTION) {
>                     if (status != STATUS_MARKED_ROLLBACK) {
>                         if (Debug.warningOn()) Debug.logWarning(new Exception(), "[TransactionUtil.setRollbackOnly] Calling transaction setRollbackOnly; this stack trace shows where this is happening:", module);
>                         ut.setRollbackOnly();
>                         setSetRollbackOnlyCause(causeMessage, causeThrowable);
>                     } else {
>                         Debug.logInfo("[TransactionUtil.setRollbackOnly] transaction rollback only not set, rollback only is already set.", module);
>                     }
>                 } else {
>                     Debug.logInfo("[TransactionUtil.setRollbackOnly] transaction rollback only not set, status is STATUS_NO_TRANSACTION", module);
>                 }
>             } catch (SystemException e) {
>                 //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>                 throw new GenericTransactionException("System error, could not set rollback only on transaction", e);
>             }
>         } else {
>             Debug.logInfo("[TransactionUtil.setRollbackOnly] No UserTransaction, transaction rollback only not set", module);
>         }
>     }
>     public static Transaction suspend() throws GenericTransactionException {
>         try {
>             if (TransactionUtil.getStatus() == TransactionUtil.STATUS_ACTIVE) {
>                 TransactionManager txMgr = TransactionFactory.getTransactionManager();
>                 if (txMgr != null ) {
>                     pushTransactionBeginStackSave(clearTransactionBeginStack());
>                     pushSetRollbackOnlyCauseSave(clearSetRollbackOnlyCause());
>                     Transaction trans = txMgr.suspend();
>                     pushSuspendedTransaction(trans);
>                     return trans;
>                 } else {
>                     return null;
>                 }
>             } else {
>                 Debug.logWarning("No transaction active, so not suspending.", module);
>                 return null;
>             }
>         } catch (SystemException e) {
>             throw new GenericTransactionException("System error, could not suspend transaction", e);
>         }
>     }
>     public static void resume(Transaction parentTx) throws GenericTransactionException {
>         if (parentTx == null) return;
>         try {
>             TransactionManager txMgr = TransactionFactory.getTransactionManager();
>             if (txMgr != null ) {
>                 setTransactionBeginStack(popTransactionBeginStackSave());
>                 setSetRollbackOnlyCause(popSetRollbackOnlyCauseSave());
>                 txMgr.resume(parentTx);
>                 removeSuspendedTransaction(parentTx);
>             }
>         } catch (InvalidTransactionException e) {
>             /* NOTE: uncomment this for Weblogic Application Server
>             // this is a work-around for non-standard Weblogic functionality; for more information see: http://www.onjava.com/pub/a/onjava/2005/07/20/transactions.html?page=3
>             if (parentTx instanceof weblogic.transaction.ClientTransactionManager) {
>                 // WebLogic 8 and above
>                 ((weblogic.transaction.ClientTransactionManager) parentTx).forceResume(transaction);
>             } else if (parentTx instanceof weblogic.transaction.TransactionManager) {
>                 // WebLogic 7
>                 ((weblogic.transaction.TransactionManager) parentTx).forceResume(transaction);
>             } else {
>                 throw new GenericTransactionException("System error, could not resume transaction", e);
>             }
>             */
>             throw new GenericTransactionException("System error, could not resume transaction", e);
>         } catch (SystemException e) {
>             throw new GenericTransactionException("System error, could not resume transaction", e);
>         }
>     }
>     /** Sets the timeout of the transaction in the current thread IF transactions are available */
>     public static void setTransactionTimeout(int seconds) throws GenericTransactionException {
>         UserTransaction ut = TransactionFactory.getUserTransaction();
>         if (ut != null) {
>             try {
>                 ut.setTransactionTimeout(seconds);
>             } catch (SystemException e) {
>                 throw new GenericTransactionException("System error, could not set transaction timeout", e);
>             }
>         }
>     }
>     /** Enlists the given XAConnection and if a transaction is active in the current thread, returns a plain JDBC Connection */
>     public static Connection enlistConnection(XAConnection xacon) throws GenericTransactionException {
>         if (xacon == null) {
>             return null;
>         }
>         try {
>             XAResource resource = xacon.getXAResource();
>             TransactionUtil.enlistResource(resource);
>             return xacon.getConnection();
>         } catch (SQLException e) {
>             throw new GenericTransactionException("SQL error, could not enlist connection in transaction even though transactions are available", e);
>         }
>     }
>     public static void enlistResource(XAResource resource) throws GenericTransactionException {
>         if (resource == null) {
>             return;
>         }
>         try {
>             TransactionManager tm = TransactionFactory.getTransactionManager();
>             if (tm != null && tm.getStatus() == STATUS_ACTIVE) {
>                 Transaction tx = tm.getTransaction();
>                 if (tx != null) {
>                      tx.enlistResource(resource);
>                 }
>             }
>         } catch (RollbackException e) {
>             //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>             throw new GenericTransactionException("Roll Back error, could not enlist resource in transaction even though transactions are available, current transaction rolled back", e);
>         } catch (SystemException e) {
>             //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>             throw new GenericTransactionException("System error, could not enlist resource in transaction even though transactions are available", e);
>         }
>     }
>     public static String getTransactionStateString(int state) {
>         switch (state) {
>             case Status.STATUS_ACTIVE:
>                 return "Transaction Active (" + state + ")";
>             case Status.STATUS_COMMITTED:
>                 return "Transaction Committed (" + state + ")";
>             case Status.STATUS_COMMITTING:
>                 return "Transaction Committing (" + state + ")";
>             case Status.STATUS_MARKED_ROLLBACK:
>                 return "Transaction Marked Rollback (" + state + ")";
>             case Status.STATUS_NO_TRANSACTION:
>                 return "No Transaction (" + state + ")";
>             case Status.STATUS_PREPARED:
>                 return "Transaction Prepared (" + state + ")";
>             case Status.STATUS_PREPARING:
>                 return "Transaction Preparing (" + state + ")";
>             case Status.STATUS_ROLLEDBACK:
>                 return "Transaction Rolledback (" + state + ")";
>             case Status.STATUS_ROLLING_BACK:
>                 return "Transaction Rolling Back (" + state + ")";
>             case Status.STATUS_UNKNOWN:
>                 return "Transaction Status Unknown (" + state + ")";
>             default:
>                 return "Not a valid state code (" + state + ")";
>         }
>     }
>     public static void logRunningTx() {
>         if (debugResources) {
>             if (debugResMap != null && debugResMap.size() > 0) {
>                 Iterator i = debugResMap.keySet().iterator();
>                 while (i.hasNext()) {
>                     Object o = i.next();
>                     DebugXaResource dxa = (DebugXaResource) debugResMap.get(o);
>                     dxa.log();
>                 }
>             }
>         }
>     }
>     public static void registerSynchronization(Synchronization sync) throws GenericTransactionException {
>         if (sync == null) {
>             return;
>         }
>         try {
>             TransactionManager tm = TransactionFactory.getTransactionManager();
>             if (tm != null && tm.getStatus() == STATUS_ACTIVE) {
>                 Transaction tx = tm.getTransaction();
>                 if (tx != null) {
>                     tx.registerSynchronization(sync);
>                 }
>             }
>         } catch (RollbackException e) {
>             //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>             throw new GenericTransactionException("Roll Back error, could not register synchronization in transaction even though transactions are available, current transaction rolled back", e);
>         } catch (SystemException e) {
>             //This is Java 1.4 only, but useful for certain debuggins: Throwable t = e.getCause() == null ? e : e.getCause();
>             throw new GenericTransactionException("System error, could not register synchronization in transaction even though transactions are available", e);
>         }
>     }
>     // =======================================
>     // =======================================
>     private static ThreadLocal suspendedTxStack = new ThreadLocal();
>     /** BE VERY CARFUL WHERE YOU CALL THIS!! */
>     public static int cleanSuspendedTransactions() throws GenericTransactionException {
>         Transaction trans = null;
>         int num = 0;
>         while ((trans = popSuspendedTransaction()) != null) {
>             resume(trans);
>             rollback();
>             num++;
>         }
>         clearTransactionStartStampStack(); //no transactions to remember ;-)
>         return num;
>     }
>     public static boolean suspendedTransactionsHeld() {
>         List tl = (List) suspendedTxStack.get();
>         if (tl != null && tl.size() > 0) {
>             return true;
>         } else {
>             return false;
>         }
>     }
>     protected static void pushSuspendedTransaction(Transaction t) {
>         List tl = (List) suspendedTxStack.get();
>         if (tl == null) {
>             tl = new LinkedList();
>             suspendedTxStack.set(tl);
>         }
>         tl.add(0, t);
>         pushTransactionStartStamp(t); //save the current transaction start stamp
>     }
>     protected static Transaction popSuspendedTransaction() {
>         List tl = (List) suspendedTxStack.get();
>         if (tl != null && tl.size() > 0) {
>         popTransactionStartStamp(); //restore the transaction start stamp
>             return (Transaction) tl.remove(0);
>         } else {
>             return null;
>         }
>     }
>     protected static void removeSuspendedTransaction(Transaction t) {
>         List tl = (List) suspendedTxStack.get();
>         if (tl != null && tl.size() > 0) {
>             tl.remove(t);
>             popTransactionStartStamp(t);
>         }
>     }
>     // =======================================
>     // =======================================
>     private static ThreadLocal transactionBeginStack = new ThreadLocal();
>     private static ThreadLocal transactionBeginStackSave = new ThreadLocal();
>     private static void pushTransactionBeginStackSave(Exception e) {
>         List el = (List) transactionBeginStackSave.get();
>         if (el == null) {
>             el = new LinkedList();
>             transactionBeginStackSave.set(el);
>         }
>         el.add(0, e);
>     }
>     private static Exception popTransactionBeginStackSave() {
>         List el = (List) transactionBeginStackSave.get();
>         if (el != null && el.size() > 0) {
>             return (Exception) el.remove(0);
>         } else {
>             return null;
>         }
>     }
>     private static void setTransactionBeginStack() {
>         Exception e = new Exception("Tx Stack Placeholder");
>         setTransactionBeginStack(e);
>     }
>     private static void setTransactionBeginStack(Exception newExc) {
>         if (transactionBeginStack.get() != null) {
>             Exception e = (Exception) transactionBeginStack.get();
>             Debug.logWarning(e, "WARNING: In setTransactionBeginStack a stack placeholder was already in place, here is where the transaction began: ", module);
>             Exception e2 = new Exception("Current Stack Trace");
>             Debug.logWarning(e2, "WARNING: In setTransactionBeginStack a stack placeholder was already in place, here is the current location: ", module);
>         }
>         transactionBeginStack.set(newExc);
>     }
>     private static Exception clearTransactionBeginStack() {
>         Exception e = (Exception) transactionBeginStack.get();
>         if (e == null) {
>             Exception e2 = new Exception("Current Stack Trace");
>             Debug.logWarning("WARNING: In clearTransactionBeginStack no stack placeholder was in place, here is the current location: ", module);
>             return null;
>         } else {
>             transactionBeginStack.set(null);
>             return e;
>         }
>     }
>     public static Exception getTransactionBeginStack() {
>         if (transactionBeginStack.get() == null) {
>             Exception e2 = new Exception("Current Stack Trace");
>             Debug.logWarning("WARNING: In getTransactionBeginStack no stack placeholder was in place, here is the current location: ", module);
>         }
>         return (Exception) transactionBeginStack.get();
>     }
>     // =======================================
>     // =======================================
>     private static class RollbackOnlyCause {
>         protected String causeMessage;
>         protected Throwable causeThrowable;
>         public RollbackOnlyCause(String causeMessage, Throwable causeThrowable) {
>             this.causeMessage = causeMessage;
>             this.causeThrowable = causeThrowable;
>         }
>         public String getCauseMessage() { return this.causeMessage + (this.causeThrowable == null ? "" : this.causeThrowable.toString()); }
>         public Throwable getCauseThrowable() { return this.causeThrowable; }
>         public void logError(String message) { Debug.logError(this.getCauseThrowable(), (message == null ? "" : message) + this.getCauseMessage(), module); }
>         public boolean isEmpty() { return (UtilValidate.isEmpty(this.getCauseMessage()) && this.getCauseThrowable() == null); }
>     }
>    
>     private static ThreadLocal setRollbackOnlyCause = new ThreadLocal();
>     private static ThreadLocal setRollbackOnlyCauseSave = new ThreadLocal();
>     private static void pushSetRollbackOnlyCauseSave(RollbackOnlyCause e) {
>         List el = (List) setRollbackOnlyCauseSave.get();
>         if (el == null) {
>             el = new LinkedList();
>             setRollbackOnlyCauseSave.set(el);
>         }
>         el.add(0, e);
>     }
>     private static RollbackOnlyCause popSetRollbackOnlyCauseSave() {
>         List el = (List) setRollbackOnlyCauseSave.get();
>         if (el != null && el.size() > 0) {
>             return (RollbackOnlyCause) el.remove(0);
>         } else {
>             return null;
>         }
>     }
>     private static void setSetRollbackOnlyCause(String causeMessage, Throwable causeThrowable) {
>         RollbackOnlyCause roc = new RollbackOnlyCause(causeMessage, causeThrowable);
>         setSetRollbackOnlyCause(roc);
>     }
>     private static void setSetRollbackOnlyCause(RollbackOnlyCause newRoc) {
>         if (setRollbackOnlyCause.get() != null) {
>             RollbackOnlyCause roc = (RollbackOnlyCause) setRollbackOnlyCause.get();
>             roc.logError("WARNING: In setSetRollbackOnlyCause a stack placeholder was already in place, here is the original rollbackOnly cause: ");
>             Exception e2 = new Exception("Current Stack Trace");
>             Debug.logWarning(e2, "WARNING: In setSetRollbackOnlyCause a stack placeholder was already in place, here is the current location: ", module);
>         }
>         setRollbackOnlyCause.set(newRoc);
>     }
>     private static RollbackOnlyCause clearSetRollbackOnlyCause() {
>         RollbackOnlyCause roc = (RollbackOnlyCause) setRollbackOnlyCause.get();
>         if (roc == null) {
>             /* this is an obnoxious message, leaving out for now; could be added manually if a problem with this is suspected
>             if (Debug.verboseOn()) {
>                 // for this in particular, unlike the begin location, normally there will not be a setRollbackOnlyCause, so don't complain about it except in verbose
>                 Debug.logVerbose(new Exception("Current Stack Trace"), "In clearSetRollbackOnlyCause no stack placeholder was in place, here is the current location: ", module);
>             }
>             */
>             return null;
>         } else {
>             setRollbackOnlyCause.set(null);
>             return roc;
>         }
>     }
>     public static RollbackOnlyCause getSetRollbackOnlyCause() {
>         if (setRollbackOnlyCause.get() == null) {
>             Exception e2 = new Exception("Current Stack Trace");
>             Debug.logWarning("WARNING: In getSetRollbackOnlyCause no stack placeholder was in place, here is the current location: ", module);
>         }
>         return (RollbackOnlyCause) setRollbackOnlyCause.get();
>     }
>     // =======================================
>     // =======================================
>    
>     /**
>      * Maintain the suspended transactions together with their timestamps
>      */
>     private static ThreadLocal suspendedTxStartStamps = new ThreadLocal() {
> public Object initialValue() {
> return new ListOrderedMap() ;
> }
> };
>    
> /**
> * Put the stamp to remember later
> * @param t transaction just suspended
> */
> private static void pushTransactionStartStamp(Transaction t) {
> Map map = (Map)suspendedTxStartStamps.get();
> Timestamp stamp = (Timestamp)transactionStartStamp.get();
> if (stamp != null) {
> map.put(t, stamp);
> }
> else {
> Debug.logError("Error in transaction handling - no start stamp to push.", module);
> }
> }
>
>
> /**
> * Method called when the suspended stack gets cleaned by {@link #cleanSuspendedTransactions()}.
> */
> private static void clearTransactionStartStampStack() {
> ((Map)suspendedTxStartStamps.get()).clear();
> }
>
> /**
> * Remove the stamp of the specified transaction from stack (when resuming)
> * and set it as current start stamp.
> * @param t transaction just resumed
> */
> private static void popTransactionStartStamp(Transaction t) {
> Map map = (Map)suspendedTxStartStamps.get();
> if (map.size() > 0) {
> Timestamp stamp = (Timestamp)map.remove(t);
> if (stamp != null) {
> transactionStartStamp.set(stamp);
>    }
> else {
> Debug.logError("Error in transaction handling - no saved start stamp found - using NOW.", module);
> transactionStartStamp.set(UtilDateTime.nowTimestamp());
> }
> }
> }
>
> /**
> * Remove the stamp from stack (when resuming)
> */
> private static void popTransactionStartStamp() {
> ListOrderedMap map = (ListOrderedMap)suspendedTxStartStamps.get();
> if (map.size() > 0) {
> transactionStartStamp.set(map.remove(map.lastKey()));
> }
> else {
> Debug.logError("Error in transaction handling - no saved start stamp found - using NOW.", module);
> transactionStartStamp.set(UtilDateTime.nowTimestamp());
> }
>
> }
>
>    
>     private static ThreadLocal transactionStartStamp = new ThreadLocal();
>     private static ThreadLocal transactionLastNowStamp = new ThreadLocal();
>     public static Timestamp getTransactionStartStamp() {
>         Timestamp curStamp = (Timestamp) transactionStartStamp.get();
>         if (curStamp == null) {
>             curStamp = UtilDateTime.nowTimestamp();
>             transactionStartStamp.set(curStamp);
>             // we know this is the first time set for this transaction, so make sure the StampClearSync is registered
>             try {
>                 registerSynchronization(new StampClearSync());
>             } catch (GenericTransactionException e) {
>                 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);
>             }
>         }
>         return curStamp;
>     }
>     public static Timestamp getTransactionUniqueNowStamp() {
>         Timestamp lastNowStamp = (Timestamp) transactionLastNowStamp.get();
>         Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
>         // check for an overlap with the lastNowStamp, or if the lastNowStamp is in the future because of incrementing to make each stamp unique
>         if (lastNowStamp != null && (lastNowStamp.equals(nowTimestamp) || lastNowStamp.after(nowTimestamp))) {
>             nowTimestamp = new Timestamp(lastNowStamp.getTime() + 1);
>         }
>         transactionLastNowStamp.set(nowTimestamp);
>         return nowTimestamp;
>     }
>     protected static void clearTransactionStamps() {
>         transactionStartStamp.set(null);
>         transactionLastNowStamp.set(null);
>     }
>     public static class StampClearSync implements Synchronization {
>         public void afterCompletion(int status) {
>             TransactionUtil.clearTransactionStamps();
>         }
>         public void beforeCompletion() {
>         }
>     }
> }

--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://jira.undersunconsulting.com/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira