[exo-jcr-commits] exo-jcr SVN: r1680 - in jcr/trunk/exo.jcr.component.core/src/main: java/org/exoplatform/services/jcr/impl/core and 3 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Feb 4 09:33:14 EST 2010


Author: sergiykarpenko
Date: 2010-02-04 09:33:13 -0500 (Thu, 04 Feb 2010)
New Revision: 1680

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/WorkspaceLockManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManagerImpl.java
Removed:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/AbstractLockManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManager.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManagerImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockRemover.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheLockImpl.java
   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/CacheableSessionLockManager.java
   jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.default.sql
   jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.ora.sql
Log:
EXOJCR-472: CacheableLockManager refactoring

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -41,7 +41,7 @@
 import org.exoplatform.services.jcr.impl.core.WorkspaceInitializer;
 import org.exoplatform.services.jcr.impl.core.access.DefaultAccessManagerImpl;
 import org.exoplatform.services.jcr.impl.core.lock.LockManagerImpl;
-import org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManager;
+import org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl;
 import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeDataManagerImpl;
 import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeManagerImpl;
 import org.exoplatform.services.jcr.impl.core.nodetype.registration.JCRNodeTypeDataPersister;

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/SessionImpl.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -31,7 +31,7 @@
 import org.exoplatform.services.jcr.datamodel.NodeData;
 import org.exoplatform.services.jcr.datamodel.QPathEntry;
 import org.exoplatform.services.jcr.impl.Constants;
-import org.exoplatform.services.jcr.impl.core.lock.AbstractLockManager;
+import org.exoplatform.services.jcr.impl.core.lock.WorkspaceLockManager;
 import org.exoplatform.services.jcr.impl.core.lock.SessionLockManager;
 import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeImpl;
 import org.exoplatform.services.jcr.impl.core.observation.ObservationManagerImpl;
@@ -157,8 +157,7 @@
 
       this.accessManager = (AccessManager)container.getComponentInstanceOfType(AccessManager.class);
       this.lockManager =
-         ((AbstractLockManager)container.getComponentInstanceOfType(AbstractLockManager.class))
-            .getSessionLockManager(id);
+         ((WorkspaceLockManager)container.getComponentInstanceOfType(WorkspaceLockManager.class)).getSessionLockManager(id);
       WorkspaceEntry wsConfig = (WorkspaceEntry)container.getComponentInstanceOfType(WorkspaceEntry.class);
 
       this.lazyReadThreshold =

Deleted: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/AbstractLockManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/AbstractLockManager.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/AbstractLockManager.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2003-2010 eXo Platform SAS.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program 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 org.exoplatform.services.jcr.impl.core.lock;
-
-/**
- * Created by The eXo Platform SAS.
- * 
- * <br/>Date: 2010
- *
- * @author <a href="mailto:alex.reshetnyak at exoplatform.com.ua">Alex Reshetnyak</a> 
- * @version $Id$
- */
-public abstract class AbstractLockManager implements LockManager
-{
-   /**
-    * {@inheritDoc}
-    */
-   public abstract void removeExpired();
-
-   /**
-    * {@inheritDoc}
-    */
-   public abstract SessionLockManager getSessionLockManager(String sessionId);
-}

Deleted: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManager.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManager.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2009 eXo Platform SAS.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.exoplatform.services.jcr.impl.core.lock;
-
-/**
- * @author <a href="mailto:Sergey.Kabashnyuk at gmail.com">Sergey Kabashnyuk</a>
- * @version $Id: LockManager.java 11907 2008-03-13 15:36:21Z ksm $
- */
-public interface LockManager
-{
-   /**
-    * Remove expired locks. Called by LockRemover timer.
-    */
-   void removeExpired();
-
-   /**
-    * Returns session lock manager that interact with this LockManager.
-    * 
-    * @param sessionId - sessionId
-    * @return
-    */
-   SessionLockManager getSessionLockManager(String sessionId);
-}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManagerImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManagerImpl.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockManagerImpl.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -74,7 +74,7 @@
  */
 @Managed
 @NameTemplate(@Property(key = "service", value = "lockmanager"))
