/*
* 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.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 MethodDispacherInterceptor
{
protected TransactionManager txManager = null;
protected TransactionTable txTable = null;
@Inject
private void injectDependencies(TransactionManager txManager, TransactionTable txTable)
{
this.txManager = txManager;
this.txTable = txTable;
}
protected TransactionWorkspace getTransactionWorkspace(GlobalTransaction gtx) throws CacheException
{
OptimisticTransactionEntry transactionEntry = (OptimisticTransactionEntry) txTable.get(gtx);
if (transactionEntry == null)
{
throw new CacheException("Unable to map global transaction " + gtx + " 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.
*
* @param list
* @param n
*/
protected void greedyGetFqns(List<Fqn> list, NodeSPI<?, ?> n, Fqn newBase)
{
list.add(n.getFqn());
Fqn newFqn = new Fqn(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
*/
protected void undeleteWorkspaceNode(WorkspaceNode nodeToUndelete, WorkspaceNode parent)
{
nodeToUndelete.markAsDeleted(false);
nodeToUndelete.clearData();
// add in parent again
parent.addChild(nodeToUndelete);
nodeToUndelete.markAsResurrected(true);
}
protected WorkspaceNode lockAndCreateWorkspaceNode(NodeFactory nodeFactory, NodeSPI node, TransactionWorkspace workspace, GlobalTransaction gtx, long timeout)
{
boolean locked = false;
try
{
locked = node.getLock().acquireReadLock(gtx, timeout);
}
catch (InterruptedException e)
{
// do nothing
}
if (!locked)
throw new TimeoutException("Unable to lock node " + node.getFqn() + " after timeout " + timeout + " for copying into workspace");
WorkspaceNode wn = nodeFactory.createWorkspaceNode(node, workspace);
node.getLock().release(gtx);
return wn;
}
}