/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.transaction;
import java.util.*;
import java.util.logging.*;
import javax.transaction.*;
import javax.transaction.xa.*;
import javax.persistence.EntityManagerFactory;
import com.sun.enterprise.util.Utility;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.transaction.api.JavaEETransaction;
import com.sun.enterprise.transaction.api.SimpleResource;
import com.sun.enterprise.transaction.api.JavaEETransactionManager;
import com.sun.enterprise.transaction.spi.TransactionalResource;
import com.sun.enterprise.transaction.spi.TransactionInternal;
import com.sun.logging.LogDomains;
/**
* This class implements the JTA Transaction API for the J2EE RI.
* It is a wrapper over the JTS Transaction object that provides optimized local
* transaction support when a transaction uses zero/one non-XA resource,
* and delegates to JTS otherwise.
* This object can be in two states: local tx (jtsTx==null) or global (JTS) tx.
* If jtsTx!=null, all calls are delegated to jtsTx.
*
* Time out capability is added to the local transactions. This class extends the TimerTask.
* When the transaction needs to be timedout, this schedules with the timer. At the commit
* and rollback time, task will be cancelled. If the transaction is timedout, run() method
* will be called and transaction will be marked for rollback.
*/
public final class JavaEETransactionImpl extends TimerTask implements
JavaEETransaction {
static Logger _logger = LogDomains.getLogger(JavaEETransactionImpl.class, LogDomains.JTA_LOGGER);
// Sting Manager for Localization
private static StringManager sm = StringManager.getManager(JavaEETransactionImpl.class);
JavaEETransactionManager javaEETM;
// Local Tx ids are just numbers: they dont need to be unique across
// processes or across multiple activations of this server process.
private static long txIdCounter = 1;
// Fall back to the old (wrong) behavior for the case when setRollbackOnly
// was called before XA transaction started
private static boolean DISABLE_STATUS_CHECK_ON_SWITCH_TO_XA =
Boolean.getBoolean("com.sun.jts.disable_status_check_on_switch_to_xa");
private long txId;
private JavaEEXid xid;
private TransactionInternal jtsTx;
private TransactionalResource nonXAResource;
private TransactionalResource laoResource;
private int localTxStatus;
private Vector syncs = new Vector();
private Vector interposedSyncs = new Vector();
private boolean commitStarted = false;
// START 4662745
private long startTime;
// END 4662745
// START: local transaction timeout
private boolean timedOut = false;
private boolean isTimerTask = false;
private int timeout = 0;
// END: local transaction timeout
private boolean imported = false;
private HashMap resourceTable;
private HashMap<Object, Object> userResourceMap;
//This cache contains the EntityContexts in this Tx
private Object activeTxCache;
// SimpleResource mapping for EMs with TX persistent context type
private Map<EntityManagerFactory, SimpleResource> txEntityManagerMap;
// SimpleResource mapping for EMs with EXTENDED persistence context type
private Map<EntityManagerFactory, SimpleResource> extendedEntityManagerMap;
private String componentName = null;
private ArrayList<String> resourceNames = null;
// tx-specific ejb container info associated with this tx
private Object containerData = null;
static private boolean isTimerInitialized = false;
static private Timer timer = null;
static private long timerTasksScheduled = 0; // Global counter
static synchronized private void initializeTimer() {
if (isTimerInitialized)
return;
timer = new Timer("JTA TX Timer",true); // daemon
isTimerInitialized = true;
}
JavaEETransactionImpl(JavaEETransactionManager javaEETM) {
this.javaEETM = javaEETM;
this.txId = getNewTxId();
this.xid = new JavaEEXid(txId);
this.resourceTable = new HashMap();
localTxStatus = Status.STATUS_ACTIVE;
startTime=System.currentTimeMillis();
if (_logger != null && _logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--Created new JavaEETransactionImpl, txId = "+txId);
}
}
// START: local transaction timeout
JavaEETransactionImpl(int timeout, JavaEETransactionManager javaEETM) {
this(javaEETM);
if (!isTimerInitialized)
initializeTimer();
timer.schedule(this,timeout * 1000L);
timerTasksScheduled++;
isTimerTask = true;
this.timeout = timeout;
}
// END: local transaction timeout
JavaEETransactionImpl(TransactionInternal jtsTx, JavaEETransactionManager javaEETM) {
this(javaEETM);
this.jtsTx = jtsTx;
imported = true;
}
// START: local transaction timeout
// TimerTask run() method implementation
public void run() {
timedOut = true;
try {
setRollbackOnly();
} catch (Exception e) {
_logger.log(Level.WARNING, "enterprise_distributedtx.some_excep", e);
}
}
public Object getContainerData() {
return containerData;
}
public void setContainerData(Object data) {
containerData = data;
}
boolean isAssociatedTimeout() {
return isTimerTask;
}
// Cancels the timertask and returns the timeout
public int cancelTimerTask() {
cancel();
int mod = javaEETM.getPurgeCancelledTtransactionsAfter();
if (mod > 0 && timerTasksScheduled % mod == 0) {
int purged = timer.purge();
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "Purged " + purged + " timer tasks from canceled queue");
}
}
return timeout;
}
public boolean isTimedOut() {
return timedOut;
}
// END: local transaction timeout
private static synchronized long getNewTxId() {
long newTxId = txIdCounter++;
return newTxId;
}
public boolean equals(Object other) {
if ( other == this )
return true;
if ( other instanceof JavaEETransactionImpl ) {
JavaEETransactionImpl othertx = (JavaEETransactionImpl)other;
return ( txId == othertx.txId );
}
return false;
}
public int hashCode() {
return (int)txId;
}
Xid getLocalXid() {
return xid;
}
public TransactionalResource getNonXAResource() {
return nonXAResource;
}
void setNonXAResource(TransactionalResource h) {
nonXAResource = h;
}
public TransactionalResource getLAOResource() {
return laoResource;
}
public void setLAOResource(TransactionalResource h) {
laoResource = h;
}
boolean isImportedTransaction() {
return imported;
}
synchronized void putUserResource(Object key, Object value) {
if (userResourceMap == null)
userResourceMap = new HashMap<Object, Object>();
userResourceMap.put(key, value);
}
synchronized Object getUserResource(Object key) {
if (userResourceMap == null)
return null;
return userResourceMap.get(key);
}
void registerInterposedSynchronization(Synchronization sync)
throws RollbackException,
SystemException {
interposedSyncs.add(sync);
if (jtsTx != null)
jtsTx.registerInterposedSynchronization(sync);
}
void setComponentName(String componentName) {
this.componentName = componentName;
}
String getComponentName() {
return componentName;
}
synchronized void addResourceName(String resourceName) {
if (resourceNames == null)
resourceNames = new ArrayList<String>();
if( !resourceNames.contains(resourceName) ) {
resourceNames.add(resourceName);
}
}
synchronized ArrayList<String> getResourceNames() {
return resourceNames;
}
public void addTxEntityManagerMapping(EntityManagerFactory emf,
SimpleResource em) {
getTxEntityManagerMap().put(emf, em);
}
public SimpleResource getTxEntityManagerResource(EntityManagerFactory emf) {
return getTxEntityManagerMap().get(emf);
}
private Map<EntityManagerFactory, SimpleResource>
getTxEntityManagerMap() {
if( txEntityManagerMap == null ) {
txEntityManagerMap =
new HashMap<EntityManagerFactory, SimpleResource>();
}
return txEntityManagerMap;
}
protected void onTxCompletion(boolean status) {
if( txEntityManagerMap == null ) {
return;
}
for (Map.Entry<EntityManagerFactory, SimpleResource> entry :
getTxEntityManagerMap().entrySet()) {
SimpleResource em = entry.getValue();
if (em.isOpen()) {
try {
em.close();
} catch (Throwable th) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "Exception while closing em.", th);
}
}
}
}
}
public void addExtendedEntityManagerMapping(EntityManagerFactory emf,
SimpleResource em) {
getExtendedEntityManagerMap().put(emf, em);
}
public void removeExtendedEntityManagerMapping(EntityManagerFactory emf) {
getExtendedEntityManagerMap().remove(emf);
}
public SimpleResource getExtendedEntityManagerResource(EntityManagerFactory emf) {
return getExtendedEntityManagerMap().get(emf);
}
private Map<EntityManagerFactory, SimpleResource>
getExtendedEntityManagerMap() {
if( extendedEntityManagerMap == null ) {
extendedEntityManagerMap =
new HashMap<EntityManagerFactory, SimpleResource>();
}
return extendedEntityManagerMap;
}
public boolean isLocalTx() {
return (jtsTx==null);
}
void setJTSTx(TransactionInternal jtsTx) throws RollbackException, SystemException {
// Remember the status from this transaction
boolean marked_for_rollback = isRollbackOnly();
this.jtsTx = jtsTx;
if ( !commitStarted ) {
// register syncs
for ( int i=0; i<syncs.size(); i++ )
jtsTx.registerSynchronization((Synchronization)syncs.elementAt(i));
for ( int i=0; i<interposedSyncs.size(); i++ )
jtsTx.registerInterposedSynchronization(
(Synchronization)interposedSyncs.elementAt(i));
}
// Now adjust the status
if (!DISABLE_STATUS_CHECK_ON_SWITCH_TO_XA && marked_for_rollback) {
jtsTx.setRollbackOnly();
}
}
TransactionInternal getJTSTx() {
return jtsTx;
}
public void commit() throws RollbackException,
HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, SystemException {
checkTransationActive();
// START local transaction timeout
// If this transaction is set for timeout, cancel it as it is in the commit state
if (isTimerTask)
cancelTimerTask();
// END local transaction timeout
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--In JavaEETransactionImpl.commit, jtsTx="+jtsTx
+" nonXAResource="+ nonXAResource);
}
commitStarted = true;
boolean success = false;
if ( jtsTx != null ) {
try {
jtsTx.commit();
success = true;
} catch(HeuristicMixedException e) {
success = true;
throw e;
} finally {
((JavaEETransactionManagerSimplified) javaEETM).monitorTxCompleted(this, success);
((JavaEETransactionManagerSimplified) javaEETM).clearThreadTx();
onTxCompletion(success);
try {
localTxStatus = jtsTx.getStatus();
} catch (Exception e) {
localTxStatus = Status.STATUS_NO_TRANSACTION;
}
jtsTx = null;
}
} else { // local tx
Exception caughtException = null;
try {
if ( timedOut ) {
// rollback nonXA resource
if ( nonXAResource != null )
nonXAResource.getXAResource().rollback(xid);
localTxStatus = Status.STATUS_ROLLEDBACK;
throw new RollbackException(sm.getString("enterprise_distributedtx.rollback_timeout"));
}
if ( isRollbackOnly() ) {
// rollback nonXA resource
if ( nonXAResource != null )
nonXAResource.getXAResource().rollback(xid);
localTxStatus = Status.STATUS_ROLLEDBACK;
throw new RollbackException(sm.getString("enterprise_distributedtx.mark_rollback"));
}
// call beforeCompletion
for ( int i=0; i<syncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)syncs.elementAt(i);
sync.beforeCompletion();
} catch ( RuntimeException ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.before_completion_excep", ex);
setRollbackOnly();
caughtException = ex;
break;
} catch (Exception ex) {
_logger.log(Level.WARNING, "enterprise_distributedtx.before_completion_excep", ex);
// XXX-V2 no setRollbackOnly() ???
}
}
for ( int i=0; i<interposedSyncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)interposedSyncs.elementAt(i);
sync.beforeCompletion();
} catch ( RuntimeException ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.before_completion_excep", ex);
setRollbackOnly();
caughtException = ex;
break;
} catch (Exception ex) {
_logger.log(Level.WARNING, "enterprise_distributedtx.before_completion_excep", ex);
// XXX-V2 no setRollbackOnly() ???
}
}
// check rollbackonly again, in case any of the beforeCompletion
// calls marked it for rollback.
if ( isRollbackOnly()) {
//Check if it is a Local Transaction
RollbackException rbe = null;
if(jtsTx == null) {
if ( nonXAResource != null )
nonXAResource.getXAResource().rollback(xid);
localTxStatus = Status.STATUS_ROLLEDBACK;
rbe = new RollbackException(sm.getString("enterprise_distributedtx.mark_rollback"));
// else it is a global transaction
} else {
jtsTx.rollback();
localTxStatus = Status.STATUS_ROLLEDBACK;
rbe = new RollbackException(sm.getString("enterprise_distributedtx.mark_rollback"));
}
// RollbackException doesn't have a constructor that takes a Throwable.
if (caughtException != null) {
rbe.initCause(caughtException);
}
throw rbe;
}
// check if there is a jtsTx active, in case any of the
// beforeCompletions registered the first XA resource.
if ( jtsTx != null ) {
jtsTx.commit();
// Note: JTS will not call afterCompletions in this case,
// because no syncs have been registered with JTS.
// So afterCompletions are called in finally block below.
} else {
// do single-phase commit on nonXA resource
if ( nonXAResource != null )
nonXAResource.getXAResource().commit(xid, true);
}
// V2-XXX should this be STATUS_NO_TRANSACTION ?
localTxStatus = Status.STATUS_COMMITTED;
success = true;
} catch ( RollbackException ex ) {
localTxStatus = Status.STATUS_ROLLEDBACK; // V2-XXX is this correct ?
throw ex;
} catch ( SystemException ex ) {
// localTxStatus = Status.STATUS_ROLLEDBACK; // V2-XXX is this correct ?
localTxStatus = Status.STATUS_COMMITTING;
success = true;
throw ex;
} catch ( Exception ex ) {
localTxStatus = Status.STATUS_ROLLEDBACK; // V2-XXX is this correct ?
SystemException exc = new SystemException();
exc.initCause(ex);
throw exc;
} finally {
((JavaEETransactionManagerSimplified) javaEETM).monitorTxCompleted(this, success);
((JavaEETransactionManagerSimplified) javaEETM).clearThreadTx();
for ( int i=0; i<interposedSyncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)interposedSyncs.elementAt(i);
sync.afterCompletion(localTxStatus);
} catch ( Exception ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.after_completion_excep", ex);
}
}
// call afterCompletions
for ( int i=0; i<syncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)syncs.elementAt(i);
sync.afterCompletion(localTxStatus);
} catch ( Exception ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.after_completion_excep", ex);
}
}
onTxCompletion(success);
jtsTx = null;
}
}
}
public void rollback() throws IllegalStateException, SystemException {
// START local transaction timeout
// If this transaction is set for timeout, cancel it as it is in the rollback state
if (isTimerTask)
cancelTimerTask();
// END local transaction timeout
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--In JavaEETransactionImpl.rollback, jtsTx="+jtsTx
+" nonXAResource="+nonXAResource);
}
if ( jtsTx == null )
checkTransationActive(); // non-xa transaction can't be in prepared state, xa code will do its check
try {
if ( jtsTx != null )
jtsTx.rollback();
else { // rollback nonXA resource
if ( nonXAResource != null )
nonXAResource.getXAResource().rollback(xid);
}
} catch ( SystemException ex ) {
throw ex;
} catch ( IllegalStateException ex ) {
throw ex;
} catch ( Exception ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.some_excep", ex);
} finally {
// V2-XXX should this be STATUS_NO_TRANSACTION ?
localTxStatus = Status.STATUS_ROLLEDBACK;
((JavaEETransactionManagerSimplified) javaEETM).monitorTxCompleted(this, false);
((JavaEETransactionManagerSimplified) javaEETM).clearThreadTx();
if ( jtsTx == null ) {
for ( int i=0; i<interposedSyncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)interposedSyncs.elementAt(i);
sync.afterCompletion(Status.STATUS_ROLLEDBACK);
} catch ( Exception ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.after_completion_excep", ex);
}
}
// call afterCompletions
for ( int i=0; i<syncs.size(); i++ ) {
try {
Synchronization sync = (Synchronization)syncs.elementAt(i);
sync.afterCompletion(Status.STATUS_ROLLEDBACK);
} catch ( Exception ex ) {
_logger.log(Level.WARNING, "enterprise_distributedtx.after_completion_excep", ex);
}
}
}
onTxCompletion(false);
jtsTx = null;
}
}
public boolean delistResource(XAResource xaRes, int flag)
throws IllegalStateException, SystemException {
// START OF IASRI 4660742
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--In JavaEETransactionImpl.delistResource: "
+ xaRes + " from " + this);
}
// END OF IASRI 4660742
checkTransationActive();
if ( jtsTx != null )
return jtsTx.delistResource(xaRes, flag);
else
throw new IllegalStateException(sm.getString("enterprise_distributedtx.deleteresource_for_localtx"));
}
public boolean enlistResource(XAResource xaRes)
throws RollbackException, IllegalStateException,
SystemException {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--In JavaEETransactionImpl.enlistResource, jtsTx="
+jtsTx+" nonXAResource="+nonXAResource);
}
checkTransationActive();
if ( jtsTx != null )
return jtsTx.enlistResource(xaRes);
else if ( nonXAResource != null )
throw new IllegalStateException(sm.getString("enterprise_distributedtx.already_has_nonxa"));
// IASRI END 4723068
/***
else // V2-XXX what to do ? Start a new JTS tx ?
throw new IllegalStateException("JavaEETransactionImpl.enlistResource called for local tx");
***/
else { // Start a new JTS tx
((JavaEETransactionManagerSimplified) javaEETM).startJTSTx(this);
return jtsTx.enlistResource(xaRes);
}
// IASRI END 4723068
}
public int getStatus() throws SystemException {
if ( jtsTx != null )
return jtsTx.getStatus();
else
return localTxStatus;
}
public void registerSynchronization(Synchronization sync)
throws RollbackException, IllegalStateException,
SystemException {
// START OF IASRI 4660742
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,"--In JavaEETransactionImpl.registerSynchronization, jtsTx="
+jtsTx+" nonXAResource="+nonXAResource);
}
// END OF IASRI 4660742
checkTransationActive();
if ( jtsTx != null )
jtsTx.registerSynchronization(sync);
else
syncs.add(sync);
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
checkTransationActive();
if ( jtsTx != null )
jtsTx.setRollbackOnly();
else
localTxStatus = Status.STATUS_MARKED_ROLLBACK;
}
private boolean isRollbackOnly() throws IllegalStateException,
SystemException {
int status;
if ( jtsTx != null )
status = jtsTx.getStatus();
else
status = localTxStatus;
return (status == Status.STATUS_MARKED_ROLLBACK);
}
private void checkTransationActive() throws SystemException {
int status = getStatus();
if (status != Status.STATUS_MARKED_ROLLBACK &&
status != Status.STATUS_ACTIVE) {
throw new IllegalStateException(sm.getString(
"enterprise_distributedtx.transaction_notactive"));
}
}
public String toString() {
return "JavaEETransactionImpl: txId="+txId+" nonXAResource="+nonXAResource
+" jtsTx="+jtsTx+" localTxStatus="+localTxStatus
+" syncs="+syncs;
}
// START IASRI 4662745
/*
* This method is used for the Admin Framework displaying
* of Transactions Ids
*/
public String getTransactionId(){
return xid.toString();
}
/*
* This method returns the time this transaction was started
*/
public long getStartTime(){
return startTime;
}
// END IASRI 4662745
public void setResources(Set resources, Object poolInfo) {
resourceTable.put(poolInfo, resources);
}
public Set getResources(Object poolInfo) {
return (Set) resourceTable.get(poolInfo);
}
/**
* Return all pools registered in the resourceTable. This
* will cut down the scope of pools on which transactionComplted
* is called by the PoolManagerImpl. This method will return
* only those pools that have ever participated in a tx
*/
public Set getAllParticipatingPools() {
return (Set) resourceTable.keySet();
}
// Assume that there is only one instance of this class per local tx.
private static class JavaEEXid implements javax.transaction.xa.Xid {
private static final int formatId = 987654321;
private static final byte[] bqual = new byte[]{0};
private byte[] gtrId;
// START IASRI 4662745
private String stringForm=null;
// END IASRI 4662745
JavaEEXid(long txId) {
gtrId = new byte[8];
Utility.longToBytes(txId, gtrId, 0);
}
public int getFormatId() {
return formatId;
}
public byte[] getGlobalTransactionId() {
return gtrId;
}
public byte[] getBranchQualifier() {
return bqual; // V2-XXX check if its ok to always have same bqual
}
// START IASRI 4662745
/*
* returens the Transaction id of this transaction
*/
public String toString(){
// If we have a cached copy of the string form of the global identifier, return
// it now.
if( stringForm != null ) return stringForm;
// Otherwise format the global identifier.
//char[] buff = new char[gtrId.length*2 + 2/*'[' and ']'*/ + 3/*bqual and ':'*/];
char[] buff = new char[gtrId.length*2 + 3/*bqual and ':'*/];
int pos = 0;
//buff[pos++] = '[';
// Convert the global transaction identifier into a string of hex digits.
int globalLen = gtrId.length ;
for( int i = 0; i < globalLen; i++ ) {
int currCharHigh = (gtrId[i]&0xf0) >> 4;
int currCharLow = gtrId[i]&0x0f;
buff[pos++] = (char)(currCharHigh + (currCharHigh > 9 ? 'A'-10 : '0'));
buff[pos++] = (char)(currCharLow + (currCharLow > 9 ? 'A'-10 : '0'));
}
//buff[pos++] = ':';
buff[pos++] = '_';
int currCharHigh = (0&0xf0) >> 4;
int currCharLow = 0&0x0f;
buff[pos++] = (char)(currCharHigh + (currCharHigh > 9 ? 'A'-10 : '0'));
buff[pos++] = (char)(currCharLow + (currCharLow > 9 ? 'A'-10 : '0'));
//buff[pos] = ']';
// Cache the string form of the global identifier.
stringForm = new String(buff);
return stringForm;
}
// END IASRI 4662745
}
public void setActiveTxCache(Object cache) {
this.activeTxCache = cache;
}
public Object getActiveTxCache() {
return this.activeTxCache;
}
/**
* Return duration in seconds before transaction would timeout.
*
* Returns zero if this transaction has no timeout set.
* Returns negative value if already timed out.
*/
public int getRemainingTimeout() {
if (timeout == 0) {
return timeout;
} else if (timedOut) {
return -1;
} else {
// compute how much time left before transaction times out
return timeout - (int)((System.currentTimeMillis() - startTime) / 1000L);
}
}
}