/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.interceptors;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.NodeFactory;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.interceptors.base.CommandInterceptor;
import org.jboss.cache.lock.LockManager;
import static org.jboss.cache.lock.LockType.READ;
import org.jboss.cache.lock.TimeoutException;
import org.jboss.cache.optimistic.TransactionWorkspace;
import org.jboss.cache.optimistic.WorkspaceNode;
import org.jboss.cache.transaction.GlobalTransaction;
import org.jboss.cache.transaction.OptimisticTransactionEntry;
import org.jboss.cache.transaction.TransactionTable;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.List;
/**
* Abstract interceptor for optimistic locking
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
*/
public abstract class OptimisticInterceptor extends CommandInterceptor
{
protected TransactionManager txManager;
protected TransactionTable txTable;
protected LockManager lockManager;
@Inject
private void injectDependencies(TransactionManager txManager, TransactionTable txTable, LockManager lockManager)
{
this.txManager = txManager;
this.txTable = txTable;
this.lockManager = lockManager;
}
protected TransactionWorkspace getTransactionWorkspace(InvocationContext ctx) throws CacheException
{
OptimisticTransactionEntry transactionEntry = (OptimisticTransactionEntry) ctx.getTransactionEntry();
if (transactionEntry == null)
{
throw new CacheException("Unable to map global transaction " + ctx.getGlobalTransaction() + " to transaction entry when trying to retrieve transaction workspace.");
}
// try and get the workspace from the transaction
return transactionEntry.getTransactionWorkSpace();
}
/**
* Adds the Fqn of the node as well as all children and childrens children to the list.
*/
protected void greedyGetFqns(List<Fqn> list, NodeSPI<?, ?> n, Fqn newBase)
{
list.add(n.getFqn());
Fqn newFqn = Fqn.fromRelativeElements(newBase, n.getFqn().getLastElement());
list.add(newFqn);
for (NodeSPI child : n.getChildrenDirect())
{
greedyGetFqns(list, child, newFqn);
}
}
/**
* @return the {@link org.jboss.cache.transaction.GlobalTransaction}, extracted from the current {@link org.jboss.cache.InvocationContext}.
* @throws CacheException if the {@link org.jboss.cache.transaction.GlobalTransaction} or {@link javax.transaction.Transaction} associated with the
* {@link org.jboss.cache.InvocationContext} is null.
*/
protected GlobalTransaction getGlobalTransaction(InvocationContext ctx) throws CacheException
{
Transaction tx = ctx.getTransaction();
if (tx == null) throw new CacheException("Transaction associated with the current invocation is null!");
GlobalTransaction gtx = ctx.getGlobalTransaction();
if (gtx == null) throw new CacheException("GlobalTransaction associated with the current invocation is null!");
return gtx;
}
protected void undeleteWorkspaceNode(WorkspaceNode nodeToUndelete, TransactionWorkspace workspace)
{
undeleteWorkspaceNode(nodeToUndelete, workspace.getNode(nodeToUndelete.getFqn().getParent()));
}
/**
* Undeletes a node that already exists in the workspace, by setting appropriate flags and re-adding to parent's child map.
*
* @param nodeToUndelete WorkspaceNode to undelete
* @param parent parent of node to undelete
*/
@SuppressWarnings("unchecked")
protected void undeleteWorkspaceNode(WorkspaceNode nodeToUndelete, WorkspaceNode parent)
{
nodeToUndelete.markAsDeleted(false);
nodeToUndelete.clearData();
// add in parent again
parent.addChild(nodeToUndelete);
nodeToUndelete.markAsResurrected(true);
}
@SuppressWarnings("unchecked")
protected WorkspaceNode lockAndCreateWorkspaceNode(NodeFactory nodeFactory, NodeSPI node, TransactionWorkspace workspace, GlobalTransaction gtx, long timeout)
{
boolean locked = lockManager.lock(node, READ, gtx, timeout);
if (!locked)
throw new TimeoutException("Unable to lock node " + node.getFqn() + " after timeout " + timeout + " for copying into workspace");
WorkspaceNode wn = nodeFactory.createWorkspaceNode(node, workspace);
lockManager.unlock(node, gtx);
return wn;
}
}