[ 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 |
Free forum by Nabble | Edit this page |