[exo-jcr-commits] exo-jcr SVN: r1532 - jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Jan 21 11:13:00 EST 2010


Author: nzamosenchuk
Date: 2010-01-21 11:13:00 -0500 (Thu, 21 Jan 2010)
New Revision: 1532

Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManager.java
Log:
EXOJCR-424: added sorting in onSaveItems() in cacheable Lock manager to avoid deadlocks.

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManager.java	2010-01-21 14:48:20 UTC (rev 1531)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManager.java	2010-01-21 16:13:00 UTC (rev 1532)
@@ -44,7 +44,6 @@
 import org.exoplatform.services.jcr.impl.core.lock.SessionLockManager;
 import org.exoplatform.services.jcr.impl.dataflow.TransientItemData;
 import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
-import org.exoplatform.services.jcr.impl.dataflow.persistent.TxIsolatedOperation;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspacePersistentDataManager;
 import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
 import org.exoplatform.services.jcr.observation.ExtendedEvent;
@@ -64,7 +63,6 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -164,7 +162,7 @@
    private final Fqn<String> lockRoot;
 
    private Map<String, CacheableSessionLockManager> sessionLockManagers;
-   
+
    /**
     * Constructor.
     * 
@@ -176,12 +174,11 @@
     * @throws RepositoryConfigurationException
     */
    public CacheableLockManager(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
-            InitialContextInitializer context, TransactionService transactionService)
-            throws RepositoryConfigurationException
+      InitialContextInitializer context, TransactionService transactionService) throws RepositoryConfigurationException
    {
       this(dataManager, config, context, transactionService.getTransactionManager());
    }
-   
+
    /**
     * Constructor.
     * 
@@ -191,12 +188,11 @@
     * @throws RepositoryConfigurationException
     */
    public CacheableLockManager(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
-            InitialContextInitializer context)
-            throws RepositoryConfigurationException
+      InitialContextInitializer context) throws RepositoryConfigurationException
    {
       this(dataManager, config, context, (TransactionManager)null);
    }
-   
+
    /**
     * Constructor.
     * 
@@ -211,7 +207,7 @@
       InitialContextInitializer context, TransactionManager transactionManager) throws RepositoryConfigurationException
    {
       lockRoot = Fqn.fromElements(LOCKS);
-      
+
       List<SimpleParameterEntry> paramenerts = config.getLockManager().getParameters();
 
       this.dataManager = dataManager;
@@ -252,15 +248,15 @@
          CacheFactory<Serializable, Object> factory = new DefaultCacheFactory<Serializable, Object>();
 
          cache = factory.createCache(pathToConfig, false);
-         
+
          if (transactionManager != null)
          {
             cache.getConfiguration().getRuntimeConfig().setTransactionManager(transactionManager);
          }
-         
+
          cache.create();
          cache.start();
-         
+
          createStructuredNode(lockRoot);
 
          // Context recall is a workaround of JDBCCacheLoader starting. 
@@ -338,7 +334,7 @@
     */
    public boolean isLockLive(String nodeId)
    {
-      if (pendingLocks.containsKey(nodeId) ||  cache.getRoot().hasChild(makeLockFqn(nodeId)))
+      if (pendingLocks.containsKey(nodeId) || cache.getRoot().hasChild(makeLockFqn(nodeId)))
       {
          return true;
       }
@@ -382,7 +378,9 @@
             chengesLogList.add(iter.nextLog());
          }
       }
-      
+
+      List<LockOperationContainer> containers = new ArrayList<LockOperationContainer>();
+
       for (PlainChangesLog currChangesLog : chengesLogList)
       {
          String nodeIdentifier;
@@ -394,30 +392,31 @@
                   if (currChangesLog.getSize() < 2)
                   {
                      log.error("Incorrect changes log  of type ExtendedEvent.LOCK size=" + currChangesLog.getSize()
-                              + "<2 \n" + currChangesLog.dump());
+                        + "<2 \n" + currChangesLog.dump());
                      break;
                   }
                   nodeIdentifier = currChangesLog.getAllStates().get(0).getData().getParentIdentifier();
 
                   if (pendingLocks.containsKey(nodeIdentifier))
                   {
-                     internalLock(nodeIdentifier);
+                     containers.add(new LockOperationContainer(nodeIdentifier, currChangesLog.getSessionId(),
+                        ExtendedEvent.LOCK));
                   }
                   else
                   {
-                     throw new LockException("Lock must exist in pending locks.");
+                     log.error("Lock must exist in pending locks.");
                   }
                   break;
                case ExtendedEvent.UNLOCK :
                   if (currChangesLog.getSize() < 2)
                   {
                      log.error("Incorrect changes log  of type ExtendedEvent.UNLOCK size=" + currChangesLog.getSize()
-                              + "<2 \n" + currChangesLog.dump());
+                        + "<2 \n" + currChangesLog.dump());
                      break;
                   }
 
-                  internalUnLock(currChangesLog.getSessionId(), currChangesLog.getAllStates().get(0).getData()
-                           .getParentIdentifier());
+                  containers.add(new LockOperationContainer(currChangesLog.getAllStates().get(0).getData()
+                     .getParentIdentifier(), currChangesLog.getSessionId(), ExtendedEvent.UNLOCK));
                   break;
                default :
                   HashSet<String> removedLock = new HashSet<String>();
