com.gemstone.gemfire.internal.jta.TransactionManagerImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.jta;
/**
*
* TransactionManager: A JTA compatible Transaction Manager.
*
*
* @author Mitul Bid
* @author Asif
* @since 4.1.1
*/
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import java.io.Serializable;
//import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.transaction.*;
import com.gemstone.gemfire.CancelException;
import com.gemstone.gemfire.internal.LogWriterImpl;
import com.gemstone.gemfire.internal.cache.TXManagerImpl;
public class TransactionManagerImpl implements TransactionManager, Serializable {
private static final long serialVersionUID = 5033392316185449821L;
/**
* A mapping of Thread - Transaction Objects.
*
* [sumedh] Now it is in the TXManagerImpl ThreadLocal to allow for only one
* ThreadLocal lookup to get current TXStateInterface and detect JTA (see
* LocalRegion.discoverJTA). Furthermore, ThreadLocal lookup will normally be
* more efficient than a ConcurrentMap lookup.
*/
//private final Map transactionMap = CFactory.createCM();
/**
* A mapping of Transaction - Global Transaction
*/
private final Map globalTransactionMap = Collections.synchronizedMap(new HashMap());
/**
* Ordered set of active global transactions - Used for timeOut
*/
protected SortedSet gtxSet = Collections.synchronizedSortedSet(new TreeSet(
new GlobalTransactionComparator()));
/**
* Transaction TimeOut Class
*/
transient private TransactionTimeOutThread cleaner;
/**
* Singleton transactionManager
*/
private static TransactionManagerImpl transactionManager = null;
/**
* Transaction TimeOut thread
*/
transient private Thread cleanUpThread = null;
/**
* Default Transaction Time Out
*/
static final int DEFAULT_TRANSACTION_TIMEOUT = Integer.getInteger("jta.defaultTimeout", 600).intValue();
/**
* Asif: The integers identifying the cause of Rollback
*/
private static final int MARKED_ROLLBACK = 1;
private static final int EXCEPTION_IN_NOTIFY_BEFORE_COMPLETION = 2;
private static final int COMMIT_FAILED_SO_ROLLEDBAK = 3;
private static final int COMMIT_FAILED_ROLLBAK_ALSO_FAILED = 4;
private static final int ROLLBAK_FAILED = 5;
//TODO:Asif .Not yet used this exception code
// private static final int EXCEPTION_IN_NOTIFY_AFTER_COMPLETION = 6;
/*
* to enable VERBOSE = true pass System parameter jta.VERBOSE = true
* while running the test.
*/
private static boolean VERBOSE = Boolean.getBoolean("jta.VERBOSE");
/*
* checks if the TransactionManager is active
*/
private boolean isActive = true;
/**
* Constructs a new TransactionManagerImpl
*/
private TransactionManagerImpl() {
cleaner = this.new TransactionTimeOutThread();
ThreadGroup group =
LogWriterImpl.createThreadGroup(LocalizedStrings.TransactionManagerImpl_CLEAN_UP_THREADS.toLocalizedString(), (LogWriterI18n) null);
cleanUpThread = new Thread(group, cleaner, "GlobalTXTimeoutMonitor");
cleanUpThread.setDaemon(true);
cleanUpThread.start();
}
/**
* Returns the singleton TransactionManagerImpl Object
*/
public static TransactionManagerImpl getTransactionManager() {
if (transactionManager == null) {
createTransactionManager();
}
return transactionManager;
}
/**
* Creates an instance of TransactionManagerImpl if none exists
*/
private static synchronized void createTransactionManager() {
if (transactionManager == null)
transactionManager = new TransactionManagerImpl();
}
/**
* Create a new transaction and associate it with the current thread if none
* exists with the current thread else throw an exception since nested
* transactions are not supported
*
* Create a global transaction and associate the transaction created with the
* global transaction
*
* @throws NotSupportedException - Thrown if the thread is already associated
* with a transaction and the Transaction Manager implementation
* does not support nested transactions.
* @throws SystemException - Thrown if the transaction manager encounters an
* unexpected error condition.
*
* @see javax.transaction.TransactionManager#begin()
*/
public void begin() throws NotSupportedException, SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
LogWriterI18n log = TransactionUtils.getLogWriterI18n();
if (log.fineEnabled()) {
log.fine("TransactionManager.begin() invoked");
}
final TXManagerImpl.TXContext context = TXManagerImpl.currentTXContext();
if (context != null && context.getJTA() != null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_BEGIN_NESTED_TRANSACTION_IS_NOT_SUPPORTED.toLocalizedString();
if (VERBOSE) log.fine(exception);
throw new NotSupportedException(exception);
}
try {
TransactionImpl transaction = new TransactionImpl();
GlobalTransaction globalTransaction = new GlobalTransaction();
globalTransactionMap.put(transaction, globalTransaction);
globalTransaction.addTransaction(transaction);
globalTransaction.setStatus(Status.STATUS_ACTIVE);
TXManagerImpl.beginJTA(transaction, context);
}
catch (Exception e) {
String exception = LocalizedStrings.TransactionManagerImpl_BEGIN__SYSTEMEXCEPTION_DUE_TO_0.toLocalizedString(new Object[] {e});
if (log.severeEnabled()) log.severe(LocalizedStrings.TransactionManagerImpl_BEGIN__SYSTEMEXCEPTION_DUE_TO_0, new Object[] {e});
final SystemException ex = new SystemException(exception);
ex.initCause(e);
throw ex;
}
}
/**
* Complete the transaction associated with the current thread by calling the
* GlobalTransaction.commit(). When this method completes, the thread is no
* longer associated with a transaction.
*
* @throws RollbackException - Thrown to indicate that the transaction has
* been rolled back rather than committed.
* @throws HeuristicMixedException - Thrown to indicate that a heuristic
* decision was made and that some relevant updates have been
* committed while others have been rolled back.
* @throws HeuristicRollbackException - Thrown to indicate that a heuristic
* decision was made and that all relevant updates have been
* rolled back.
* @throws java.lang.SecurityException - Thrown to indicate that the thread
* is not allowed to commit the transaction.
* @throws java.lang.IllegalStateException - Thrown if the current thread is
* not associated with a transaction.
* @throws SystemException - Thrown if the transaction manager encounters an
* unexpected error condition.
*
* @see javax.transaction.TransactionManager#commit()
*/
public void commit() throws HeuristicRollbackException, RollbackException,
HeuristicMixedException, SystemException {
if (!isActive) {
throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString());
}
int cozOfException = -1;
Transaction transactionImpl = getTransaction();
if (transactionImpl == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_TRANSACTION_IS_NULL_CANNOT_COMMIT_A_NULL_TRANSACTION.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
GlobalTransaction gtx = getGlobalTransaction(transactionImpl);
if (gtx == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_GLOBAL_TRANSACTION_IS_NULL_CANNOT_COMMIT_A_NULL_GLOBAL_TRANSACTION.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
boolean isCommit = false;
//ensure only one thread can commit. Use a synchronized block
//Asif
int status = -1;
if (((status = gtx.getStatus()) == Status.STATUS_ACTIVE)
|| status == Status.STATUS_MARKED_ROLLBACK) {
synchronized (gtx) {
if ((status = gtx.getStatus()) == Status.STATUS_ACTIVE) {
gtx.setStatus(Status.STATUS_COMMITTING);
isCommit = true;
}
else if (status == Status.STATUS_MARKED_ROLLBACK) {
gtx.setStatus(Status.STATUS_ROLLING_BACK);
cozOfException = MARKED_ROLLBACK;
}
else {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_TRANSACTION_NOT_ACTIVE_CANNOT_BE_COMMITTED_TRANSACTION_STATUS_0.toLocalizedString(Integer.valueOf(status));
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
}
}
else {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_TRANSACTION_IS_NOT_ACTIVE_AND_CANNOT_BE_COMMITTED.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
//Only one thread can call commit (the first thread to do reach the block
// above).
//Before commiting the notifications to be done before the done are called
// the global transaction is called and then the after completion
// notifications
// are taken care of. The transactions associated to the global
// transactions are
//removed from the map and also the tread to transaction.
//
// Asif : Store the thrown Exception in case of commit .
//Reuse it for thrwing later.
//Asif TODO:Verify if it is a good practise
boolean isClean = false;
Exception e = null;
try {
((TransactionImpl) transactionImpl).notifyBeforeCompletion();
isClean = true;
}
catch (Exception ge) {
//Asif : Just mark the Tranxn to setRollbackOnly to ensure Rollback
setRollbackOnly();
cozOfException = EXCEPTION_IN_NOTIFY_BEFORE_COMPLETION;
e = ge;
}
//TODO:Asif In case the status of transaction is marked as
// ROLLING_BACK , then we don't have to take a synch block
// As once the status is marked for ROLLING_BACK , setRollnbackonly
//will be harmless
if (isCommit) {
synchronized (gtx) {
if ((status = gtx.getStatus()) == Status.STATUS_COMMITTING) {
//Asif: Catch any exception encountered during commit
// and appropriately mark the exception code
try {
gtx.commit();
}
catch (RollbackException rbe) {
e = rbe;
cozOfException = COMMIT_FAILED_SO_ROLLEDBAK;
}
catch (SystemException se) {
e = se;
cozOfException = COMMIT_FAILED_ROLLBAK_ALSO_FAILED;
}
}
else if (status == Status.STATUS_ROLLING_BACK) {
try {
gtx.rollback();
if (isClean) cozOfException = MARKED_ROLLBACK;
}
catch (SystemException se) {
e = se;
cozOfException = ROLLBAK_FAILED;
}
}
}
}
else {
try {
gtx.rollback();
}
catch (SystemException se) {
e = se;
cozOfException = ROLLBAK_FAILED;
}
}
try {
((TransactionImpl) transactionImpl).notifyAfterCompletion(status = gtx
.getStatus());
}
catch (Exception ge) {
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (writer.infoEnabled())
writer.info(LocalizedStrings.TransactionManagerImpl_EXCEPTION_IN_NOTIFY_AFTER_COMPLETION_DUE_TO__0, ge.getMessage(), ge);
}
TXManagerImpl.endCurrentJTA();
this.gtxSet.remove(gtx);
if (status != Status.STATUS_COMMITTED) {
switch (cozOfException) {
case EXCEPTION_IN_NOTIFY_BEFORE_COMPLETION: {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_TRANSACTION_ROLLED_BACK_BECAUSE_OF_EXCEPTION_IN_NOTIFYBEFORECOMPLETION_FUNCTION_CALL_ACTUAL_EXCEPTION_0.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception, e);
RollbackException re = new RollbackException(exception);
re.initCause(e);
throw re;
}
case MARKED_ROLLBACK: {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_COMMIT_TRANSACTION_ROLLED_BACK_BECAUSE_A_USER_MARKED_IT_FOR_ROLLBACK.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception, e);
throw new RollbackException(exception);
}
case COMMIT_FAILED_SO_ROLLEDBAK: {
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(e);
throw (RollbackException) e;
}
case COMMIT_FAILED_ROLLBAK_ALSO_FAILED:
case ROLLBAK_FAILED: {
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(e);
throw (SystemException) e;
}
}
}
gtx.setStatus(Status.STATUS_NO_TRANSACTION);
}
/**
* Rolls back the transaction associated with the current thread by calling
* the GlobalTransaction.rollback(). When this method completes, the thread
* is no longer associated with a transaction.
*
* @throws java.lang.SecurityException - Thrown to indicate that the thread
* is not allowed to commit the transaction.
* @throws java.lang.IllegalStateException - Thrown if the current thread is
* not associated with a transaction.
* @throws SystemException - Thrown if the transaction manager encounters an
* unexpected error condition.
*
* @see javax.transaction.TransactionManager#commit()
*/
public void rollback() throws IllegalStateException, SecurityException,
SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
// boolean isRollingBack = false;
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
Transaction transactionImpl = getTransaction();
if (transactionImpl == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_ROLLBACK_NO_TRANSACTION_EXISTS.toLocalizedString();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
GlobalTransaction gtx = getGlobalTransaction(transactionImpl);
if (gtx == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_ROLLBACK_NO_GLOBAL_TRANSACTION_EXISTS.toLocalizedString();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
int status = gtx.getStatus();
if (!(status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK)) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_ROLLBACK_TRANSACTION_STATUS_DOES_NOT_ALLOW_ROLLBACK_TRANSACTIONAL_STATUS_0.toLocalizedString(Integer.valueOf(status));
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
//ensure only one thread proceeds from here
status = -1;
synchronized (gtx) {
if ((status = gtx.getStatus()) == Status.STATUS_ACTIVE
|| status == Status.STATUS_MARKED_ROLLBACK)
gtx.setStatus(Status.STATUS_ROLLING_BACK);
else if (gtx.getStatus() == Status.STATUS_ROLLING_BACK) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_ROLLBACK_TRANSACTION_ALREADY_IN_A_ROLLING_BACK_STATE_TRANSACTIONAL_STATUS_0.toLocalizedString(Integer.valueOf(status));
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
else {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_ROLLBACK_TRANSACTION_STATUS_DOES_NOT_ALLOW_ROLLBACK.toLocalizedString();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
}
//Only one thread can call rollback (the first thread to do reach the
// block above).
//Before rollback the notifications to be done before the done are called
// the global transaction is called and then the after completion
// notifications
// are taken care of. The transactions associated to the global
// transactions are
//removed from the map and also the tread to transaction.
//
//TODO remove all threads-transactions (from the map)
//for transactions participating in the global transaction
//
SystemException se = null;
try {
gtx.rollback();
}
catch (SystemException se1) {
se = se1;
}
try {
((TransactionImpl) transactionImpl)
.notifyAfterCompletion(gtx.getStatus());
}
catch (Exception e1) {
if (writer.infoEnabled())
writer.info(LocalizedStrings.TransactionManagerImpl_EXCEPTION_IN_NOTIFY_AFTER_COMPLETION_DUE_TO__0, e1.getMessage(), e1);
}
TXManagerImpl.endCurrentJTA();
this.gtxSet.remove(gtx);
if (se != null) {
if (VERBOSE) writer.fine(se);
throw se;
}
gtx.setStatus(Status.STATUS_NO_TRANSACTION);
}
/**
* Set the Global Transaction status (Associated with the current thread) to
* be RollBackOnly
*
* Becauce we are using one phase commit, we are not considering Prepared and
* preparing states.
*
* @see javax.transaction.TransactionManager#setRollbackOnly()
*/
public void setRollbackOnly() throws IllegalStateException, SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
GlobalTransaction gtx = getGlobalTransaction();
if (gtx == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_SETROLLBACKONLY_NO_GLOBAL_TRANSACTION_EXISTS.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
synchronized (gtx) {
int status = gtx.getStatus();
if (status == Status.STATUS_ACTIVE)
gtx.setRollbackOnly();
else if (status == Status.STATUS_COMMITTING)
gtx.setStatus(Status.STATUS_ROLLING_BACK);
else if (status == Status.STATUS_ROLLING_BACK)
; //Dont do anything
else {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_SETROLLBACKONLY_TRANSACTION_CANNOT_BE_MARKED_FOR_ROLLBACK_TRANSCATION_STATUS_0.toLocalizedString(Integer.valueOf(status));
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new IllegalStateException(exception);
}
}
//Asif : Log after exiting synch block
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine("Transaction Set to Rollback only");
}
/**
* Get the status of the global transaction associated with this thread
*
* @see javax.transaction.TransactionManager#getStatus()
*/
public int getStatus() throws SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
GlobalTransaction gtx = getGlobalTransaction();
if (gtx == null) {
return Status.STATUS_NO_TRANSACTION;
}
return gtx.getStatus();
}
/**
* not supported
*
* @see javax.transaction.TransactionManager#setTransactionTimeout(int)
*/
public void setTransactionTimeout(int seconds) throws SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
GlobalTransaction gtx = getGlobalTransaction();
if (gtx == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_SETTRANSACTIONTIMEOUT_NO_GLOBAL_TRANSACTION_EXISTS.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
long newExpiry = gtx.setTransactionTimeoutForXARes(seconds);
if(newExpiry > 0 ){
//long expirationTime = System.currentTimeMillis() + (seconds * 1000);
gtxSet.remove(gtx);
// Asif :Lets blindly remove the current gtx from the TreeMap &
// Add only if status is neither Rolledback, Unknown , committed or no
// transaction or GTX not
// expired, which gurantees that the client thread will be returning &
// cleaning up .so we
//don't add it
int status = gtx.getStatus();
if (status != Status.STATUS_NO_TRANSACTION
&& status != Status.STATUS_COMMITTED
&& status != Status.STATUS_ROLLEDBACK && !gtx.isExpired()) {
//Asif : Take a lock on GTX while setting the new Transaction timeout
// value,
// so that cleaner thread sees the new value immediately else due to
// volatility issue
// we may have inconsistent values of time out
boolean toAdd = false;
synchronized (gtx) {
if (!gtx.isExpired()) {
gtx.setTimeoutValue(newExpiry);
toAdd = true;
}
}
//Asif : It is possible that in the window between we set the new
//timeout value in current GTX & add it to the gtxSet , the currentGtx
//is expired by the cleaner thread as there is no safeguard for it.
//We allow it to happen that is add the expired GTx to the TreeSet.
//Since a notify will be issued , the cleaner thread wil take care
// of it.
if (toAdd) {
synchronized (gtxSet) {
gtxSet.add(gtx);
if (gtxSet.first() == gtx) {
gtxSet.notify();
}
}
}
}
else {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_SETTRANSACTIONTIMEOUT_TRANSACTION_HAS_EITHER_EXPIRED_OR_ROLLEDBACK_OR_COMITTED.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
}
}
/**
* @see javax.transaction.TransactionManager#suspend()
*/
public Transaction suspend() throws SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
Transaction txn = getTransaction();
if (txn != null) {
GlobalTransaction gtx = getGlobalTransaction(txn);
gtx.suspend();
TXManagerImpl.setCurrentJTA(null);
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (writer.infoEnabled())
writer.info(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPLSUSPENDTRANSACTION_SUSPENDED);
}
return txn;
}
/**
* @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
*/
public void resume(Transaction txn) throws InvalidTransactionException,
IllegalStateException, SystemException {
if (!isActive) { throw new SystemException(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID.toLocalizedString()); }
if (txn == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_RESUME_CANNOT_RESUME_A_NULL_TRANSACTION.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new InvalidTransactionException(exception);
}
GlobalTransaction gtx = getGlobalTransaction(txn);
if (gtx == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_RESUME_CANNOT_RESUME_A_NULL_TRANSACTION.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new InvalidTransactionException(exception);
}
gtx.resume();
try {
TXManagerImpl.setCurrentJTA(txn);
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (writer.infoEnabled())
writer.info(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPLRESUMETRANSACTION_RESUMED);
}
catch (Exception e) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_RESUME_ERROR_IN_LISTING_THREAD_TO_TRANSACTION_MAP_DUE_TO_0.toLocalizedString(e);
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
final SystemException ex = new SystemException(exception);
ex.initCause(e);
throw ex;
}
}
/**
* Get the transaction associated with the calling thread
*
* @see javax.transaction.TransactionManager#getTransaction()
*/
public Transaction getTransaction() throws SystemException {
if (!isActive) {
throw new SystemException(
LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGER_INVALID
.toLocalizedString());
}
return TXManagerImpl.getCurrentJTA();
}
/**
* Get the transaction associated with the calling thread with given context.
*
* @see javax.transaction.TransactionManager#getTransaction()
*/
public Transaction getTransaction(final TXManagerImpl.TXContext context)
throws SystemException {
if (this.isActive) {
return context.getJTA();
}
return null;
}
/**
* Get the Global Transaction associated with the calling thread
*/
GlobalTransaction getGlobalTransaction() throws SystemException {
Transaction txn = TXManagerImpl.getCurrentJTA();
if (txn == null) {
return null;
}
GlobalTransaction gtx = (GlobalTransaction) globalTransactionMap.get(txn);
return gtx;
}
/**
* Get the Global Transaction associated with the calling thread
*/
GlobalTransaction getGlobalTransaction(Transaction txn)
throws SystemException {
if (txn == null) {
String exception = LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPL_GETGLOBALTRANSACTION_NO_TRANSACTION_EXISTS.toLocalizedString();
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (VERBOSE) writer.fine(exception);
throw new SystemException(exception);
}
GlobalTransaction gtx = (GlobalTransaction) globalTransactionMap.get(txn);
return gtx;
}
//Asif : This method is used only for testing purposes
Map getGlobalTransactionMap() {
return globalTransactionMap;
}
//Asif : Remove the mapping of tranxn to Global Tranxn.
//Previously thsi task was being done in GlobalTranxn
void cleanGlobalTransactionMap(List tranxns) {
synchronized (tranxns) {
Iterator iterator = tranxns.iterator();
while (iterator.hasNext()) {
globalTransactionMap.remove(iterator.next());
}
}
}
void removeTranxnMappings(List txns) {
final Map allContexts = TXManagerImpl
.getJTAContexts();
Transaction tx;
synchronized (allContexts) {
final Iterator contextIter = allContexts
.keySet().iterator();
while (contextIter.hasNext()) {
final TXManagerImpl.TXContext context = contextIter.next();
if ((tx = context.getJTA()) != null && txns.contains(tx)) {
context.clearJTAOnly();
contextIter.remove();
}
}
}
for (Object o : txns) {
globalTransactionMap.remove(o);
}
txns.clear();
}
class TransactionTimeOutThread implements Runnable {
protected volatile boolean toContinueRunning = true;
public void run() {
GlobalTransaction currentGtx = null;
long lag = 0;
LogWriterI18n logger = TransactionUtils.getLogWriterI18n();
while (toContinueRunning) { //Asif :Ensure that we do not get out of
try { // wait
//without a GTX object
synchronized (gtxSet) {
while (gtxSet.isEmpty() && toContinueRunning) {
gtxSet.wait();
}
if (!toContinueRunning) continue;
currentGtx = (GlobalTransaction) gtxSet.first();
}
//Asif : Check whether current GTX has timed out or not
boolean continueInner = true;
do {
synchronized (currentGtx) {
lag = System.currentTimeMillis() - currentGtx.getExpirationTime();
if (lag >= 0) {
//Asif: Expire the GTX .Do not worry if some GTX comes
// before it in Map , ie
// even if tehre is a GTX earlier than this one to expire ,
// it is OK
// to first take care of this one
//TODO: Do the clean up from all Maps
currentGtx.expireGTX();
gtxSet.remove(currentGtx);
}
}
synchronized (gtxSet) {
if (gtxSet.isEmpty()) {
continueInner = false;
}
else {
currentGtx = (GlobalTransaction) gtxSet.first();
boolean isGTXExp = false;
//Asif: There will not be any need for synchronizing
// on currentGTX as we are already taking lokc on gtxSet.
// Since the GTXSEt is locked that implies no GTX can be
// either removed or added ( if already removed)
//if the thread is in this block . thus we are safe
//
//synchronized (currentGtx) {
lag = System.currentTimeMillis() - currentGtx.getExpirationTime();
if (lag < 0) {
//Asif : Make the thread wait for stipluated
// time
isGTXExp = false;
}
else {
isGTXExp = true;
//Asif It is possibel that GTX is already expired but
// just got added in TreeSet bcoz of the small window
// in setTimeOut function of TranxnManager.
if (!currentGtx.isExpired()) {
currentGtx.expireGTX();
}
gtxSet.remove(currentGtx);
//Asif Clean the objects
if (gtxSet.isEmpty()) {
continueInner = false;
}
else {
currentGtx = (GlobalTransaction) gtxSet.first();
}
}
//}
//Asif : It is OK if we release the lock on Current GTX
// for sleep
//bcoz as we have still the the lock on gtxSet, even if
// the
//transaction set time out gets modified by other client
// thread,
// it will notbe added to the set as the lock is still
// with us.
//So in case of new tiemout value, if it is such that
// the GTX is
/// at the beginning , notified will be issued & the
// cleaner thread
// wil awake.
// the set as the lock is
if (!isGTXExp && toContinueRunning) {
gtxSet.wait(-(lag));
if (gtxSet.isEmpty()) {
continueInner = false;
} else {
currentGtx = (GlobalTransaction) gtxSet.first();
}
}
if (!toContinueRunning) {
continueInner = false;
}
}
} // synchronized
} while (continueInner);
}
catch (InterruptedException e) {
// No need to reset the bit; we'll exit.
if (toContinueRunning) {
logger.fine("TransactionTimeOutThread: unexpected exception", e);
}
return;
}
catch (CancelException e) {
// this thread is shutdown by doing an interrupt so this is expected
//logger.fine("TransactionTimeOutThread: encountered exception", e);
return;
}
catch (Exception e) {
if (logger.severeEnabled() && toContinueRunning) {
logger.severe(
LocalizedStrings.TransactionManagerImpl_TRANSACTIONTIMEOUTTHREAD__RUN_EXCEPTION_OCCURRED_WHILE_INSPECTING_GTX_FOR_EXPIRY, e);
}
}
}
}
}
static class GlobalTransactionComparator implements Comparator {
/**
* Sort the array in ascending order of expiration times
*/
public int compare(Object obj1, Object obj2) {
GlobalTransaction gtx1 = (GlobalTransaction) obj1;
GlobalTransaction gtx2 = (GlobalTransaction) obj2;
return gtx1.compare(gtx2);
}
/**
* Overwrite default equals implementation
*/
@Override
public boolean equals(Object o1) {
return this == o1;
}
}
/**
* Shutdown the transactionManager and threads associated with this.
*/
public static void refresh() {
getTransactionManager();
transactionManager.isActive = false;
transactionManager.cleaner.toContinueRunning = false;
try {
transactionManager.cleanUpThread.interrupt();
}
catch (Exception e) {
LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
if (writer.infoEnabled())
writer
.info(LocalizedStrings.TransactionManagerImpl_TRANSACTIONMANAGERIMPLCLEANUPEXCEPTION_WHILE_CLEANING_THREAD_BEFORE_RE_STATRUP);
}
/* try {
transactionManager.cleanUpThread.join();
}
catch (Exception e) {
e.printStackTrace();
}*/
transactionManager = null;
}
}