/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.
*/
package org.apache.geode.internal.jta;
/**
* <p>
* TransactionManager: A JTA compatible Transaction Manager.
* </p>
*
* @since GemFire 4.1.1
*/
import java.io.Serializable;
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 java.util.concurrent.ConcurrentHashMap;
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.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.apache.geode.CancelException;
import org.apache.geode.i18n.LogWriterI18n;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LoggingThreadGroup;
public class TransactionManagerImpl implements TransactionManager, Serializable {
private static final long serialVersionUID = 5033392316185449821L;
/**
* A mapping of Thread - Transaction Objects
*/
private Map transactionMap = new ConcurrentHashMap();
/**
* A mapping of Transaction - Global Transaction
*/
private 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 = LoggingThreadGroup.createThreadGroup(
LocalizedStrings.TransactionManagerImpl_CLEAN_UP_THREADS.toLocalizedString());
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");
}
Thread thread = Thread.currentThread();
if (transactionMap.get(thread) != 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();
transactionMap.put(thread, transaction);
GlobalTransaction globalTransaction = new GlobalTransaction();
globalTransactionMap.put(transaction, globalTransaction);
globalTransaction.addTransaction(transaction);
globalTransaction.setStatus(Status.STATUS_ACTIVE);
} 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});
throw new SystemException(exception);
}
}
/**
* 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);
}
Thread thread = Thread.currentThread();
transactionMap.remove(thread);
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);
}
Thread thread = Thread.currentThread();
transactionMap.remove(thread);
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 (null != txn) {
GlobalTransaction gtx = getGlobalTransaction(txn);
gtx.suspend();
transactionMap.remove(Thread.currentThread());
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 {
Thread thread = Thread.currentThread();
transactionMap.put(thread, 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);
throw new SystemException(exception);
}
}
/**
* 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());
}
Thread thread = Thread.currentThread();
Transaction txn = (Transaction) transactionMap.get(thread);
return txn;
}
/**
* Get the Global Transaction associated with the calling thread
*/
GlobalTransaction getGlobalTransaction() throws SystemException {
Transaction txn = getTransaction();
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 : This method is used only for testing purposes
Map getTransactionMap() {
return transactionMap;
}
// 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 tranxns) {
Object[] threads = transactionMap.keySet().toArray();
int len = threads.length;
Object tx = null;
boolean removed = false;
Object temp = null;
for (int i = 0; i < len; ++i) {
tx = transactionMap.get((temp = threads[i]));
removed = tranxns.remove(tx);
if (removed) {
transactionMap.remove(temp);
globalTransactionMap.remove(tx);
}
}
}
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;
}
}