-public class LockManagerImpl extends AbstractLockManager implements ItemsPersistenceListener, Startable
+public class LockManagerImpl implements WorkspaceLockManager, ItemsPersistenceListener, Startable
 {
    /**
     * Default lock time out. 30min
@@ -100,7 +100,7 @@
    /**
     * Logger
     */
-   private final Log log = ExoLogger.getLogger("jcr.lock.LockManager");
+   private final Log log = ExoLogger.getLogger("jcr.lock.LockManagerImpl");
 
    /**
     * Map NodeIdentifier -- lockData
@@ -798,4 +798,9 @@
    {
       return new SessionLockManagerImpl(sessionId, this);
    }
+
+   public void closeSessionLockManager(String sessionId)
+   {
+      //do nothing
+   }
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockRemover.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockRemover.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/LockRemover.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -37,14 +37,14 @@
 
    // sec
 
-   private final LockManager lockManagerImpl;
+   private final WorkspaceLockManager lockManagerImpl;
 
-   public LockRemover(LockManager lockManagerImpl)
+   public LockRemover(WorkspaceLockManager lockManagerImpl)
    {
       this(lockManagerImpl, DEFAULT_THREAD_TIMEOUT);
    }
 
-   private LockRemover(LockManager lockManagerImpl, long timeout)
+   private LockRemover(WorkspaceLockManager lockManagerImpl, long timeout)
    {
       super(timeout);
       this.lockManagerImpl = lockManagerImpl;

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/WorkspaceLockManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/WorkspaceLockManager.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/WorkspaceLockManager.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.core.lock;
+
+/**
+ * @author <a href="mailto:Sergey.Kabashnyuk at gmail.com">Sergey Kabashnyuk</a>
+ * @version $Id: LockManager.java 11907 2008-03-13 15:36:21Z ksm $
+ */
+public interface WorkspaceLockManager
+{
+   /**
+    * Remove expired locks. Called by LockRemover timer.
+    */
+   void removeExpired();
+
+   /**
+    * Returns session lock manager that interact with this LockManager.
+    * 
+    * @param sessionId - session ID
+    * @return
+    */
+   SessionLockManager getSessionLockManager(String sessionId);
+
+   /**
+    * Release resources associated with previously opened SessionLockManager.
+    * @param sessionId - session ID
+    */
+   void closeSessionLockManager(String sessionId);
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheLockImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheLockImpl.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheLockImpl.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -113,6 +113,7 @@
       }
       catch (RepositoryException e)
       {
+         //TODO
          e.printStackTrace();
       }
       return null;

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-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManager.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -1,1033 +1,83 @@
-/*
- * Copyright (C) 2003-2010 eXo Platform SAS.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program 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 org.exoplatform.services.jcr.impl.core.lock.jbosscache;
-
-import org.exoplatform.container.configuration.ConfigurationManager;
-import org.exoplatform.management.annotations.Managed;
-import org.exoplatform.management.annotations.ManagedDescription;
-import org.exoplatform.management.jmx.annotations.NameTemplate;
-import org.exoplatform.management.jmx.annotations.Property;
-import org.exoplatform.services.jcr.access.SystemIdentity;
-import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
-import org.exoplatform.services.jcr.config.SimpleParameterEntry;
-import org.exoplatform.services.jcr.config.WorkspaceEntry;
-import org.exoplatform.services.jcr.dataflow.ChangesLogIterator;
-import org.exoplatform.services.jcr.dataflow.CompositeChangesLog;
-import org.exoplatform.services.jcr.dataflow.DataManager;
-import org.exoplatform.services.jcr.dataflow.ItemState;
-import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
-import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
-import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
-import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
-import org.exoplatform.services.jcr.dataflow.persistent.ItemsPersistenceListener;
-import org.exoplatform.services.jcr.datamodel.ItemData;
-import org.exoplatform.services.jcr.datamodel.NodeData;
-import org.exoplatform.services.jcr.datamodel.PropertyData;
-import org.exoplatform.services.jcr.datamodel.QPathEntry;
-import org.exoplatform.services.jcr.impl.Constants;
-import org.exoplatform.services.jcr.impl.core.NodeImpl;
-import org.exoplatform.services.jcr.impl.core.lock.AbstractLockManager;
-import org.exoplatform.services.jcr.impl.core.lock.LockRemover;
-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.WorkspacePersistentDataManager;
-import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
-import org.exoplatform.services.jcr.jbosscache.ExoJBossCacheFactory;
-import org.exoplatform.services.jcr.observation.ExtendedEvent;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-import org.exoplatform.services.naming.InitialContextInitializer;
-import org.exoplatform.services.transaction.TransactionService;
-import org.jboss.cache.Cache;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.Node;
-import org.jboss.cache.loader.CacheLoader;
-import org.picocontainer.Startable;
-
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.lock.LockException;
-import javax.transaction.Transaction;
-import javax.transaction.TransactionManager;
-
-/**
- * Created by The eXo Platform SAS.
- * 
- * <br/>Date: 
- *
- * @author <a href="karpenko.sergiy at gmail.com">Karpenko Sergiy</a> 
- * @version $Id: CacheableLockManager.java 111 2008-11-11 11:11:11Z serg $
- */
- at Managed
- at NameTemplate(@Property(key = "service", value = "lockmanager"))
-public class CacheableLockManager extends AbstractLockManager implements ItemsPersistenceListener, Startable
-{
-   /**
-    *  The name to property time out. 
-    */
-   public static final String TIME_OUT = "time-out";
-
-   /**
-    *  The name to property cache configuration. 
-    */
-   public static final String JBOSSCACCHE_CONFIG = "jbosscache-configuration";
-
-   /**
-    * Default lock time out. 30min
-    */
-   public static final long DEFAULT_LOCK_TIMEOUT = 1000 * 60 * 30;
-
-   // Search constants
-   /**
-    * The exact lock token.
-    */
-   protected static final int SEARCH_EXECMATCH = 1;
-
-   /**
-    * Lock token of closed parent
-    */
-   protected static final int SEARCH_CLOSEDPARENT = 2;
-
-   /**
-    * Lock token of closed child
-    */
-   protected static final int SEARCH_CLOSEDCHILD = 4;
-
-   /**
-    * Name of lock root in jboss-cache.
-    */
-   public static final String LOCKS = "$LOCKS";
-
-   /**
-    * Attribute name where LockData will be stored.
-    */
-   public static final String LOCK_DATA = "$LOCK_DATA";
-
-   /**
-    * Logger
-    */
-   private final Log log = ExoLogger.getLogger("jcr.lock.CacheableLockManager");
-
-   /**
-    * Data manager.
-    */
-   private final DataManager dataManager;
-
-   /**
-    * Map NodeIdentifier -- lockData
-    */
-   private final Map<String, LockData> pendingLocks;
-
-   /**
-    * Context recall is a workaround of JDBCCacheLoader starting. 
-    */
-   //private final InitialContextInitializer context;
-
-   /**
-    * Run time lock time out.
-    */
-   private long lockTimeOut;
-
-   /**
-    * Lock remover thread.
-    */
-   private LockRemover lockRemover;
-
-   /**
-    * The current Transaction Manager
-    */
-   private TransactionManager tm;
-
-   private Cache<Serializable, Object> cache;
-
-   private final Fqn<String> lockRoot;
-
-   private Map<String, CacheableSessionLockManager> sessionLockManagers;
-
-   /**
-    * Constructor.
-    * 
-    * @param dataManager - workspace persistent data manager
-    * @param config - workspace entry
-    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
-    * @param transactionService 
-    *          the transaction service
-    * @throws RepositoryConfigurationException
-    */
-   public CacheableLockManager(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
-      InitialContextInitializer context, TransactionService transactionService, ConfigurationManager cfm)
-      throws RepositoryConfigurationException
-   {
-      this(dataManager, config, context, transactionService.getTransactionManager(), cfm);
-   }
-
-   /**
-    * Constructor.
-    * 
-    * @param dataManager - workspace persistent data manager
-    * @param config - workspace entry
-    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
-    * @throws RepositoryConfigurationException
-    */
-   public CacheableLockManager(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
-      InitialContextInitializer context, ConfigurationManager cfm) throws RepositoryConfigurationException
-   {
-      this(dataManager, config, context, (TransactionManager)null, cfm);
-   }
-
-   /**
-    * Constructor.
-    * 
-    * @param dataManager - workspace persistent data manager
-    * @param config - workspace entry
-    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
-    * @param transactionManager 
-    *          the transaction manager
-    * @throws RepositoryConfigurationException
-    */
-   public CacheableLockManager(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
-      InitialContextInitializer context, TransactionManager transactionManager, ConfigurationManager cfm)
-      throws RepositoryConfigurationException
-   {
-      lockRoot = Fqn.fromElements(LOCKS);
-
-      List<SimpleParameterEntry> paramenerts = config.getLockManager().getParameters();
-
-      this.dataManager = dataManager;
-      if (config.getLockManager() != null)
-      {
-         if (paramenerts != null && config.getLockManager().getParameterValue(TIME_OUT, null) != null)
-         {
-            long timeOut = config.getLockManager().getParameterTime(TIME_OUT);
-            lockTimeOut = timeOut > 0 ? timeOut : DEFAULT_LOCK_TIMEOUT;
-         }
-         else
-         {
-            lockTimeOut =
-               config.getLockManager().getTimeout() > 0 ? config.getLockManager().getTimeout() : DEFAULT_LOCK_TIMEOUT;
-         }
-
-      }
-      else
-      {
-         lockTimeOut = DEFAULT_LOCK_TIMEOUT;
-      }
-
-      pendingLocks = new HashMap<String, LockData>();
-      sessionLockManagers = new HashMap<String, CacheableSessionLockManager>();
-      //this.context = context;
-
-      dataManager.addItemPersistenceListener(this);
-
-      // make cache
-      if (config.getLockManager() != null)
-      {
-         this.tm = transactionManager;
-
-         // create cache using custom factory
-         ExoJBossCacheFactory<Serializable, Object> factory =
-            new ExoJBossCacheFactory<Serializable, Object>(cfm, transactionManager);
-
-         cache = factory.createCache(config.getLockManager());
-
-         cache.create();
-         cache.start();
-
-         createStructuredNode(lockRoot);
-
-         // Context recall is a workaround of JDBCCacheLoader starting. 
-         context.recall();
-      }
-      else
-      {
-         throw new RepositoryConfigurationException("Cache configuration not found");
-      }
-   }
-
-   /*
-    * (non-Javadoc)
-    * @see
-    * org.exoplatform.services.jcr.impl.core.lock.LockManager#addPendingLock(org.exoplatform.services
-    * .jcr.impl.core.NodeImpl, boolean, boolean, long)
-    */
-   public synchronized void addPendingLock(String nodeIdentifier, LockData lData)
-   {
-      pendingLocks.put(nodeIdentifier, lData);
-   }
-
-   @Managed
-   @ManagedDescription("Remove the expired locks")
-   public void cleanExpiredLocks()
-   {
-      removeExpired();
-   }
-
-   public long getDefaultLockTimeOut()
-   {
-      return lockTimeOut;
-   }
-
-   /*
-    * (non-Javadoc)
-    * @see
-    * org.exoplatform.services.jcr.impl.core.lock.LockManager#getLock(org.exoplatform.services.jcr
-    * .impl.core.NodeImpl)
-    */
-   public LockData getLockData(NodeImpl node) throws LockException, RepositoryException
-   {
-
-      LockData lData = getLockData((NodeData)node.getData(), SEARCH_EXECMATCH | SEARCH_CLOSEDPARENT);
-
-      if (lData == null || (!node.getInternalIdentifier().equals(lData.getNodeIdentifier()) && !lData.isDeep()))
-      {
-         throw new LockException("Node not locked: " + node.getData().getQPath());
-      }
-      return lData;
-   }
-
-   private final LockActionNonTxAware<Integer, Object> getNumLocks = new LockActionNonTxAware<Integer, Object>()
-   {
-      public Integer execute(Object arg)
-      {
-         return cache.getChildrenNames(lockRoot).size();
-      }
-   };
-
-   @Managed
-   @ManagedDescription("The number of active locks")
-   public int getNumLocks()
-   {
-      try
-      {
-         return executeLockActionNonTxAware(getNumLocks, null);
-      }
-      catch (LockException e)
-      {
-         // ignore me will never occur
-      }
-      return -1;
-   }
-
-   /**
-    * Return new instance of session lock manager.
-    */
-   public SessionLockManager getSessionLockManager(String sessionId)
-   {
-      CacheableSessionLockManager sessionManager = new CacheableSessionLockManager(sessionId, this);
-      sessionLockManagers.put(sessionId, sessionManager);
-      return sessionManager;
-   }
-
-   private final LockActionNonTxAware<Boolean, String> isLockLive = new LockActionNonTxAware<Boolean, String>()
-   {
-      public Boolean execute(String nodeId)
-      {
-         if (pendingLocks.containsKey(nodeId) || cache.getRoot().hasChild(makeLockFqn(nodeId)))
-         {
-            return true;
-         }
-
-         return false;
-      }
-   };
-
-   /**
-    * Check is LockManager contains lock. No matter it is in pending or persistent state.
-    * 
-    * @param nodeId - locked node id
-    * @return 
-    */
-   public boolean isLockLive(String nodeId)
-   {
-      try
-      {
-         return executeLockActionNonTxAware(isLockLive, nodeId);
-      }
-      catch (LockException e)
-      {
-         // ignore me will never occur
-      }
-      return false;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public boolean isTXAware()
-   {
-      return true;
-   }
-
-   /*
-    * (non-Javadoc)
-    * @seeorg.exoplatform.services.jcr.dataflow.persistent.ItemsPersistenceListener#onSaveItems(org.
-    * exoplatform.services.jcr.dataflow.ItemStateChangesLog)
-    */
-   public void onSaveItems(ItemStateChangesLog changesLog)
-   {
-      List<PlainChangesLog> chengesLogList = new ArrayList<PlainChangesLog>();
-      if (changesLog instanceof TransactionChangesLog)
-      {
-         ChangesLogIterator logIterator = ((TransactionChangesLog)changesLog).getLogIterator();
-
-         while (logIterator.hasNextLog())
-         {
-            chengesLogList.add(logIterator.nextLog());
-         }
-      }
-      else if (changesLog instanceof PlainChangesLog)
-      {
-         chengesLogList.add((PlainChangesLog)changesLog);
-      }
-      else if (changesLog instanceof CompositeChangesLog)
-      {
-         for (ChangesLogIterator iter = ((CompositeChangesLog)changesLog).getLogIterator(); iter.hasNextLog();)
-         {
-            chengesLogList.add(iter.nextLog());
-         }
-      }
-
-      List<LockOperationContainer> containers = new ArrayList<LockOperationContainer>();
-
-      for (PlainChangesLog currChangesLog : chengesLogList)
-      {
-         String nodeIdentifier;
-         try
-         {
-            switch (currChangesLog.getEventType())
-            {
-               case ExtendedEvent.LOCK :
-                  if (currChangesLog.getSize() < 2)
-                  {
-                     log.error("Incorrect changes log  of type ExtendedEvent.LOCK size=" + currChangesLog.getSize()
-                        + "<2 \n" + currChangesLog.dump());
-                     break;
-                  }
-                  nodeIdentifier = currChangesLog.getAllStates().get(0).getData().getParentIdentifier();
-
-                  if (pendingLocks.containsKey(nodeIdentifier))
-                  {
-                     containers.add(new LockOperationContainer(nodeIdentifier, currChangesLog.getSessionId(),
-                        ExtendedEvent.LOCK));
-                  }
-                  else
-                  {
-                     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());
-                     break;
-                  }
-
-                  containers.add(new LockOperationContainer(currChangesLog.getAllStates().get(0).getData()
-                     .getParentIdentifier(), currChangesLog.getSessionId(), ExtendedEvent.UNLOCK));
-                  break;
-               default :
-                  HashSet<String> removedLock = new HashSet<String>();
-                  for (ItemState itemState : currChangesLog.getAllStates())
-                  {
-                     // this is a node and node is locked
-                     if (itemState.getData().isNode() && lockExist(itemState.getData().getIdentifier()))
-                     {
-                        nodeIdentifier = itemState.getData().getIdentifier();
-                        if (itemState.isDeleted())
-                        {
-                           removedLock.add(nodeIdentifier);
-                        }
-                        else if (itemState.isAdded() || itemState.isRenamed() || itemState.isUpdated())
-                        {
-                           removedLock.remove(nodeIdentifier);
-                        }
-                     }
-                  }
-                  for (String identifier : removedLock)
-                  {
-                     containers.add(new LockOperationContainer(identifier, currChangesLog.getSessionId(),
-                        ExtendedEvent.UNLOCK));
-                  }
-                  break;
-            }
-         }
-         catch (IllegalStateException e)
-         {
-            log.error(e.getLocalizedMessage(), e);
-         }
-      }
-
-      // sort locking and unlocking operations to avoid deadlocks in JBossCache
-      Collections.sort(containers);
-      for (LockOperationContainer container : containers)
-      {
-         try
-         {
-            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());
-      }
-   }
-
-   private final LockActionNonTxAware<Object, LockData> refresh = new LockActionNonTxAware<Object, LockData>()
-   {
-      public Object execute(LockData newLockData) throws LockException
-      {
-         //first look pending locks
-         if (pendingLocks.containsKey(newLockData.getNodeIdentifier()))
-         {
-            pendingLocks.put(newLockData.getNodeIdentifier(), newLockData);
-         }
-         else
-         {
-            Fqn<String> fqn = makeLockFqn(newLockData.getNodeIdentifier());
-            Object oldValue = cache.put(fqn, LOCK_DATA, newLockData);
-            if (oldValue == null)
-            {
-               throw new LockException("Can't refresh lock for node " + newLockData.getNodeIdentifier()
-                  + " since lock is not exist");
-            }
-         }
-         return null;
-      }
-   };
-
-   /**
-    * Refreshed lock data in cache
-    * 
-    * @param newLockData
-    */
-   public void refresh(LockData newLockData) throws LockException
-   {
-      executeLockActionNonTxAware(refresh, newLockData);
-   }
-
-   /**
-    * Remove expired locks. Used from LockRemover.
-    */
-   public synchronized void removeExpired()
-   {
-      final List<String> removeLockList = new ArrayList<String>();
-
-      for (LockData lock : getLockList())
-      {
-         if (!lock.isSessionScoped() && lock.getTimeToDeath() < 0)
-         {
-            removeLockList.add(lock.getNodeIdentifier());
-         }
-      }
-
-      Collections.sort(removeLockList);
-
-      for (String rLock : removeLockList)
-      {
-         removeLock(rLock);
-      }
-   }
-
-   public void removePendingLock(String nodeId)
-   {
-      pendingLocks.remove(nodeId);
-   }
-
-   /*
-    * (non-Javadoc)
-    * @see org.picocontainer.Startable#start()
-    */
-   public void start()
-   {
-      lockRemover = new LockRemover(this);
-   }
-
-   /*
-    * (non-Javadoc)
-    * @see org.picocontainer.Startable#stop()
-    */
-   public void stop()
-   {
-      lockRemover.halt();
-      lockRemover.interrupt();
-      pendingLocks.clear();
-      sessionLockManagers.clear();
-      cache.stop();
-   }
-
-   /**
-    * Copy <code>PropertyData prop<code> to new TransientItemData
-    * 
-    * @param prop
-    * @return
-    * @throws RepositoryException
-    */
-   private TransientItemData copyItemData(PropertyData prop) throws RepositoryException
-   {
-      if (prop == null)
-      {
-         return null;
-      }
-
-      // make a copy, value may be null for deleting items
-      TransientPropertyData newData =
-         new TransientPropertyData(prop.getQPath(), prop.getIdentifier(), prop.getPersistedVersion(), prop.getType(),
-            prop.getParentIdentifier(), prop.isMultiValued(), prop.getValues());
-
-      return newData;
-   }
-
-   /**
-    * Internal lock
-    * 
-    * @param nodeIdentifier
-    * @throws LockException
-    */
-   private synchronized void internalLock(String nodeIdentifier) throws LockException
-   {
-      LockData lockData = pendingLocks.get(nodeIdentifier);
-      if (lockData != null)
-      {
-         Fqn<String> lockPath = makeLockFqn(lockData.getNodeIdentifier());
-
-         // addChild will add if absent or return old if present
-         Node<Serializable, Object> node = cache.getRoot().addChild(lockPath);
-
-         // this will return null if success. And old data if something exists...
-         LockData oldLockData = (LockData)node.putIfAbsent(LOCK_DATA, lockData);
-
-         if (oldLockData != null)
-         {
-            throw new LockException("Unable to write LockData. Node [" + lockData.getNodeIdentifier()
-               + "] already has LockData!");
-         }
-         pendingLocks.remove(nodeIdentifier);
-      }
-      else
-      {
-         throw new LockException("No lock in pending locks");
-      }
-   }
-
-   /**
-    * Internal unlock.
-    * 
-    * @param sessionId
-    * @param nodeIdentifier
-    * @throws LockException
-    */
-   private synchronized void internalUnLock(String sessionId, String nodeIdentifier) throws LockException
-   {
-      LockData lData = getLockDataById(nodeIdentifier);
-
-      if (lData != null)
-      {
-         cache.removeNode(makeLockFqn(nodeIdentifier));
-
-         CacheableSessionLockManager sessMgr = sessionLockManagers.get(sessionId);
-         if (sessMgr != null)
-         {
-            sessMgr.notifyLockRemoved(nodeIdentifier);
-         }
-      }
-   }
-
-   private final LockActionNonTxAware<Boolean, String> lockExist = new LockActionNonTxAware<Boolean, String>()
-   {
-      public Boolean execute(String nodeId) throws LockException
-      {
-         return cache.getRoot().hasChild(makeLockFqn(nodeId));
-      }
-   };
-
-   private boolean lockExist(String nodeId)
-   {
-      try
-      {
-         return executeLockActionNonTxAware(lockExist, nodeId);
-      }
-      catch (LockException e)
-      {
-         // ignore me will never occur
-      }
-      return false;
-   }
-
-   /**
-    * Calculates md5 hash of string.
-    * 
-    * @param token
-    * @return
-    */
-   protected String getHash(String token)
-   {
-      String hash = "";
-      try
-      {
-         MessageDigest m = MessageDigest.getInstance("MD5");
-         m.update(token.getBytes(), 0, token.length());
-         hash = new BigInteger(1, m.digest()).toString(16);
-      }
-      catch (NoSuchAlgorithmException e)
-      {
-         log.error("Can't get instanse of MD5 MessageDigest!", e);
-      }
-      return hash;
-   }
-
-   /**
-    * Search lock in maps.
-    * 
-    * @param data
-    * @param searchType
-    * @return
-    */
-   protected LockData getLockData(NodeData data, int searchType)
-   {
-      if (data == null)
-         return null;
-      LockData retval = null;
-      try
-      {
-         if ((searchType & SEARCH_EXECMATCH) != 0)
-         {
-            retval = getLockDataById(data.getIdentifier());
-         }
-         if (retval == null && (searchType & SEARCH_CLOSEDPARENT) != 0)
-         {
-
-            NodeData parentData = (NodeData)dataManager.getItemData(data.getParentIdentifier());
-            if (parentData != null)
-            {
-               retval = getLockDataById(parentData.getIdentifier());
-               // parent not found try to fo upper
-               if (retval == null)
-               {
-                  retval = getLockData(parentData, SEARCH_CLOSEDPARENT);
-               }
-            }
-         }
-         if (retval == null && (searchType & SEARCH_CLOSEDCHILD) != 0)
-         {
-
-            List<NodeData> childData = dataManager.getChildNodesData(data);
-            for (NodeData nodeData : childData)
-            {
-               retval = getLockDataById(nodeData.getIdentifier());
-               if (retval != null)
-                  break;
-            }
-            if (retval == null)
-            {
-               // child not found try to find diper
-               for (NodeData nodeData : childData)
-               {
-                  retval = getLockData(nodeData, SEARCH_CLOSEDCHILD);
-                  if (retval != null)
-                     break;
-               }
-            }
-         }
-      }
-      catch (RepositoryException e)
-      {
-         return null;
-      }
-
-      return retval;
-   }
-
-   private final LockActionNonTxAware<LockData, String> getLockDataById = new LockActionNonTxAware<LockData, String>()
-   {
-      public LockData execute(String nodeId) throws LockException
-      {
-         return (LockData)cache.get(makeLockFqn(nodeId), LOCK_DATA);
-      }
-   };
-
-   protected LockData getLockDataById(String nodeId)
-   {
-      try
-      {
-         return executeLockActionNonTxAware(getLockDataById, nodeId);
-      }
-      catch (LockException e)
-      {
-         // ignore me will never occur
-      }
-      return null;
-   }
-
-   private final LockActionNonTxAware<List<LockData>, Object> getLockList =
-      new LockActionNonTxAware<List<LockData>, Object>()
-      {
-         public List<LockData> execute(Object arg) throws LockException
-         {
-            Set<Object> nodesId = cache.getChildrenNames(lockRoot);
-
-            List<LockData> locksData = new ArrayList<LockData>();
-            for (Object nodeId : nodesId)
-            {
-               LockData lockData = (LockData)cache.get(makeLockFqn((String)nodeId), LOCK_DATA);
-               if (lockData != null)
-               {
-                  locksData.add(lockData);
-               }
-            }
-            return locksData;
-         }
-      };
-
-   protected synchronized List<LockData> getLockList()
-   {
-      try
-      {
-         return executeLockActionNonTxAware(getLockList, null);
-      }
-      catch (LockException e)
-      {
-         // ignore me will never occur
-      }
-      return null;
-   }
-
-   /**
-    * Remove lock, used by Lock remover.
-    * 
-    * @param nodeIdentifier String
-    */
-   protected void removeLock(String nodeIdentifier)
-   {
-      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);
-
-         ItemData lockOwner =
-            copyItemData((PropertyData)dataManager.getItemData(nData, new QPathEntry(Constants.JCR_LOCKOWNER, 1)));
-
-         //TODO EXOJCR-412, should be refactored in future.
-         //Skip removing, because that lock was removed in other node of cluster.  
-         if (lockOwner == null)
-         {
-            return;
-         }
-
-         changesLog.add(ItemState.createDeletedState(lockOwner));
-
-         ItemData lockIsDeep =
-            copyItemData((PropertyData)dataManager.getItemData(nData, new QPathEntry(Constants.JCR_LOCKISDEEP, 1)));
-
-         //TODO EXOJCR-412, should be refactored in future.
-         //Skip removing, because that lock was removed in other node of cluster.  
-         if (lockIsDeep == null)
-         {
-            return;
-         }
-
-         changesLog.add(ItemState.createDeletedState(lockIsDeep));
-
-         // lock probably removed by other thread
-         if (lockOwner == null && lockIsDeep == null)
-         {
-            return;
-         }
-
-         dataManager.save(new TransactionChangesLog(changesLog));
-      }
-      catch (JCRInvalidItemStateException e)
-      {
-         //TODO EXOJCR-412, should be refactored in future.
-         //Skip property not found in DB, because that lock property was removed in other node of cluster.
-         if (log.isDebugEnabled())
-         {
-            log.debug("The propperty was removed in other node of cluster.", e);
-         }
-
-      }
-      catch (RepositoryException e)
-      {
-         log.error("Error occur during removing lock" + e.getLocalizedMessage(), e);
-      }
-   }
-
-   /**
-    * Release all resources associated with CacheableSessionLockManager.
-    * 
-    * @param sessionID - session identifier
-    */
-   protected void closeSession(String sessionID)
-   {
-      sessionLockManagers.remove(sessionID);
-   }
-
-   /**
-    * Make lock absolute Fqn, i.e. /$LOCKS/nodeID.
-    *
-    * @param itemId String
-    * @return Fqn
-    */
-   private Fqn<String> makeLockFqn(String nodeId)
-   {
-      return Fqn.fromRelativeElements(lockRoot, nodeId);
-   }
-
-   /**
-    *  Will be created structured node in cache, like /$LOCKS
-    */
-   private void createStructuredNode(Fqn<String> 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);
-   }
-
-   /**
-    * Execute the given action outside a transaction. This is needed since the {@link Cache} used by {@link CacheableLockManager}
-    * manages the persistence of its locks thanks to a {@link CacheLoader} and a {@link CacheLoader} lock the JBoss cache {@link Node}
-    * even for read operations which cause deadlock issue when a XA {@link Transaction} is already opened
-    * @throws LockException when a exception occurs
-    */
-   private <R, A> R executeLockActionNonTxAware(LockActionNonTxAware<R, A> action, A arg) throws LockException
-   {
-      Transaction tx = null;
-      try
-      {
-         if (tm != null)
-         {
-            try
-            {
-               tx = tm.suspend();
-            }
-            catch (Exception e)
-            {
-               log.warn("Cannot suspend the current transaction", e);
-            }
-         }
-         return action.execute(arg);
-      }
-      finally
-      {
-         if (tx != null)
-         {
-            try
-            {
-               tm.resume(tx);
-            }
-            catch (Exception e)
-            {
-               log.warn("Cannot resume the current transaction", e);
-            }
-         }
-      }
-   }
-
-   /**
-    * Actions that are not supposed to be called within a transaction
-    * 
-    * Created by The eXo Platform SAS
-    * Author : Nicolas Filotto 
-    *          nicolas.filotto at exoplatform.com
-    * 21 janv. 2010
-    */
-   private static interface LockActionNonTxAware<R, A>
-   {
-      R execute(A arg) throws LockException;
-   }
-}
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 org.exoplatform.services.jcr.impl.core.lock.jbosscache;
+
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.impl.core.lock.WorkspaceLockManager;
+
+import javax.jcr.lock.LockException;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * <br/>Date: 
+ *
+ * @author <a href="karpenko.sergiy at gmail.com">Karpenko Sergiy</a> 
+ * @version $Id: CacheableLockManager.java 111 2008-11-11 11:11:11Z serg $
+ */
+public interface CacheableLockManager extends WorkspaceLockManager
+{
+
+   /**
+    * Returns new LockData object or throw LockException if can't place lock here.
+    * 
+    * @param node
+    * @param lockToken
+    * @param isDeep
+    * @param isSessionScoped
+    * @param timeOut
+    * @return LockData object 
+    * @throws LockException - if node can not be locked
+    */
+   LockData createLockData(NodeData node, String lockToken, boolean isDeep, boolean isSessionScoped, String owner,
+      long timeOut) throws LockException;
+
+   /**
+    * Is lock assigned exactly for node exist.
+    * 
+    * @param node
+    * @return 
+    */
+   boolean exactLockExist(NodeData node);
+
+   /**
+    * Returns lock data that is assigned to this node or its parent.
+    * 
+    * @param node
+    * @return
+    */
+   LockData getExactOrCloseParentLock(NodeData node);
+
+   String getHash(String lockToken);
+
+   /**
+    * Is lock live
+    * 
+    * @param nodeIdentifier
+    * @return
+    */
+   boolean isLockLive(String nodeIdentifier);
+
+   /**
+    * Replace old lockData with new one. Node ID, token can't be replaced.
+    * 
+    * @param newLockData
+    * @throws LockException
+    */
+   void refreshLockData(LockData newLockData) throws LockException;
+
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManagerImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManagerImpl.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableLockManagerImpl.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 org.exoplatform.services.jcr.impl.core.lock.jbosscache;
+
+import org.exoplatform.container.configuration.ConfigurationManager;
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.management.jmx.annotations.NameTemplate;
+import org.exoplatform.management.jmx.annotations.Property;
+import org.exoplatform.services.jcr.access.SystemIdentity;
+import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
+import org.exoplatform.services.jcr.config.SimpleParameterEntry;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.dataflow.ChangesLogIterator;
+import org.exoplatform.services.jcr.dataflow.CompositeChangesLog;
+import org.exoplatform.services.jcr.dataflow.DataManager;
+import org.exoplatform.services.jcr.dataflow.ItemState;
+import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
+import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
+import org.exoplatform.services.jcr.dataflow.persistent.ItemsPersistenceListener;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.jcr.impl.core.lock.LockRemover;
+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.WorkspacePersistentDataManager;
+import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
+import org.exoplatform.services.jcr.jbosscache.ExoJBossCacheFactory;
+import org.exoplatform.services.jcr.observation.ExtendedEvent;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.naming.InitialContextInitializer;
+import org.exoplatform.services.transaction.TransactionService;
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.loader.CacheLoader;
+import org.picocontainer.Startable;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * <br/>Date: 
+ *
+ * @author <a href="karpenko.sergiy at gmail.com">Karpenko Sergiy</a> 
+ * @version $Id: CacheableLockManager.java 111 2008-11-11 11:11:11Z serg $
+ */
+ at Managed
+ at NameTemplate(@Property(key = "service", value = "lockmanager"))
+public class CacheableLockManagerImpl implements CacheableLockManager, ItemsPersistenceListener, Startable
+{
+   /**
+    *  The name to property time out.  
+    */
+   public static final String TIME_OUT = "time-out";
+
+   /**
+    *  The name to property cache configuration. 
+    */
+   public static final String JBOSSCACCHE_CONFIG = "jbosscache-configuration";
+
+   /**
+    * Default lock time out. 30min
+    */
+   public static final long DEFAULT_LOCK_TIMEOUT = 1000 * 60 * 30;
+
+   // Search constants
+   /**
+    * The exact lock token.
+    */
+   protected static final int SEARCH_EXECMATCH = 1;
+
+   /**
+    * Lock token of closed parent
+    */
+   protected static final int SEARCH_CLOSEDPARENT = 2;
+
+   /**
+    * Lock token of closed child
+    */
+   protected static final int SEARCH_CLOSEDCHILD = 4;
+
+   /**
+    * Name of lock root in jboss-cache.
+    */
+   public static final String LOCKS = "$LOCKS";
+
+   /**
+    * Attribute name where LockData will be stored.
+    */
+   public static final String LOCK_DATA = "$LOCK_DATA";
+
+   /**
+    * Logger
+    */
+   private final Log log = ExoLogger.getLogger("jcr.lock.CacheableLockManager");
+
+   /**
+    * Data manager.
+    */
+   private final DataManager dataManager;
+
+   /**
+    * Context recall is a workaround of JDBCCacheLoader starting. 
+    */
+   //private final InitialContextInitializer context;
+
+   /**
+    * Run time lock time out.
+    */
+   private long lockTimeOut;
+
+   /**
+    * Lock remover thread.
+    */
+   private LockRemover lockRemover;
+
+   /**
+    * The current Transaction Manager
+    */
+   private TransactionManager tm;
+
+   private Cache<Serializable, Object> cache;
+
+   private final Fqn<String> lockRoot;
+
+   private Map<String, CacheableSessionLockManager> sessionLockManagers;
+
+   /**
+    * Constructor.
+    * 
+    * @param dataManager - workspace persistent data manager
+    * @param config - workspace entry
+    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
+    * @param transactionService 
+    *          the transaction service
+    * @throws RepositoryConfigurationException
+    */
+   public CacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
+      InitialContextInitializer context, TransactionService transactionService, ConfigurationManager cfm)
+      throws RepositoryConfigurationException
+   {
+      this(dataManager, config, context, transactionService.getTransactionManager(), cfm);
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param dataManager - workspace persistent data manager
+    * @param config - workspace entry
+    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
+    * @throws RepositoryConfigurationException
+    */
+   public CacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
+      InitialContextInitializer context, ConfigurationManager cfm) throws RepositoryConfigurationException
+   {
+      this(dataManager, config, context, (TransactionManager)null, cfm);
+
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param dataManager - workspace persistent data manager
+    * @param config - workspace entry
+    * @param context InitialContextInitializer, needed to reload context after JBoss cache creation
+    * @param transactionManager 
+    *          the transaction manager
+    * @throws RepositoryConfigurationException
+    */
+   public CacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config,
+      InitialContextInitializer context, TransactionManager transactionManager, ConfigurationManager cfm)
+      throws RepositoryConfigurationException
+   {
+      lockRoot = Fqn.fromElements(LOCKS);
+
+      List<SimpleParameterEntry> paramenerts = config.getLockManager().getParameters();
+
+      this.dataManager = dataManager;
+      if (config.getLockManager() != null)
+      {
+         if (paramenerts != null && config.getLockManager().getParameterValue(TIME_OUT, null) != null)
+         {
+            long timeOut = config.getLockManager().getParameterTime(TIME_OUT);
+            lockTimeOut = timeOut > 0 ? timeOut : DEFAULT_LOCK_TIMEOUT;
+         }
+         else
+         {
+            lockTimeOut =
+               config.getLockManager().getTimeout() > 0 ? config.getLockManager().getTimeout() : DEFAULT_LOCK_TIMEOUT;
+         }
+
+      }
+      else
+      {
+         lockTimeOut = DEFAULT_LOCK_TIMEOUT;
+      }
+
+      //pendingLocks = new HashMap<String, LockData>();
+      sessionLockManagers = new HashMap<String, CacheableSessionLockManager>();
+      //this.context = context;
+
+      dataManager.addItemPersistenceListener(this);
+
+      // make cache
+      if (config.getLockManager() != null)
+      {
+         this.tm = transactionManager;
+
+         // create cache using custom factory
+         ExoJBossCacheFactory<Serializable, Object> factory =
+            new ExoJBossCacheFactory<Serializable, Object>(cfm, transactionManager);
+
+         cache = factory.createCache(config.getLockManager());
+
+         cache.create();
+         cache.start();
+
+         createStructuredNode(lockRoot);
+
+         // Context recall is a workaround of JDBCCacheLoader starting. 
+         context.recall();
+      }
+      else
+      {
+         throw new RepositoryConfigurationException("Cache configuration not found");
+      }
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see
+    * org.exoplatform.services.jcr.impl.core.lock.LockManager#addPendingLock(org.exoplatform.services
+    * .jcr.impl.core.NodeImpl, boolean, boolean, long)
+    */
+   //   public synchronized void addPendingLock(String nodeIdentifier, LockData lData)
+   //   {
+   //      pendingLocks.put(nodeIdentifier, lData);
+   //   }
+
+   @Managed
+   @ManagedDescription("Remove the expired locks")
+   public void cleanExpiredLocks()
+   {
+      removeExpired();
+   }
+
+   public long getDefaultLockTimeOut()
+   {
+      return lockTimeOut;
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see
+    * org.exoplatform.services.jcr.impl.core.lock.LockManager#getLock(org.exoplatform.services.jcr
+    * .impl.core.NodeImpl)
+    */
+   //   public LockData getLockData(NodeImpl node) throws LockException, RepositoryException
+   //   {
+   //
+   //      LockData lData = getLockData((NodeData)node.getData(), SEARCH_EXECMATCH | SEARCH_CLOSEDPARENT);
+   //
+   //      if (lData == null || (!node.getInternalIdentifier().equals(lData.getNodeIdentifier()) && !lData.isDeep()))
+   //      {
+   //         throw new LockException("Node not locked: " + node.getData().getQPath());
+   //      }
+   //      return lData;
+   //   }
+
+   private final LockActionNonTxAware<Integer, Object> getNumLocks = new LockActionNonTxAware<Integer, Object>()
+   {
+      public Integer execute(Object arg)
+      {
+         return cache.getChildrenNames(lockRoot).size();
+      }
+   };
+
+   @Managed
+   @ManagedDescription("The number of active locks")
+   public int getNumLocks()
+   {
+      try
+      {
+         return executeLockActionNonTxAware(getNumLocks, null);
+      }
+      catch (LockException e)
+      {
+         // ignore me will never occur
+      }
+      return -1;
+   }
+
+   /**
+    * Return new instance of session lock manager.
+    */
+   public SessionLockManager getSessionLockManager(String sessionId)
+   {
+      CacheableSessionLockManager sessionManager = new CacheableSessionLockManager(sessionId, this);
+      sessionLockManagers.put(sessionId, sessionManager);
+      return sessionManager;
+   }
+
+   private final LockActionNonTxAware<Boolean, String> isLockLive = new LockActionNonTxAware<Boolean, String>()
+   {
+      public Boolean execute(String nodeId)
+      {
+         if (/*pendingLocks.containsKey(nodeId) || */cache.getRoot().hasChild(makeLockFqn(nodeId)))
+         {
+            return true;
+         }
+
+         return false;
+      }
+   };
+
+   /**
+    * Check is LockManager contains lock. No matter it is in pending or persistent state.
+    * 
+    * @param nodeId - locked node id
+    * @return 
+    */
+   public boolean isLockLive(String nodeId)
+   {
+      try
+      {
+         return executeLockActionNonTxAware(isLockLive, nodeId);
+      }
+      catch (LockException e)
+      {
+         // ignore me will never occur
+      }
+      return false;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean isTXAware()
+   {
+      return true;
+   }
+
+   /*
+    * (non-Javadoc)
+    * @seeorg.exoplatform.services.jcr.dataflow.persistent.ItemsPersistenceListener#onSaveItems(org.
+    * exoplatform.services.jcr.dataflow.ItemStateChangesLog)
+    */
+   public void onSaveItems(ItemStateChangesLog changesLog)
+   {
+      List<PlainChangesLog> chengesLogList = new ArrayList<PlainChangesLog>();
+      if (changesLog instanceof TransactionChangesLog)
+      {
+         ChangesLogIterator logIterator = ((TransactionChangesLog)changesLog).getLogIterator();
+
+         while (logIterator.hasNextLog())
+         {
+            chengesLogList.add(logIterator.nextLog());
+         }
+      }
+      else if (changesLog instanceof PlainChangesLog)
+      {
+         chengesLogList.add((PlainChangesLog)changesLog);
+      }
+      else if (changesLog instanceof CompositeChangesLog)
+      {
+         for (ChangesLogIterator iter = ((CompositeChangesLog)changesLog).getLogIterator(); iter.hasNextLog();)
+         {
+            chengesLogList.add(iter.nextLog());
+         }
+      }
+
+      List<LockOperationContainer> containers = new ArrayList<LockOperationContainer>();
+
+      for (PlainChangesLog currChangesLog : chengesLogList)
+      {
+         String sessionId = currChangesLog.getSessionId();
+
+         String nodeIdentifier;
+         try
+         {
+            switch (currChangesLog.getEventType())
+            {
+               case ExtendedEvent.LOCK :
+                  if (currChangesLog.getSize() < 2)
+                  {
+                     log.error("Incorrect changes log  of type ExtendedEvent.LOCK size=" + currChangesLog.getSize()
+                        + "<2 \n" + currChangesLog.dump());
+                     break;
+                  }
+                  nodeIdentifier = currChangesLog.getAllStates().get(0).getData().getParentIdentifier();
+
+                  CacheableSessionLockManager session = sessionLockManagers.get(sessionId);
+                  if (session != null && session.cotainsPendingLock(nodeIdentifier))
+                  {
+                     containers.add(new LockOperationContainer(nodeIdentifier, currChangesLog.getSessionId(),
+                        ExtendedEvent.LOCK));
+                  }
+                  else
+                  {
+                     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());
+                     break;
+                  }
+
+                  containers.add(new LockOperationContainer(currChangesLog.getAllStates().get(0).getData()
+                     .getParentIdentifier(), currChangesLog.getSessionId(), ExtendedEvent.UNLOCK));
+                  break;
+               default :
+                  HashSet<String> removedLock = new HashSet<String>();
+                  for (ItemState itemState : currChangesLog.getAllStates())
+                  {
+                     // this is a node and node is locked
+                     if (itemState.getData().isNode() && lockExist(itemState.getData().getIdentifier()))
+                     {
+                        nodeIdentifier = itemState.getData().getIdentifier();
+                        if (itemState.isDeleted())
+                        {
+                           removedLock.add(nodeIdentifier);
+                        }
+                        else if (itemState.isAdded() || itemState.isRenamed() || itemState.isUpdated())
+                        {
+                           removedLock.remove(nodeIdentifier);
+                        }
+                     }
+                  }
+                  for (String identifier : removedLock)
+                  {
+                     containers.add(new LockOperationContainer(identifier, currChangesLog.getSessionId(),
+                        ExtendedEvent.UNLOCK));
+                  }
+                  break;
+            }
+         }
+         catch (IllegalStateException e)
+         {
+            log.error(e.getLocalizedMessage(), e);
+         }
+      }
+
+      // sort locking and unlocking operations to avoid deadlocks in JBossCache
+      Collections.sort(containers);
+      for (LockOperationContainer container : containers)
+      {
+         try
+         {
+            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(sessionId, 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());
+      }
+   }
+
+   private final LockActionNonTxAware<Object, LockData> refresh = new LockActionNonTxAware<Object, LockData>()
+   {
+      public Object execute(LockData newLockData) throws LockException
+      {
+         Fqn<String> fqn = makeLockFqn(newLockData.getNodeIdentifier());
+         Object oldValue = cache.put(fqn, LOCK_DATA, newLockData);
+         if (oldValue == null)
+         {
+            throw new LockException("Can't refresh lock for node " + newLockData.getNodeIdentifier()
+               + " since lock is not exist");
+         }
+         return null;
+      }
+   };
+
+   /**
+    * Refreshed lock data in cache
+    * 
+    * @param newLockData
+    */
+   public void refreshLockData(LockData newLockData) throws LockException
+   {
+      executeLockActionNonTxAware(refresh, newLockData);
+   }
+
+   /**
+    * Remove expired locks. Used from LockRemover.
+    */
+   public synchronized void removeExpired()
+   {
+      final List<String> removeLockList = new ArrayList<String>();
+
+      for (LockData lock : getLockList())
+      {
+         if (!lock.isSessionScoped() && lock.getTimeToDeath() < 0)
+         {
+            removeLockList.add(lock.getNodeIdentifier());
+         }
+      }
+
+      Collections.sort(removeLockList);
+
+      for (String rLock : removeLockList)
+      {
+         removeLock(rLock);
+      }
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picocontainer.Startable#start()
+    */
+   public void start()
+   {
+      lockRemover = new LockRemover(this);
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picocontainer.Startable#stop()
+    */
+   public void stop()
+   {
+      lockRemover.halt();
+      lockRemover.interrupt();
+      sessionLockManagers.clear();
+      cache.stop();
+   }
+
+   /**
+    * Copy <code>PropertyData prop<code> to new TransientItemData
+    * 
+    * @param prop
+    * @return
+    * @throws RepositoryException
+    */
+   private TransientItemData copyItemData(PropertyData prop) throws RepositoryException
+   {
+      if (prop == null)
+      {
+         return null;
+      }
+
+      // make a copy, value may be null for deleting items
+      TransientPropertyData newData =
+         new TransientPropertyData(prop.getQPath(), prop.getIdentifier(), prop.getPersistedVersion(), prop.getType(),
+            prop.getParentIdentifier(), prop.isMultiValued(), prop.getValues());
+
+      return newData;
+   }
+
+   /**
+    * Internal lock
+    * 
+    * @param nodeIdentifier
+    * @throws LockException
+    */
+   private synchronized void internalLock(String sessionId, String nodeIdentifier) throws LockException
+   {
+      CacheableSessionLockManager session = sessionLockManagers.get(sessionId);
+      if (session != null && session.cotainsPendingLock(nodeIdentifier))
+      {
+         LockData lockData = session.getPendingLock(nodeIdentifier);
+         Fqn<String> lockPath = makeLockFqn(lockData.getNodeIdentifier());
+
+         // addChild will add if absent or return old if present
+         Node<Serializable, Object> node = cache.getRoot().addChild(lockPath);
+
+         // this will return null if success. And old data if something exists...
+         LockData oldLockData = (LockData)node.putIfAbsent(LOCK_DATA, lockData);
+
+         if (oldLockData != null)
+         {
+            throw new LockException("Unable to write LockData. Node [" + lockData.getNodeIdentifier()
+               + "] already has LockData!");
+         }
+
+         session.notifyLockPersisted(nodeIdentifier);
+      }
+      else
+      {
+         throw new LockException("No lock in pending locks");
+      }
+   }
+
+   /**
+    * Internal unlock.
+    * 
+    * @param sessionId
+    * @param nodeIdentifier
+    * @throws LockException
+    */
+   private synchronized void internalUnLock(String sessionId, String nodeIdentifier) throws LockException
+   {
+      LockData lData = getLockDataById(nodeIdentifier);
+
+      if (lData != null)
+      {
+         cache.removeNode(makeLockFqn(nodeIdentifier));
+
+         CacheableSessionLockManager sessMgr = sessionLockManagers.get(sessionId);
+         if (sessMgr != null)
+         {
+            sessMgr.notifyLockRemoved(nodeIdentifier);
+         }
+      }
+   }
+
+   private final LockActionNonTxAware<Boolean, String> lockExist = new LockActionNonTxAware<Boolean, String>()
+   {
+      public Boolean execute(String nodeId) throws LockException
+      {
+         return cache.getRoot().hasChild(makeLockFqn(nodeId));
+      }
+   };
+
+   private boolean lockExist(String nodeId)
+   {
+      try
+      {
+         return executeLockActionNonTxAware(lockExist, nodeId);
+      }
+      catch (LockException e)
+      {
+         // ignore me will never occur
+      }
+      return false;
+   }
+
+   /**
+    * Calculates md5 hash of string.
+    * 
+    * @param token
+    * @return
+    */
+   public String getHash(String token)
+   {
+      String hash = "";
+      try
+      {
+         MessageDigest m = MessageDigest.getInstance("MD5");
+         m.update(token.getBytes(), 0, token.length());
+         hash = new BigInteger(1, m.digest()).toString(16);
+      }
+      catch (NoSuchAlgorithmException e)
+      {
+         log.error("Can't get instanse of MD5 MessageDigest!", e);
+      }
+      return hash;
+   }
+
+   /**
+    * Search lock in maps.
+    * 
+    * @param data
+    * @param searchType
+    * @return
+    */
+   public LockData getLockData(NodeData data, int searchType)
+   {
+      if (data == null)
+         return null;
+      LockData retval = null;
+      try
+      {
+         if ((searchType & SEARCH_EXECMATCH) != 0)
+         {
+            retval = getLockDataById(data.getIdentifier());
+         }
+         if (retval == null && (searchType & SEARCH_CLOSEDPARENT) != 0)
+         {
+
+            NodeData parentData = (NodeData)dataManager.getItemData(data.getParentIdentifier());
+            if (parentData != null)
+            {
+               retval = getLockDataById(parentData.getIdentifier());
+               // parent not found try to fo upper
+               if (retval == null)
+               {
+                  retval = getLockData(parentData, SEARCH_CLOSEDPARENT);
+               }
+            }
+         }
+         if (retval == null && (searchType & SEARCH_CLOSEDCHILD) != 0)
+         {
+
+            List<NodeData> childData = dataManager.getChildNodesData(data);
+            for (NodeData nodeData : childData)
+            {
+               retval = getLockDataById(nodeData.getIdentifier());
+               if (retval != null)
+                  break;
+            }
+            if (retval == null)
+            {
+               // child not found try to find diper
+               for (NodeData nodeData : childData)
+               {
+                  retval = getLockData(nodeData, SEARCH_CLOSEDCHILD);
+                  if (retval != null)
+                     break;
+               }
+            }
+         }
+      }
+      catch (RepositoryException e)
+      {
+         //TODO
+         return null;
+      }
+
+      return retval;
+   }
+
+   private final LockActionNonTxAware<LockData, String> getLockDataById = new LockActionNonTxAware<LockData, String>()
+   {
+      public LockData execute(String nodeId) throws LockException
+      {
+         return (LockData)cache.get(makeLockFqn(nodeId), LOCK_DATA);
+      }
+   };
+
+   protected LockData getLockDataById(String nodeId)
+   {
+      try
+      {
+         return executeLockActionNonTxAware(getLockDataById, nodeId);
+      }
+      catch (LockException e)
+      {
+         // ignore me will never occur
+      }
+      return null;
+   }
+
+   private final LockActionNonTxAware<List<LockData>, Object> getLockList =
+      new LockActionNonTxAware<List<LockData>, Object>()
+      {
+         public List<LockData> execute(Object arg) throws LockException
+         {
+            Set<Object> nodesId = cache.getChildrenNames(lockRoot);
+
+            List<LockData> locksData = new ArrayList<LockData>();
+            for (Object nodeId : nodesId)
+            {
+               LockData lockData = (LockData)cache.get(makeLockFqn((String)nodeId), LOCK_DATA);
+               if (lockData != null)
+               {
+                  locksData.add(lockData);
+               }
+            }
+            return locksData;
+         }
+      };
+
+   protected synchronized List<LockData> getLockList()
+   {
+      try
+      {
+         return executeLockActionNonTxAware(getLockList, null);
+      }
+      catch (LockException e)
+      {
+         // ignore me will never occur
+      }
+      return null;
+   }
+
+   /**
+    * Remove lock, used by Lock remover.
+    * 
+    * @param nodeIdentifier String
+    */
+   protected void removeLock(String nodeIdentifier)
+   {
+      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);
+
+         ItemData lockOwner =
+            copyItemData((PropertyData)dataManager.getItemData(nData, new QPathEntry(Constants.JCR_LOCKOWNER, 1)));
+
+         //TODO EXOJCR-412, should be refactored in future.
+         //Skip removing, because that lock was removed in other node of cluster.  
+         if (lockOwner == null)
+         {
+            return;
+         }
+
+         changesLog.add(ItemState.createDeletedState(lockOwner));
+
+         ItemData lockIsDeep =
+            copyItemData((PropertyData)dataManager.getItemData(nData, new QPathEntry(Constants.JCR_LOCKISDEEP, 1)));
+
+         //TODO EXOJCR-412, should be refactored in future.
+         //Skip removing, because that lock was removed in other node of cluster.  
+         if (lockIsDeep == null)
+         {
+            return;
+         }
+
+         changesLog.add(ItemState.createDeletedState(lockIsDeep));
+
+         // lock probably removed by other thread
+         if (lockOwner == null && lockIsDeep == null)
+         {
+            return;
+         }
+
+         dataManager.save(new TransactionChangesLog(changesLog));
+      }
+      catch (JCRInvalidItemStateException e)
+      {
+         //TODO EXOJCR-412, should be refactored in future.
+         //Skip property not found in DB, because that lock property was removed in other node of cluster.
+         if (log.isDebugEnabled())
+         {
+            log.debug("The propperty was removed in other node of cluster.", e);
+         }
+
+      }
+      catch (RepositoryException e)
+      {
+         log.error("Error occur during removing lock" + e.getLocalizedMessage(), e);
+      }
+   }
+
+   /**
+    * Release all resources associated with CacheableSessionLockManager.
+    * 
+    * @param sessionID - session identifier
+    */
+   public void closeSessionLockManager(String sessionID)
+   {
+
+      sessionLockManagers.remove(sessionID);
+   }
+
+   /**
+    * Make lock absolute Fqn, i.e. /$LOCKS/nodeID.
+    *
+    * @param itemId String
+    * @return Fqn
+    */
+   private Fqn<String> makeLockFqn(String nodeId)
+   {
+      return Fqn.fromRelativeElements(lockRoot, nodeId);
+   }
+
+   /**
+    *  Will be created structured node in cache, like /$LOCKS
+    */
+   private void createStructuredNode(Fqn<String> 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);
+   }
+
+   /**
+    * Execute the given action outside a transaction. This is needed since the {@link Cache} used by {@link CacheableLockManagerImpl}
+    * manages the persistence of its locks thanks to a {@link CacheLoader} and a {@link CacheLoader} lock the JBoss cache {@link Node}
+    * even for read operations which cause deadlock issue when a XA {@link Transaction} is already opened
+    * @throws LockException when a exception occurs
+    */
+   private <R, A> R executeLockActionNonTxAware(LockActionNonTxAware<R, A> action, A arg) throws LockException
+   {
+      Transaction tx = null;
+      try
+      {
+         if (tm != null)
+         {
+            try
+            {
+               tx = tm.suspend();
+            }
+            catch (Exception e)
+            {
+               log.warn("Cannot suspend the current transaction", e);
+            }
+         }
+         return action.execute(arg);
+      }
+      finally
+      {
+         if (tx != null)
+         {
+            try
+            {
+               tm.resume(tx);
+            }
+            catch (Exception e)
+            {
+               log.warn("Cannot resume the current transaction", e);
+            }
+         }
+      }
+   }
+
+   /**
+    * Actions that are not supposed to be called within a transaction
+    * 
+    * Created by The eXo Platform SAS
+    * Author : Nicolas Filotto 
+    *          nicolas.filotto at exoplatform.com
+    * 21 janv. 2010
+    */
+   private static interface LockActionNonTxAware<R, A>
+   {
+      R execute(A arg) throws LockException;
+   }
+
+   public LockData createLockData(NodeData node, String lockToken, boolean isDeep, boolean isSessionScoped,
+      String owner, long timeOut) throws LockException
+   {
+
+      LockData lData =
+         getLockData(node, CacheableLockManagerImpl.SEARCH_EXECMATCH | CacheableLockManagerImpl.SEARCH_CLOSEDPARENT);
+      if (lData != null)
+      {
+         if (lData.getNodeIdentifier().equals(node.getIdentifier()))
+         {
+            throw new LockException("Node already locked: " + node.getQPath());
+         }
+         else if (lData.isDeep())
+         {
+            throw new LockException("Parent node has deep lock.");
+         }
+      }
+
+      if (isDeep && getLockData(node, CacheableLockManagerImpl.SEARCH_CLOSEDCHILD) != null)
+      {
+         throw new LockException("Some child node is locked.");
+      }
+
+      String lockTokenHash = getHash(lockToken);
+
+      lData =
+         new LockData(node.getIdentifier(), lockTokenHash, isDeep, isSessionScoped, owner, timeOut > 0 ? timeOut
+            : getDefaultLockTimeOut());
+      return lData;
+   }
+
+   public LockData getExactOrCloseParentLock(NodeData node)
+   {
+      return getLockData(node, CacheableLockManagerImpl.SEARCH_EXECMATCH | CacheableLockManagerImpl.SEARCH_CLOSEDPARENT);
+
+   }
+
+   public boolean exactLockExist(NodeData node)
+   {
+      return getLockData(node, CacheableLockManagerImpl.SEARCH_EXECMATCH) != null;
+   }
+
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableSessionLockManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableSessionLockManager.java	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/jbosscache/CacheableSessionLockManager.java	2010-02-04 14:33:13 UTC (rev 1680)
@@ -28,7 +28,9 @@
 import org.exoplatform.services.log.Log;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import javax.jcr.AccessDeniedException;
 import javax.jcr.RepositoryException;
@@ -69,6 +71,11 @@
    private final Map<String, LockData> lockedNodes;
 
    /**
+    * Set of pending locked nodes identifiers.
+    */
+   private final Set<String> pendingLocks;
+
+   /**
     * Workspace lock manager
     */
    private final CacheableLockManager lockManager;
@@ -84,6 +91,7 @@
       this.sessionID = sessionID;
       this.tokens = new HashMap<String, String>();
       this.lockedNodes = new HashMap<String, LockData>();
+      this.pendingLocks = new HashSet<String>();
       this.lockManager = lockManager;
    }
 
