Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 600   Methods: 18
NCLOC: 420   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
PessimisticLockInterceptor.java 82.4% 91.1% 100% 89%
coverage coverage
 1    /*
 2    * JBoss, Home of Professional Open Source
 3    *
 4    * Distributable under LGPL license.
 5    * See terms of license at gnu.org.
 6    */
 7    package org.jboss.cache.interceptors;
 8   
 9    import org.jboss.cache.CacheImpl;
 10    import org.jboss.cache.CacheSPI;
 11    import org.jboss.cache.Fqn;
 12    import org.jboss.cache.InvocationContext;
 13    import org.jboss.cache.NodeSPI;
 14    import org.jboss.cache.lock.IsolationLevel;
 15    import org.jboss.cache.lock.LockingException;
 16    import org.jboss.cache.lock.NodeLock;
 17    import org.jboss.cache.lock.TimeoutException;
 18    import org.jboss.cache.marshall.MethodCall;
 19    import org.jboss.cache.marshall.MethodDeclarations;
 20    import org.jboss.cache.transaction.GlobalTransaction;
 21    import org.jboss.cache.transaction.TransactionEntry;
 22    import org.jboss.cache.transaction.TransactionTable;
 23   
 24    import java.util.Collections;
 25    import java.util.Iterator;
 26    import java.util.LinkedList;
 27    import java.util.List;
 28    import java.util.Map;
 29    import java.util.Set;
 30   
 31    /**
 32    * An interceptor that handles locking. When a TX is associated, we register
 33    * for TX completion and unlock the locks acquired within the scope of the TX.
 34    * When no TX is present, we keep track of the locks acquired during the
 35    * current method and unlock when the method returns.
 36    *
 37    * @author Bela Ban
 38    * @version $Id: PessimisticLockInterceptor.java,v 1.54 2007/06/12 07:39:50 msurtani Exp $
 39    */
 40    public class PessimisticLockInterceptor extends Interceptor
 41    {
 42    private TransactionTable tx_table = null;
 43   
 44    boolean writeLockOnChildInsertRemove = true;
 45   
 46    /**
 47    * Map<Object, java.util.List>. Keys = threads, values = lists of locks held by that thread
 48    */
 49    private Map<Thread, List<NodeLock>> lock_table;
 50    private long lock_acquisition_timeout;
 51    private LockManager lockManager = new LockManager();
 52   
 53   
 54  4839 public void setCache(CacheSPI cache)
 55    {
 56  4839 super.setCache(cache);
 57  4839 tx_table = cache.getTransactionTable();
 58  4839 lock_table = cache.getLockTable();
 59  4839 lock_acquisition_timeout = cache.getConfiguration().getLockAcquisitionTimeout();
 60  4839 writeLockOnChildInsertRemove = cache.getConfiguration().isLockParentForChildInsertRemove();
 61    }
 62   
 63   
 64  1569803 public Object invoke(InvocationContext ctx) throws Throwable
 65    {
 66  1569809 MethodCall m = ctx.getMethodCall();
 67  1569809 Fqn fqn = null;
 68  1569769 NodeLock.LockType lock_type = NodeLock.LockType.NONE;
 69  1569809 Object[] args = m.getArgs();
 70  1569809 boolean lockNecessary = false;
 71  1569809 boolean locksAlreadyObtained = false;
 72   
 73  0 if (log.isTraceEnabled()) log.trace("PessimisticLockInterceptor invoked for method " + m);
 74  1569809 if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isSuppressLocking())
 75    {
 76  11 log.trace("Suppressing locking");
 77  11 switch (m.getMethodId())
 78    {
 79  4 case MethodDeclarations.putDataMethodLocal_id:
 80  0 case MethodDeclarations.putDataEraseMethodLocal_id:
 81  4 case MethodDeclarations.putKeyValMethodLocal_id:
 82  8 log.trace("Creating nodes if necessary");
 83  8 createNodes((Fqn) args[1], ctx.getGlobalTransaction());
 84  8 break;
 85    }
 86   
 87  11 return super.invoke(ctx);
 88    }
 89   
 90    /** List<IdentityLock> locks. Locks acquired during the current method; will be released later by UnlockInterceptor.
 91    * This list is only populated when there is no TX, otherwise the TransactionTable maintains the locks
 92    * (keyed by TX) */
 93    // List locks=null;
 94   
 95  1569768 boolean recursive = false;
 96  1569798 boolean createIfNotExists = false;
 97  1569798 boolean zeroLockTimeout = false;// only used if the call is an evict() call. See JBCACHE-794
 98  1569798 boolean isDeleteOperation = false;// needed for JBCACHE-871
 99  1569798 boolean isEvictOperation = false;
 100   
 101    // 1. Determine the type of lock (read, write, or none) depending on the method. If no lock is required, invoke
 102    // the method, then return immediately
 103    // Set the Fqn
 104  1569798 switch (m.getMethodId())
 105    {
 106  51 case MethodDeclarations.moveMethodLocal_id:
 107  51 fqn = (Fqn) args[0];
 108  51 obtainLocksForMove(ctx, fqn, (Fqn) args[1]);
 109  51 locksAlreadyObtained = true;
 110  51 lockNecessary = true;
 111  51 isDeleteOperation = true;
 112  51 break;
 113  11982 case MethodDeclarations.putDataMethodLocal_id:
 114  3 case MethodDeclarations.putDataEraseMethodLocal_id:
 115  406662 case MethodDeclarations.putKeyValMethodLocal_id:
 116  418647 createIfNotExists = true;
 117  418647 fqn = (Fqn) args[1];
 118  418647 lock_type = NodeLock.LockType.WRITE;
 119  418647 break;
 120  14 case MethodDeclarations.putForExternalReadMethodLocal_id:
 121  14 createIfNotExists = true;
 122  14 fqn = (Fqn) args[1];
 123  14 lock_type = NodeLock.LockType.WRITE;
 124  14 zeroLockTimeout = true;
 125  14 break;
 126  17062 case MethodDeclarations.removeNodeMethodLocal_id:
 127  17062 isDeleteOperation = true;
 128  17062 fqn = (Fqn) args[1];
 129  17062 lock_type = NodeLock.LockType.WRITE;
 130  17062 recursive = true;// remove node and *all* child nodes
 131  17062 break;
 132  4305 case MethodDeclarations.removeKeyMethodLocal_id:
 133  1998 case MethodDeclarations.removeDataMethodLocal_id:
 134  0 case MethodDeclarations.addChildMethodLocal_id:
 135  6303 fqn = (Fqn) args[1];
 136  6303 lock_type = NodeLock.LockType.WRITE;
 137  6303 break;
 138  117903 case MethodDeclarations.evictNodeMethodLocal_id:
 139  117903 zeroLockTimeout = true;
 140  117903 fqn = (Fqn) args[0];
 141  117903 lock_type = NodeLock.LockType.WRITE;
 142  117903 isEvictOperation = true;
 143  117903 break;
 144  463475 case MethodDeclarations.getKeyValueMethodLocal_id:
 145  373062 case MethodDeclarations.getNodeMethodLocal_id:
 146  2646 case MethodDeclarations.getKeysMethodLocal_id:
 147  24897 case MethodDeclarations.getChildrenNamesMethodLocal_id:
 148  3 case MethodDeclarations.releaseAllLocksMethodLocal_id:
 149  22 case MethodDeclarations.printMethodLocal_id:
 150  864105 fqn = (Fqn) args[0];
 151  864105 lock_type = NodeLock.LockType.READ;
 152  864105 break;
 153  3455 case MethodDeclarations.lockMethodLocal_id:
 154  3455 fqn = (Fqn) args[0];
 155  3455 lock_type = (NodeLock.LockType) args[1];
 156  3455 recursive = (Boolean) args[2];
 157  3455 break;
 158  68977 case MethodDeclarations.commitMethod_id:
 159    // commit propagated up from the tx interceptor
 160  68977 commit(ctx.getGlobalTransaction());
 161  68977 break;
 162  269 case MethodDeclarations.rollbackMethod_id:
 163    // rollback propagated up from the tx interceptor
 164  269 rollback(ctx.getGlobalTransaction());
 165  269 break;
 166  73012 default:
 167  73012 if (isOnePhaseCommitPrepareMehod(m))
 168    {
 169    // commit propagated up from the tx interceptor
 170  59 commit(ctx.getGlobalTransaction());
 171    }
 172  73012 break;
 173    }
 174   
 175    // Lock the node (must be either read or write if we get here)
 176    // If no TX: add each acquired lock to the list of locks for this method (locks)
 177    // If TX: [merge code from TransactionInterceptor]: register with TxManager, on commit/rollback,
 178    // release the locks for the given TX
 179  1569798 if (fqn != null)
 180    {
 181  1427524 if (!locksAlreadyObtained)
 182    {
 183  1427473 do
 184    {
 185  1427506 lock(ctx, fqn, lock_type, recursive, createIfNotExists, zeroLockTimeout ? 0 : lock_acquisition_timeout, isDeleteOperation, isEvictOperation);
 186    }
 187  1427439 while (createIfNotExists && cache.peek(fqn, false) == null);// keep trying until we have the lock (fixes concurrent remove())
 188    }
 189    }
 190  142274 else if (!lockNecessary)
 191    {
 192  142274 if (log.isTraceEnabled())
 193    {
 194  0 log.trace("bypassed locking as method " + m.getName() + "() doesn't require locking");
 195    }
 196    }
 197  1569731 if (m.getMethodId() == MethodDeclarations.lockMethodLocal_id)
 198    {
 199  3455 return null;
 200    }
 201  1566276 Object o = super.invoke(ctx);
 202    // FIXME this should be done in UnlockInterceptor, but I didn't want
 203    // to add the removedNodes map to CacheImpl
 204  1566270 if (isDeleteOperation && ctx.getGlobalTransaction() == null)
 205    {
 206    //cache.getRemovedNodesMap().remove(fqn);
 207    //cache.peek(fqn);
 208    // do a REAL remove here.
 209  5560 NodeSPI n = cache.peek(fqn, true);
 210  5560 if (n != null)
 211    {
 212  3318 lockManager.getLock(n).releaseAll(Thread.currentThread());
 213    }
 214  5560 ((CacheImpl) cache).realRemove(fqn, true);
 215   
 216    }
 217    else
 218  1560710 if (m.getMethodId() == MethodDeclarations.commitMethod_id || isOnePhaseCommitPrepareMehod(m) || m.getMethodId() == MethodDeclarations.rollbackMethod_id)
 219    {
 220  69304 cleanup(ctx.getGlobalTransaction());
 221    }
 222  1566270 return o;
 223    }
 224   
 225  51 private void obtainLocksForMove(InvocationContext ctx, Fqn node, Fqn parent) throws InterruptedException
 226    {
 227    // parent node (new parent) and current node's existing parent should both get RLs.
 228    // node should have a WL.
 229   
 230    // this call will ensure the node gets a WL and it's current parent gets RL.
 231  0 if (log.isTraceEnabled()) log.trace("Attempting to get WL on node to be moved [" + node + "]");
 232  51 lock(ctx, node, NodeLock.LockType.WRITE, true, false, lock_acquisition_timeout, true, false);
 233   
 234    //now for an RL for the new parent.
 235  0 if (log.isTraceEnabled()) log.trace("Attempting to get RL on new parent [" + parent + "]");
 236  51 lock(ctx, parent, NodeLock.LockType.READ, true, false, lock_acquisition_timeout, false, false);
 237    }
 238   
 239   
 240    /**
 241    * Locks a given node.
 242    *
 243    * @param fqn
 244    * @param lock_type DataNode.LOCK_TYPE_READ, DataNode.LOCK_TYPE_WRITE or DataNode.LOCK_TYPE_NONE
 245    * @param recursive Lock children recursively
 246    */
 247  1427607 private void lock(InvocationContext ctx, Fqn fqn, NodeLock.LockType lock_type, boolean recursive, boolean createIfNotExists, long timeout, boolean isDeleteOperation, boolean isEvictionOperation)
 248    throws TimeoutException, LockingException, InterruptedException
 249    {
 250  1427608 NodeSPI n;
 251  1427608 NodeSPI child_node;
 252  1427608 Object child_name;
 253  1427608 Thread currentThread = Thread.currentThread();
 254  1427608 GlobalTransaction gtx = ctx.getGlobalTransaction();
 255  1427608 Object owner = (gtx != null) ? gtx : currentThread;
 256  1427608 int treeNodeSize;
 257   
 258  0 if (log.isTraceEnabled()) log.trace("Attempting to lock node " + fqn + " for owner " + owner);
 259   
 260  1427608 if (fqn == null)
 261    {
 262  0 log.error("fqn is null - this should not be the case");
 263  0 return;
 264    }
 265   
 266  1427608 if (configuration.getIsolationLevel() == IsolationLevel.NONE)
 267    {
 268  46 lock_type = NodeLock.LockType.NONE;
 269    }
 270   
 271  1427608 n = cache.getRoot();
 272  1427608 treeNodeSize = fqn.size();
 273  1427608 for (int i = -1; i < treeNodeSize; i++)
 274    {
 275  8566341 if (i == -1)
 276    {
 277    // this is the root node
 278  1427608 child_name = Fqn.ROOT.getLastElement();
 279  1427608 child_node = n;
 280    }
 281    else
 282    {
 283  7138733 child_name = fqn.get(i);
 284  7138733 child_node = n.getChildDirect(child_name);
 285    }
 286    /*
 287    cache.getInvocationContext().getOptionOverrides().setBypassInterceptorChain(true);
 288    Fqn childFqn = new Fqn(child_name);
 289    child_node = n.getChild(new Fqn(child_name));
 290    */
 291  8566341 if (child_node == null && createIfNotExists)
 292    {
 293  163968 child_node = n.addChildDirect(new Fqn(child_name));
 294    }
 295   
 296  8566340 if (child_node == null)
 297    {
 298  86038 if (log.isTraceEnabled())
 299    {
 300  0 log.trace("failed to find or create child " + child_name + " of node " + n);
 301    }
 302  86038 return;
 303    }
 304   
 305  8480297 NodeLock.LockType lockTypeRequired;
 306  8480302 if (lock_type == NodeLock.LockType.NONE)
 307    {
 308    // acquired=false;
 309  166 n = child_node;
 310  166 continue;
 311    }
 312    else
 313    {
 314  8480136 if (writeLockNeeded(ctx, lock_type, i, treeNodeSize, isEvictionOperation, isDeleteOperation, createIfNotExists, fqn, child_node.getFqn()))
 315    {
 316    //acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_WRITE);
 317    //acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.WRITE, timeout);
 318  521646 lockTypeRequired = NodeLock.LockType.WRITE;
 319   
 320    }
 321    else
 322    {
 323    //acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_READ);
 324    //acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.READ, timeout);
 325  7958490 lockTypeRequired = NodeLock.LockType.READ;
 326    }
 327    }
 328   
 329    // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
 330  8480136 if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), lock_type, isDeleteOperation, createIfNotExists))
 331    {
 332  449 reverseRemove(child_node);
 333    }
 334   
 335  8479472 acquireNodeLock(child_node, owner, gtx, lockTypeRequired, timeout);
 336   
 337  8480070 if (recursive && isTargetNode(i, treeNodeSize))
 338    {
 339    //Set acquired_locks=child_node.acquireAll(owner, lock_timeout, lock_type);
 340  14478 Set<NodeLock> acquired_locks = lockManager.acquireAll(child_node, owner, lock_type, timeout);
 341  14478 if (acquired_locks.size() > 0)
 342    {
 343  990 if (gtx != null)
 344    {
 345  51 cache.getTransactionTable().addLocks(gtx, acquired_locks);
 346    }
 347    else
 348    {
 349  939 List<NodeLock> locks = getLocks(currentThread);
 350  939 locks.addAll(acquired_locks);
 351    }
 352    }
 353    }
 354  8480070 n = child_node;
 355    }
 356   
 357    // Add the Fqn to be removed to the transaction entry so we can clean up after ourselves during commit/rollback
 358  10853 if (isDeleteOperation && gtx != null) cache.getTransactionTable().get(gtx).addRemovedNode(fqn);
 359    }
 360   
 361  1883314 private boolean needToReverseRemove(NodeSPI n, TransactionEntry te, NodeLock.LockType lockTypeRequested, boolean isRemoveOperation, boolean createIfNotExists)
 362    {
 363  1883313 return !isRemoveOperation && createIfNotExists && lockTypeRequested == NodeLock.LockType.WRITE && n.isDeleted() && te.getRemovedNodes().contains(n.getFqn());
 364    }
 365   
 366  449 private void reverseRemove(NodeSPI n)
 367    {
 368  449 n.markAsDeleted(false);
 369    }
 370   
 371  8480136 private boolean writeLockNeeded(InvocationContext ctx, NodeLock.LockType lock_type, int currentNodeIndex, int treeNodeSize, boolean isEvictOperation, boolean isRemoveOperation, boolean createIfNotExists, Fqn targetFqn, Fqn currentFqn)
 372    {
 373    // write lock forced!!
 374  8480136 boolean isTargetNode = isTargetNode(currentNodeIndex, treeNodeSize);
 375   
 376  3 if (ctx.getOptionOverrides().isForceWriteLock() && isTargetNode) return true;
 377   
 378  8480133 if (cache.getConfiguration().isLockParentForChildInsertRemove())
 379    {
 380  14744 if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
 381    {
 382  776 return true;// we're doing a remove and we've reached the PARENT node of the target to be removed.
 383    }
 384   
 385  13968 if (!isTargetNode && cache.peek(new Fqn(currentFqn, targetFqn.get(currentNodeIndex + 1)), false) == null)
 386    {
 387  812 return createIfNotExists;// we're at a node in the tree, not yet at the target node, and we need to create the next node. So we need a WL here.
 388    }
 389    }
 390  8478545 return lock_type == NodeLock.LockType.WRITE && isTargetNode && (createIfNotExists || isRemoveOperation || isEvictOperation);//normal operation, write lock explicitly requested and this is the target to be written to.
 391    }
 392   
 393  8570989 private boolean isTargetNode(int nodePosition, int treeNodeSize)
 394    {
 395  8570989 return nodePosition == (treeNodeSize - 1);
 396    }
 397   
 398  8480136 private void acquireNodeLock(NodeSPI node, Object owner, GlobalTransaction gtx, NodeLock.LockType lock_type, long lock_timeout) throws LockingException, TimeoutException, InterruptedException
 399    {
 400  8480136 boolean acquired = lockManager.acquire(node, owner, lock_type, lock_timeout);
 401  8480070 if (acquired)
 402    {
 403    // Record the lock for release on method return or tx commit/rollback
 404  6873746 recordNodeLock(gtx, lockManager.getLock(node));
 405    }
 406    else
 407    {
 408    //if (log.isDebugEnabled()) log.debug("Unable to acquire lock on node " + node.getFqn());
 409    //throw new LockingException("Could not acquite lock for " + node.getFqn());
 410    }
 411    }
 412   
 413  6873746 private void recordNodeLock(GlobalTransaction gtx, NodeLock lock)
 414    {
 415  6873746 if (gtx != null)
 416    {
 417    // add the lock to the list of locks maintained for this transaction
 418    // (needed for release of locks on commit or rollback)
 419  291916 cache.getTransactionTable().addLock(gtx, lock);
 420    }
 421    else
 422    {
 423  6581830 Thread currentThread = Thread.currentThread();
 424  6581830 List<NodeLock> locks = getLocks(currentThread);
 425  6581830 if (!locks.contains(lock))
 426    {
 427  6581828 locks.add(lock);
 428  6581828 lock_table.put(currentThread, locks);
 429    }
 430    }
 431    }
 432   
 433  6582769 private List<NodeLock> getLocks(Thread currentThread)
 434    {
 435    // This sort of looks like a get/put race condition, but
 436    // since we key off the Thread, it's not
 437  6582729 List<NodeLock> locks = lock_table.get(currentThread);
 438  6582769 if (locks == null)
 439    {
 440  1072815 locks = Collections.synchronizedList(new LinkedList<NodeLock>());
 441  1072815 lock_table.put(currentThread, locks);
 442    }
 443  6582769 return locks;
 444    }
 445   
 446   
 447  8 private void createNodes(Fqn fqn, GlobalTransaction gtx)
 448    {
 449  8 int treeNodeSize;
 450  0 if ((treeNodeSize = fqn.size()) == 0) return;
 451  8 NodeSPI n = cache.getRoot();
 452  8 for (int i = 0; i < treeNodeSize; i++)
 453    {
 454  16 Object child_name = fqn.get(i);
 455  16 Fqn childFqn = new Fqn(child_name);
 456   
 457  16 NodeSPI child_node = n.getChildDirect(childFqn);
 458  9 if (child_node == null) child_node = n.addChildDirect(childFqn);
 459    // test if this node needs to be 'undeleted'
 460    // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
 461  16 if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), NodeLock.LockType.WRITE, false, true))
 462    {
 463  0 reverseRemove(child_node);
 464    }
 465   
 466  16 if (child_node == null)
 467    {
 468  0 if (log.isTraceEnabled())
 469    {
 470  0 log.trace("failed to find or create child " + child_name + " of node " + n.getFqn());
 471    }
 472  0 return;
 473    }
 474  16 n = child_node;
 475    }
 476    }
 477   
 478    /**
 479    * Remove all locks held by <tt>tx</tt>, remove the transaction from the transaction table
 480    *
 481    * @param gtx
 482    */
 483  69036 private void commit(GlobalTransaction gtx)
 484    {
 485  69036 if (log.isTraceEnabled())
 486    {
 487  0 log.trace("committing cache with gtx " + gtx);
 488    }
 489   
 490  69036 TransactionEntry entry = tx_table.get(gtx);
 491  69036 if (entry == null)
 492    {
 493  0 log.error("entry for transaction " + gtx + " not found (maybe already committed)");
 494  0 return;
 495    }
 496   
 497    // first remove nodes that should be deleted.
 498  69036 Iterator removedNodes = entry.getRemovedNodes().iterator();
 499  69036 CacheImpl cacheImpl = (CacheImpl) cache;
 500  69036 while (removedNodes.hasNext())
 501    {
 502  10741 Fqn f = (Fqn) removedNodes.next();
 503  10741 cacheImpl.realRemove(f, false);
 504    }
 505    }
 506   
 507  69304 private void cleanup(GlobalTransaction gtx)
 508    {
 509  69304 TransactionEntry entry = tx_table.get(gtx);
 510    // Let's do it in stack style, LIFO
 511  69300 entry.releaseAllLocksLIFO(gtx);
 512   
 513    /*
 514    Transaction ltx = entry.getTransaction();
 515    if (log.isTraceEnabled())
 516    {
 517    log.trace("removing local transaction " + ltx + " and global transaction " + gtx);
 518    }
 519    tx_table.remove(ltx);
 520    tx_table.remove(gtx);
 521    */
 522    }
 523   
 524    /**
 525    * Revert all changes made inside this TX: invoke all method calls of the undo-ops
 526    * list. Then release all locks and remove the TX from the transaction table.
 527    * <ol>
 528    * <li>Revert all modifications done in the current TX<li/>
 529    * <li>Release all locks held by the current TX</li>
 530    * <li>Remove all temporary nodes created by the current TX</li>
 531    * </ol>
 532    *
 533    * @param tx
 534    */
 535  269 private void rollback(GlobalTransaction tx)
 536    {
 537  269 TransactionEntry entry = tx_table.get(tx);
 538   
 539  269 if (log.isTraceEnabled())
 540    {
 541  0 log.trace("called to rollback cache with GlobalTransaction=" + tx);
 542    }
 543   
 544  269 if (entry == null)
 545    {
 546  0 log.error("entry for transaction " + tx + " not found (transaction has possibly already been rolled back)");
 547  0 return;
 548    }
 549   
 550  269 Iterator removedNodes = entry.getRemovedNodes().iterator();
 551  269 CacheImpl cacheImpl = (CacheImpl) cache;
 552  269 while (removedNodes.hasNext())
 553    {
 554  110 Fqn f = (Fqn) removedNodes.next();
 555  110 cacheImpl.realRemove(f, false);
 556   
 557    }
 558   
 559    // 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions !
 560  269 entry.undoOperations(cache);
 561   
 562    // This was removed as we don't use temporary nodes anymore; we now create undo-operations on put(), e.g.
 563    // put(/a/b/c) on /a, create b and c, plus undo operations _remove(a/b/c) and _remove(/a/b)
 564   
 565    // 2. Remove all temporary nodes. Need to do it backwards since node is LIFO.
 566    // for(ListIterator it=new LinkedList(entry.getNodes()).listIterator(entry.getNodes().size());
 567    // it.hasPrevious();) {
 568    // node_name=(Fqn)it.previous();
 569    // try {
 570    // cache._remove(tx, node_name, false);
 571    // }
 572    // catch(Throwable t) {
 573    // log.error("failed removing node \"" + node_name + "\"", t);
 574    // }
 575    // }
 576   
 577    // 3. Finally, release all locks held by this TX
 578    // Let's do it in stack style, LIFO
 579    // Note that the lock could have been released already so don't panic.
 580    }
 581   
 582    private static class LockManager
 583    {
 584  8480136 boolean acquire(NodeSPI node, Object owner, NodeLock.LockType lockType, long timeout) throws InterruptedException
 585    {
 586  8480136 return getLock(node).acquire(owner, timeout, lockType);
 587    }
 588   
 589  15371219 NodeLock getLock(NodeSPI node)
 590    {
 591  15371678 return node.getLock();
 592    }
 593   
 594  14478 public Set<NodeLock> acquireAll(NodeSPI node, Object owner, NodeLock.LockType lockType, long timeout) throws InterruptedException
 595    {
 596  14478 return getLock(node).acquireAll(owner, timeout, lockType);
 597    }
 598    }
 599   
 600    }