@@ -439,23 +438,90 @@
                   }
                   for (String identifier : removedLock)
                   {
-                     internalUnLock(currChangesLog.getSessionId(), identifier);
+                     containers.add(new LockOperationContainer(identifier, currChangesLog.getSessionId(),
+                        ExtendedEvent.UNLOCK));
                   }
                   break;
             }
          }
-         catch (LockException e)
+         catch (IllegalStateException e)
          {
             log.error(e.getLocalizedMessage(), e);
          }
-         catch (IllegalStateException e)
+      }
+      
+      // sort locking and unlocking operations to avoid deadlocks in JBossCache
+      Collections.sort(containers);
+      for (LockOperationContainer container : containers)
+      {
+         try
          {
-            log.error(e.getLocalizedMessage(), e);
+            container.apply();
          }
+         catch (LockException e)
+         {
+            log.error(e.getMessage(), e);
+         }
       }
    }
 
    /**
+    * Class containing operation type (LOCK or UNLOCK) and all the needed information like node uuid and session id 
+    */
+   private class LockOperationContainer implements Comparable<LockOperationContainer>
+   {
+
+      private String identifier;
+
+      private String sessionId;
+
+      private int type;
+
+      /**
+       * @param identifier node identifier 
+       * @param sessionId id of session
+       * @param type ExtendedEvent type specifying the operation (LOCK or UNLOCK)
+       */
+      public LockOperationContainer(String identifier, String sessionId, int type)
+      {
+         super();
+         this.identifier = identifier;
+         this.sessionId = sessionId;
+         this.type = type;
+      }
+
+      /**
+       * @return node identifier
+       */
+      public String getIdentifier()
+      {
+         return identifier;
+      }
+
+      public void apply() throws LockException
+      {
+         // invoke internalLock in LOCK operation
+         if (type == ExtendedEvent.LOCK)
+         {
+            internalLock(identifier);
+         }
+         // invoke internalUnLock in UNLOCK operation
+         else if (type == ExtendedEvent.UNLOCK)
+         {
+            internalUnLock(sessionId, identifier);
+         }
+      }
+
+      /**
+       * @see java.lang.Comparable#compareTo(java.lang.Object)
+       */
+      public int compareTo(LockOperationContainer o)
+      {
+         return identifier.compareTo(o.getIdentifier());
+      }
+   }
+
+   /**
     * Refreshed lock data in cache
     * 
     * @param newLockData
@@ -498,7 +564,7 @@
       }
 
       Collections.sort(removeLockList);
-      
+
       for (String rLock : removeLockList)
       {
          removeLock(rLock);
@@ -699,7 +765,7 @@
 
    protected LockData getLockDataById(String nodeId)
    {
-      return (LockData) cache.get(makeLockFqn(nodeId), LOCK_DATA);
+      return (LockData)cache.get(makeLockFqn(nodeId), LOCK_DATA);
    }
 
    protected synchronized List<LockData> getLockList()
@@ -709,7 +775,7 @@
       List<LockData> locksData = new ArrayList<LockData>();
       for (Object nodeId : nodesId)
       {
-         LockData lockData = (LockData) cache.get(makeLockFqn((String) nodeId), LOCK_DATA);;
+         LockData lockData = (LockData)cache.get(makeLockFqn((String)nodeId), LOCK_DATA);;
          if (lockData != null)
          {
             locksData.add(lockData);
@@ -728,14 +794,14 @@
       try
       {
          NodeData nData = (NodeData)dataManager.getItemData(nodeIdentifier);
-         
+
          //TODO EXOJCR-412, should be refactored in future.
          //Skip removing, because that node was removed in other node of cluster.  
          if (nData == null)
          {
             return;
          }
-         
+
          PlainChangesLog changesLog =
             new PlainChangesLogImpl(new ArrayList<ItemState>(), SystemIdentity.SYSTEM, ExtendedEvent.UNLOCK);
 
@@ -794,7 +860,7 @@
    {
       sessionLockManagers.remove(sessionID);
    }
-   
+
    /**
     * Make lock absolute Fqn, i.e. /$LOCKS/nodeID.
     *
@@ -805,17 +871,18 @@
    {
       return Fqn.fromRelativeElements(lockRoot, nodeId);
    }
-   
+
    /**
     *  Will be created structured node in cache, like /$LOCKS
     */
-   private void createStructuredNode(Fqn fqn) {
-      Node<Serializable, Object> node = cache.getRoot().getChild(fqn); 
-      if (node == null) 
-      { 
-      cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); 
-      node = cache.getRoot().addChild(fqn); 
-      } 
+   private void createStructuredNode(Fqn fqn)
+   {
+      Node<Serializable, Object> node = cache.getRoot().getChild(fqn);
+      if (node == null)
+      {
+         cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+         node = cache.getRoot().addChild(fqn);
+      }
       node.setResident(true);
    }
 }



More information about the exo-jcr-commits mailing list