/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/transaction/SlideTransactionManager.java,v 1.14 2004/07/28 09:34:33 ib Exp $
* $Revision: 1.14 $
* $Date: 2004/07/28 09:34:33 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* 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.
*
*/
package org.apache.slide.transaction;
import java.util.Hashtable;
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.slide.util.Messages;
import org.apache.slide.util.logger.Logger;
import org.apache.slide.util.logger.SimpleLogger;
/**
* JTA Transaction manager implementation.
* <p>
* Implementation notes :
* <ul>
* <li>Does not support nested transactions</li>
* <li>No security</li>
* </ul>
*
* @version $Revision: 1.14 $
*/
public final class SlideTransactionManager implements TransactionManager {
// -------------------------------------------------------------- Constants
protected static final String LOG_CHANNEL =
SlideTransactionManager.class.getName();
public static final int DEFAULT_TRANSACTION_TIMEOUT = 30;
// ------------------------------------------------------------ Constructor
// ----------------------------------------------------- Instance Variables
/**
* Transaction bindings thread id <-> transaction object.
*/
private Hashtable bindings = new Hashtable();
/**
* Transaction bindings thread id <-> transaction timeout.
*/
private Hashtable timeouts = new Hashtable();
/**
* Associated logger.
*/
private Logger logger = new SimpleLogger();
// ------------------------------------------------------------- Properties
// --------------------------------------------------------- Public Methods
/**
* Get the logger associated with the transaction manager.
*/
public Logger getLogger() {
return logger;
}
/**
* Set the logger of the transaction manager.
*/
public void setLogger(Logger logger) {
this.logger = logger;
}
// --------------------------------------------- TransactionManager Methods
/**
* Create a new transaction and associate it with the current thread.
*
* @exception NotSupportedException Thrown if the thread is already
* associated with a transaction and the Transaction Manager
* implementation does not support nested transactions.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void begin()
throws NotSupportedException, SystemException {
Transaction currentTransaction = getTransaction();
if (currentTransaction != null)
throw new NotSupportedException();
currentTransaction = new SlideTransaction(this);
bindings.put(Thread.currentThread(), currentTransaction);
if (logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
String logMessage = Messages.format
(SlideTransactionManager.class.getName() + ".begin",
currentTransaction.toString());
logger.log(logMessage, LOG_CHANNEL, Logger.DEBUG);
}
}
/**
* Complete the transaction associated with the current thread. When this
* method completes, the thread becomes associated with no transaction.
* If the commit is terminated with an exception, the rollback should be
* called, to do a proper clean-up.
*
* @exception RollbackException Thrown to indicate that the transaction
* has been rolled back rather than committed.
* @exception HeuristicMixedException Thrown to indicate that a heuristic
* decision was made and that some relevant updates have been committed
* while others have been rolled back.
* @exception HeuristicRollbackException Thrown to indicate that a
* heuristic decision was made and that some relevant updates have been
* rolled back.
* @exception SecurityException Thrown to indicate that the thread is not
* allowed to commit the transaction.
* @exception IllegalStateException Thrown if the current thread is not
* associated with a transaction.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void commit()
throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException, IllegalStateException,
SystemException {
Thread currentThread = Thread.currentThread();
Transaction currentTransaction =
(Transaction) bindings.get(currentThread);
if (currentTransaction == null)
throw new IllegalStateException();
timeouts.remove(currentThread);
if (logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
String logMessage = Messages.format
(SlideTransactionManager.class.getName() + ".commit",
currentTransaction.toString());
logger.log(logMessage, LOG_CHANNEL, Logger.DEBUG);
}
try {
currentTransaction.commit();
} finally {
bindings.remove(currentThread);
}
}
/**
* Roll back the transaction associated with the current thread. When
* this method completes, the thread becomes associated with no
* transaction.
*
* @exception SecurityException Thrown to indicate that the thread is not
* allowed to commit the transaction.
* @exception IllegalStateException Thrown if the current thread is not
* associated with a transaction.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void rollback()
throws SecurityException, IllegalStateException, SystemException {
Thread currentThread = Thread.currentThread();
Transaction currentTransaction =
(Transaction) bindings.remove(currentThread);
if (currentTransaction == null)
throw new IllegalStateException();
timeouts.remove(currentThread);
String logMessage = Messages.format
(SlideTransactionManager.class.getName() + ".rollback",
currentTransaction.toString());
logger.log(logMessage, LOG_CHANNEL, Logger.DEBUG);
currentTransaction.rollback();
}
/**
* Modify the transaction associated with the current thread such that
* the only possible outcome of the transaction is to roll back the
* transaction.
*
* @exception IllegalStateException Thrown if the current thread is not
* associated with a transaction.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void setRollbackOnly()
throws IllegalStateException, SystemException {
Transaction currentTransaction = getTransaction();
if (currentTransaction == null)
throw new IllegalStateException();
String logMessage = Messages.format
(SlideTransactionManager.class.getName() + ".rollbackOnly",
currentTransaction.toString());
logger.log(logMessage, LOG_CHANNEL, Logger.INFO);
currentTransaction.setRollbackOnly();
}
/**
* Obtain the status of the transaction associated with the current thread.
*
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
* @return The transaction status. If no transaction is associated with
* the current thread, this method returns the Status.NoTransaction value.
*/
public int getStatus()
throws SystemException {
Transaction currentTransaction = getTransaction();
if (currentTransaction == null)
return Status.STATUS_NO_TRANSACTION;
return currentTransaction.getStatus();
}
/**
* Get the transaction object that represents the transaction context of
* the calling thread.
*
* @return the Transaction object representing the transaction associated
* with the calling thread.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public Transaction getTransaction()
throws SystemException {
return (Transaction) bindings.get(Thread.currentThread());
}
/**
* Resume the transaction context association of the calling thread with
* the transaction represented by the supplied Transaction object. When
* this method returns, the calling thread is associated with the
* transaction context specified.
*
* @param tobj The Transaction object that represents the transaction to
* be resumed.
* @exception InvalidTransactionException Thrown if the parameter
* transaction object contains an invalid transaction.
* @exception IllegalStateException Thrown if the thread is already
* associated with another transaction.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void resume(Transaction tobj)
throws InvalidTransactionException, IllegalStateException,
SystemException {
if (getTransaction() != null)
throw new IllegalStateException();
if (tobj == null)
throw new InvalidTransactionException();
bindings.put(Thread.currentThread(), tobj);
}
/**
* Suspend the transaction currently associated with the calling thread
* and return a Transaction object that represents the transaction
* context being suspended. If the calling thread is not associated with
* a transaction, the method returns a null object reference. When this
* method returns, the calling thread is associated with no transaction.
*
* @return Transaction object representing the suspended transaction.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public Transaction suspend()
throws SystemException {
Transaction currentTransaction = getTransaction();
if (currentTransaction != null) {
Thread currentThread = Thread.currentThread();
bindings.remove(currentThread);
timeouts.remove(currentThread);
}
return currentTransaction;
}
/**
* Modify the value of the timeout value that is associated with the
* transactions started by the current thread with the begin method.
* <p>
* If an application has not called this method, the transaction service
* uses some default value for the transaction timeout.
*
* @param seconds The value of the timeout in seconds. If the value is
* zero, the transaction service restores the default value.
* @exception SystemException Thrown if the transaction manager encounters
* an unexpected error condition.
*/
public void setTransactionTimeout(int seconds)
throws SystemException {
timeouts.put(Thread.currentThread(), new Integer(seconds));
}
}