@@ -94,42 +102,19 @@
       RepositoryException
    {
 
-      String nodeIdentifier = node.getInternalIdentifier();
-      LockData lData =
-         lockManager.getLockData((NodeData)node.getData(), CacheableLockManager.SEARCH_EXECMATCH
-            | CacheableLockManager.SEARCH_CLOSEDPARENT);
-      if (lData != null)
-      {
-         if (lData.getNodeIdentifier().equals(node.getInternalIdentifier()))
-         {
-            throw new LockException("Node already locked: " + node.getData().getQPath());
-         }
-         else if (lData.isDeep())
-         {
-            throw new LockException("Parent node has deep lock.");
-         }
-      }
-
-      if (isDeep && lockManager.getLockData((NodeData)node.getData(), CacheableLockManager.SEARCH_CLOSEDCHILD) != null)
-      {
-         throw new LockException("Some child node is locked.");
-      }
-
       String lockToken = IdGenerator.generate();
-      String lockTokenHash = lockManager.getHash(lockToken);
 
-      lData =
-         new LockData(nodeIdentifier, lockTokenHash, isDeep, isSessionScoped, node.getSession().getUserID(),
-            timeOut > 0 ? timeOut : lockManager.getDefaultLockTimeOut());
+      LockData lData =
+         lockManager.createLockData((NodeData)node.getData(), lockToken, isDeep, isSessionScoped, node.getSession()
+            .getUserID(), timeOut);
 
       lockedNodes.put(node.getInternalIdentifier(), lData);
-      lockManager.addPendingLock(nodeIdentifier, lData);
-      tokens.put(lockToken, lockTokenHash);
+      pendingLocks.add(node.getInternalIdentifier());
+      tokens.put(lockToken, lData.getTokenHash());
 
       LockImpl lock = new CacheLockImpl(node.getSession(), lData, this);
 
       return lock;
-
    }
 
    /**
@@ -145,9 +130,7 @@
     */
    public LockImpl getLock(NodeImpl node) throws LockException, RepositoryException
    {
-      LockData lData =
-         lockManager.getLockData((NodeData)node.getData(), CacheableLockManager.SEARCH_EXECMATCH
-            | CacheableLockManager.SEARCH_CLOSEDPARENT);
+      LockData lData = lockManager.getExactOrCloseParentLock((NodeData)node.getData());
 
       if (lData == null || (!node.getInternalIdentifier().equals(lData.getNodeIdentifier()) && !lData.isDeep()))
       {
@@ -171,7 +154,7 @@
     */
    public boolean holdsLock(NodeData node) throws RepositoryException
    {
-      return lockManager.getLockData(node, CacheableLockManager.SEARCH_EXECMATCH) != null;
+      return lockManager.exactLockExist(node);
    }
 
    /**
@@ -179,9 +162,7 @@
     */
    public boolean isLocked(NodeData node)
    {
-      LockData lData =
-         lockManager
-            .getLockData(node, CacheableLockManager.SEARCH_EXECMATCH | CacheableLockManager.SEARCH_CLOSEDPARENT);
+      LockData lData = lockManager.getExactOrCloseParentLock(node);
 
       if (lData == null || (!node.getIdentifier().equals(lData.getNodeIdentifier()) && !lData.isDeep()))
       {
@@ -195,9 +176,7 @@
     */
    public boolean isLockHolder(NodeImpl node) throws RepositoryException
    {
-      LockData lData =
-         lockManager.getLockData((NodeData)node.getData(), CacheableLockManager.SEARCH_EXECMATCH
-            | CacheableLockManager.SEARCH_CLOSEDPARENT);
+      LockData lData = lockManager.getExactOrCloseParentLock((NodeData)node.getData());
 
       return lData != null && isLockHolder(lData);
    }
@@ -216,7 +195,7 @@
       {
          LockData lock = lockedNodes.remove(nodeId);
 
-         if (lock.isSessionScoped())
+         if (lock.isSessionScoped() && !pendingLocks.contains(nodeId))
          {
             try
             {
@@ -247,25 +226,13 @@
                log.error(e.getLocalizedMessage());
             }
          }
-         else
-         {
-            lockManager.removePendingLock(nodeId);
+      }
 
-            lockedNodes.remove(nodeId);
+      pendingLocks.clear();
+      tokens.clear();
+      lockedNodes.clear();
 
-            //remove token
-            String hash = lock.getTokenHash();
-            for (String token : tokens.keySet())
-            {
-               if (tokens.get(token).equals(hash))
-               {
-                  tokens.remove(token);
-                  break;
-               }
-            }
-         }
-      }
-      lockManager.closeSession(sessionID);
+      lockManager.closeSessionLockManager(sessionID);
    }
 
    /**
@@ -313,7 +280,15 @@
     */
    protected boolean isLockLive(String nodeIdentifier)
    {
-      return lockManager.isLockLive(nodeIdentifier);
+
+      if (lockManager.isLockLive(nodeIdentifier))
+      {
+         return true;
+      }
+      else
+      {
+         return pendingLocks.contains(nodeIdentifier);
+      }
    }
 
    /**
@@ -324,7 +299,7 @@
     */
    protected void refresh(LockData newLockData) throws LockException
    {
-      lockManager.refresh(newLockData);
+      lockManager.refreshLockData(newLockData);
    }
 
    /**
@@ -337,4 +312,27 @@
       lockedNodes.remove(nodeIdentifier);
    }
 
+   protected void notifyLockPersisted(String nodeIdentifier)
+   {
+      pendingLocks.remove(nodeIdentifier);
+   }
+
+   protected boolean cotainsPendingLock(String nodeId)
+   {
+      return pendingLocks.contains(nodeId);
+   }
+
+   protected LockData getPendingLock(String nodeId)
+   {
+      //TODO check it 
+      if (pendingLocks.contains(nodeId))
+      {
+         return lockedNodes.get(nodeId);
+      }
+      else
+      {
+         return null;
+      }
+   }
+
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.default.sql
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.default.sql	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.default.sql	2010-02-04 14:33:13 UTC (rev 1680)
@@ -6,7 +6,9 @@
   IS_SESSIONSCOPED CHAR NOT NULL,
   IS_DEEP CHAR NOT NULL,
   BIRTHDAY BIGINT NOT NULL,
-  TIMEOUT BIGINT NOT NULL
+  TIMEOUT BIGINT NOT NULL,
+  CONSTRAINT JCR_PK_LOCK PRIMARY KEY(NODE_ID, WS_NAME)
 );
+
 CREATE INDEX JCR_ALL_WS_LOCKS ON JCR_LOCKS(WS_NAME, NODE_ID);
-CREATE UNIQUE INDEX JCR_LOCKS_BY_ID ON JCR_LOCKS(NODE_ID);
+CREATE INDEX JCR_LOCKS_BY_ID ON JCR_LOCKS(NODE_ID);

Modified: jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.ora.sql
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.ora.sql	2010-02-04 12:10:01 UTC (rev 1679)
+++ jcr/trunk/exo.jcr.component.core/src/main/resources/conf/storage/lock-jdbc.ora.sql	2010-02-04 14:33:13 UTC (rev 1680)
@@ -1,5 +1,5 @@
 /*$DELIMITER:/ */
-CREATE TABLE ${table.name}(
+CREATE TABLE JCR_LOCKS(
   WS_NAME VARCHAR(96) NOT NULL,
   NODE_ID VARCHAR(96) NOT NULL,
   TOKEN_HASH VARCHAR(32) NOT NULL,
@@ -7,10 +7,11 @@
   IS_SESSIONSCOPED CHAR NOT NULL,
   IS_DEEP CHAR NOT NULL,
   BIRTHDAY LONG NOT NULL,
-  TIMEOUT LONG NOT NULL
+  TIMEOUT LONG NOT NULL,
+  CONSTRAINT JCR_PK_LOCK PRIMARY KEY(NODE_ID, WS_NAME)
 )
 /
 CREATE INDEX JCR_ALL_WS_LOCKS ON JCR_LOCKS(WS_NAME, NODE_ID)
 /
-CREATE UNIQUE INDEX JCR_LOCKS_BY_ID ON JCR_LOCKS(NODE_ID)
+CREATE INDEX JCR_LOCKS_BY_ID ON JCR_LOCKS(NODE_ID)
 /
\ No newline at end of file



More information about the exo-jcr-commits mailing list