/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.util;
import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionProxy;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Utility class for maintaining the association between transactions and
* instances of {@link TransactionContext}.
*
* @param <T> the type of transaction context
* @see TransactionContextFactory
*/
public class TransactionContextMap<T extends TransactionContext> {
/** The logger for this class. */
private static final LoggerWrapper logger =
new LoggerWrapper(
Logger.getLogger(TransactionContextMap.class.getName()));
/** Provides transaction and other information for the current thread. */
private final ThreadLocal<T> currentContext = new ThreadLocal<T>();
/** The transaction proxy. */
private final TransactionProxy txnProxy;
/**
* Constructs an instance of this class with the given {@code
* TransactionProxy}.
*
* @param txnProxy the transaction proxy
*/
public TransactionContextMap(TransactionProxy txnProxy) {
if (txnProxy == null) {
throw new NullPointerException("null txnProxy");
}
this.txnProxy = txnProxy;
}
/**
* Makes sure the participant obtained from {@code contextFactory} is
* joined to the current transaction and returns the associated
* context. If the participant has not yet joined the current
* transaction, creates a new context by invoking {@link
* TransactionContextFactory#createContext createContext} on {@code
* contextFactory}, passing the current transaction, sets that
* context as the current context for the current thread, and joins
* the transaction. Returns the context for the current transaction.
*
* @param contextFactory the context factory
* @return the context for the current transaction
* @throws TransactionNotActiveException if no transaction is active
* @throws IllegalStateException if there is a problem with the
* state of the transaction.
*/
public T joinTransaction(TransactionContextFactory<T> contextFactory) {
if (contextFactory == null) {
throw new NullPointerException("null contextFactory");
}
Transaction txn = txnProxy.getCurrentTransaction();
if (txn == null) {
throw new TransactionNotActiveException(
"No transaction is active");
}
T context = currentContext.get();
if (context == null) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "join txn:{0}", txn);
}
context = contextFactory.createContext(txn);
currentContext.set(context);
txn.join(contextFactory.getParticipant());
} else if (!txn.equals(context.getTransaction())) {
clearContext();
throw new IllegalStateException(
"Wrong transaction: Expected " + context.getTransaction() +
", found " + txn);
}
return context;
}
/** Removes the currently active transaction context. */
public void clearContext() {
currentContext.set(null);
}
/**
* Returns the context for the current transaction.
*
* @return the context for the current transaction
* @throws TransactionNotActiveException if no transaction is active
* @throws IllegalStateException if there is a problem with the
* state of the transaction.
*/
public T getContext() {
Transaction txn = txnProxy.getCurrentTransaction();
T context = currentContext.get();
if (context == null) {
throw new IllegalStateException(
"Not participating in transaction " + txn);
} else if (!txn.equals(context.getTransaction())) {
throw new IllegalStateException(
"Wrong transaction: Expected " + context.getTransaction() +
", found " + txn);
}
return context;
}
/**
* Checks that the specified {@code context} is currently active,
* throwing {@code TransactionNotActiveException} if it isn't.
*
* @param context a context
*
* @throws TransactionNotActiveException if the specified
* {@code context} is not currently active
*/
public void checkContext(T context) {
if (context == null) {
throw new NullPointerException("null context");
}
/* Make sure the current transaction is active */
txnProxy.getCurrentTransaction();
T threadContext = currentContext.get();
if (threadContext == null) {
throw new TransactionNotActiveException(
"Transaction " + context.getTransaction() +
" is not the current transaction");
} else if (context != threadContext) {
throw new TransactionNotActiveException(
"Wrong transaction: Expected " +
context.getTransaction() + ", found " +
threadContext.getTransaction());
}
}
/**
* Checks the specified transaction, throwing {@code
* IllegalStateException} if the current context is {@code null}
* or if the specified transaction is not equal to the transaction
* in the current context.
*
* @param txn a transaction
* @return the current transaction context
*/
public T checkTransaction(Transaction txn) {
if (txn == null) {
throw new NullPointerException("null transaction");
}
T context = currentContext.get();
if (context == null) {
throw new IllegalStateException("null context");
}
if (!txn.equals(context.getTransaction())) {
throw new IllegalStateException(
"Wrong transaction: Expected " + context.getTransaction() +
", found " + txn);
}
return context;
}
}