[exo-jcr-commits] exo-jcr SVN: r4770 - in jcr/trunk/exo.jcr.component.core/src: main/java/org/exoplatform/services/jcr/datamodel and 11 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Aug 18 02:39:45 EDT 2011


Author: sergiykarpenko
Date: 2011-08-18 02:39:44 -0400 (Thu, 18 Aug 2011)
New Revision: 4770

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java
Removed:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java
Log:
EXOJCR-1456: use bloom filters to store ids of nodes with ACL

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -275,16 +275,31 @@
    /**
     * Start buffering process.
     */
-   public void beginTransaction();
+   void beginTransaction();
 
    /**
     * Sort changes and commit data to the cache.
     */
-   public void commitTransaction();
+   void commitTransaction();
 
    /**
     * Forget about changes
     */
-   public void rollbackTransaction();
+   void rollbackTransaction();
 
+   /**
+    * Adds a new listener
+    * @param listener the listener to register
+    * @throws UnsupportedOperationException in case the listeners are not supported by the
+    * implementation
+    */
+   void addListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException;
+
+   /**
+    * Removes a listener
+    * @param listener the listener to remove
+    * @throws UnsupportedOperationException in case the listeners are not supported by the
+    * implementation
+    */
+   void removeListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException;
 }

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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.dataflow.persistent;
+
+import org.exoplatform.services.jcr.datamodel.ItemData;
+
+/**
+ * This class allows other class to be notified when a given cache event occurs
+ * 
+ * @author <a href="mailto:nfilotto at exoplatform.com">Nicolas Filotto</a>
+ * @version $Id$
+ *
+ */
+public interface WorkspaceStorageCacheListener
+{
+   /**
+    * Called when a cache entry corresponding to the given item has been added
+    * @param data the item corresponding to the added cache entry
+    */
+   void onCacheEntryAdded(ItemData data);
+   
+   /**
+    * Called when a cache entry corresponding to the given item has been updated
+    * @param data the item corresponding to the updated cache entry
+    */
+   void onCacheEntryUpdated(ItemData data);
+   
+   /**
+    * Called when a cache entry corresponding to the given item has been removed
+    * @param data the item corresponding to the removed cache entry
+    */
+   void onCacheEntryRemoved(ItemData data);
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -469,6 +469,34 @@
    }
 
    /**
+    * Make child path using QName and Item index. <br/>
+    * 
+    * @param parent
+    *          - parent QPath
+    * @param name
+    *          - Item QName
+    * @param itemIndex
+    *          - Item index
+    * @param id
+    *          - Item id
+    * @return new QPath
+    */
+   public static QPath makeChildPath(final QPath parent, final QName name, final int itemIndex, String id)
+   {
+
+      QPathEntry[] parentEntries = parent.getEntries();
+      QPathEntry[] names = new QPathEntry[parentEntries.length + 1];
+      int index = 0;
+      for (QPathEntry pname : parentEntries)
+      {
+         names[index++] = pname;
+      }
+      names[index] = new QPathEntry(name.getNamespace(), name.getName(), itemIndex, id);
+
+      QPath path = new QPath(names);
+      return path;
+   }
+   /**
     * Make child path using array of QPath entries (relative path). <br/>
     * 
     * @param parent

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -36,6 +36,8 @@
 
    private String cachedToStringShowIndex;
 
+   private String id;
+   
    /**
     * QPathEntry constructor.
     * 
@@ -46,10 +48,27 @@
     */
    public QPathEntry(InternalQName qName, int index)
    {
+      this(qName, index, null);
+   }
+
+   
+   /**
+    * QPathEntry constructor.
+    * 
+    * @param qName
+    *          - InternalQName (full qualified name)
+    * @param index
+    *          - Item index
+    * @param id
+    *          - Item id
+    */
+   public QPathEntry(InternalQName qName, int index, String id)
+   {
       super(qName.getNamespace(), qName.getName());
       this.index = index > 0 ? index : 1;
+      this.id = id;
    }
-
+   
    /**
     * QPathEntry constructor.
     * 
@@ -62,8 +81,26 @@
     */
    public QPathEntry(String namespace, String name, int index)
    {
+      this(namespace, name, index, null);
+   }
+   
+   /**
+    * QPathEntry constructor.
+    * 
+    * @param namespace
+    *          - namespace URI
+    * @param name
+    *          - Item name
+    * @param index
+    *          - Item index
+    * @param id
+    *          - Item id
+    */
+   public QPathEntry(String namespace, String name, int index, String id)
+   {
       super(namespace, name);
       this.index = index > 0 ? index : 1;
+      this.id = id;
    }
 
    /**
@@ -89,6 +126,16 @@
 
       InternalQName qname = InternalQName.parse(qnameString);
       return new QPathEntry(qname, Integer.valueOf(indexString));
+   }   
+   
+   /**
+    * Return Item id, can be null since it could not be set
+    * 
+    * @return the id of the item
+    */
+   public String getId()
+   {
+      return id;
    }
 
    /**

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 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.dataflow.persistent;
+
+/**
+ * @author <a href="mailto:nfilotto at exoplatform.com">Nicolas Filotto</a>
+ * @version $Id$
+ *
+ */
+public class ACLHolder
+{
+   /**
+    * The id of the node that holds some ACL info
+    */
+   private final String id;
+   
+   /**
+    * A flag indicating whether or not the node has owner set
+    */
+   private boolean owner;
+   
+   /**
+    * A flag indicating whether or not the node has permissions set
+    */
+   private boolean permissions;
+
+   public ACLHolder(String id)
+   {
+      this.id = id;
+   }
+
+   /**
+    * @return the id
+    */
+   public String getId()
+   {
+      return id;
+   }
+
+   /**
+    * @return the owner
+    */
+   public boolean hasOwner()
+   {
+      return owner;
+   }
+
+   /**
+    * @return the permissions
+    */
+   public boolean hasPermissions()
+   {
+      return permissions;
+   }
+
+   /**
+    * @param owner the owner to set
+    */
+   public void setOwner(boolean owner)
+   {
+      this.owner = owner;
+   }
+
+   /**
+    * @param permissions the permissions to set
+    */
+   public void setPermissions(boolean permissions)
+   {
+      this.permissions = permissions;
+   }
+}

Deleted: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -1,174 +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.dataflow.persistent;
-
-import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
-import org.exoplatform.services.jcr.dataflow.SharedDataManager;
-import org.exoplatform.services.jcr.datamodel.ItemData;
-import org.exoplatform.services.jcr.datamodel.ItemType;
-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.core.itemfilters.QPathEntryFilter;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-
-import java.util.Calendar;
-import java.util.List;
-
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.RepositoryException;
-
-/**
- * Created by The eXo Platform SAS. Data Manager supported ACL Inheritance
- * 
- * @author Gennady Azarenkov
- * @version $Id: ACLInheritanceSupportedWorkspaceDataManager.java 11907 2008-03-13 15:36:21Z ksm $
- */
-public class ACLInheritanceSupportedWorkspaceDataManager implements SharedDataManager
-{
-
-   private static Log LOG = ExoLogger.getLogger("exo.jcr.component.core.ACLInheritanceSupportedWorkspaceDataManager");
-
-   protected final CacheableWorkspaceDataManager persistentManager;
-
-   public ACLInheritanceSupportedWorkspaceDataManager(CacheableWorkspaceDataManager persistentManager)
-   {
-      this.persistentManager = persistentManager;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   // ------------ ItemDataConsumer impl ------------
-   public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException
-   {
-      return persistentManager.getChildNodesData(parent);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> patternFilters) throws RepositoryException
-   {
-      return persistentManager.getChildNodesData(parent, patternFilters);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
-      throws RepositoryException
-   {
-      return persistentManager.getChildNodesDataByPage(parent, fromOrderNum, limit, childs);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public int getLastOrderNumber(final NodeData parent) throws RepositoryException
-   {
-      return persistentManager.getLastOrderNumber(parent);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public int getChildNodesCount(final NodeData parent) throws RepositoryException
-   {
-      return persistentManager.getChildNodesCount(parent);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public ItemData getItemData(NodeData parent, QPathEntry name) throws RepositoryException
-   {
-      return getItemData(parent, name, ItemType.UNKNOWN);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public ItemData getItemData(NodeData parent, QPathEntry name, ItemType itemType) throws RepositoryException
-   {
-      return persistentManager.getItemData(parent, name, itemType);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public ItemData getItemData(String identifier) throws RepositoryException
-   {
-      return persistentManager.getItemData(identifier);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException
-   {
-      return persistentManager.getChildPropertiesData(parent);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> itemDataFilters)
-      throws RepositoryException
-   {
-      return persistentManager.getChildPropertiesData(parent, itemDataFilters);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException
-   {
-      return persistentManager.listChildPropertiesData(parent);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public List<PropertyData> getReferencesData(String identifier, boolean skipVersionStorage)
-      throws RepositoryException
-   {
-      return persistentManager.getReferencesData(identifier, skipVersionStorage);
-   }
-
-   // ------------ SharedDataManager ----------------------
-
-   /**
-    * {@inheritDoc}
-    */
-   public void save(ItemStateChangesLog changes) throws InvalidItemStateException, UnsupportedOperationException,
-      RepositoryException
-   {
-      persistentManager.save(changes);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public Calendar getCurrentTime()
-   {
-      return persistentManager.getCurrentTime();
-   }
-}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2011 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.dataflow.persistent;
+
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.BitSet;
+import java.util.Collection;
+
+/**
+ * Implementation of a Bloom-filter, as described here:
+ * http://en.wikipedia.org/wiki/Bloom_filter
+ *
+ * Inspired by the SimpleBloomFilter-class written by Ian Clarke. This
+ * implementation provides a more evenly distributed Hash-function by
+ * using a proper digest instead of the Java RNG. Many of the changes
+ * were proposed in comments in his blog:
+ * http://blog.locut.us/2008/01/12/a-decent-stand-alone-java-bloom-filter-implementation/
+ *
+ * @param <E> Object type that is to be inserted into the Bloom filter, e.g. String or Integer.
+ * @author Magnus Skjegstad <magnus at skjegstad.com>
+ */
+public class BloomFilter<E> implements Serializable {
+    private BitSet bitset;
+    private int bitSetSize;
+    private double bitsPerElement;
+    private int expectedNumberOfFilterElements; // expected (maximum) number of elements to be added
+    private int numberOfAddedElements; // number of elements actually added to the Bloom filter
+    private int k; // number of hash functions
+
+    static final Charset charset = Charset.forName("UTF-8"); // encoding used for storing hash values as strings
+
+    static final String hashName = "MD5"; // MD5 gives good enough accuracy in most circumstances. Change to SHA1 if it's needed
+    static final MessageDigest digestFunction;
+    static { // The digest method is reused between instances
+        MessageDigest tmp;
+        try {
+            tmp = java.security.MessageDigest.getInstance(hashName);
+        } catch (NoSuchAlgorithmException e) {
+            tmp = null;
+        }
+        digestFunction = tmp;
+    }
+
+    /**
+      * Constructs an empty Bloom filter. The total length of the Bloom filter will be
+     * c*n.
+      *
+      * @param c is the number of bits used per element.
+      * @param n is the expected number of elements the filter will contain.
+      * @param k is the number of hash functions used.
+      */
+    public BloomFilter(double c, int n, int k) {
+      this.expectedNumberOfFilterElements = n;
+      this.k = k;
+      this.bitsPerElement = c;
+      this.bitSetSize = (int)Math.ceil(c * n);
+      numberOfAddedElements = 0;
+      this.bitset = new BitSet(bitSetSize);
+    }
+
+    /**
+     * Constructs an empty Bloom filter. The optimal number of hash functions (k) is estimated from the total size of the Bloom
+     * and the number of expected elements.
+     *
+     * @param bitSetSize defines how many bits should be used in total for the filter.
+     * @param expectedNumberOElements defines the maximum number of elements the filter is expected to contain.
+     */
+    public BloomFilter(int bitSetSize, int expectedNumberOElements) {
+        this(bitSetSize / (double)expectedNumberOElements,
+             expectedNumberOElements,
+             (int) Math.round((bitSetSize / (double)expectedNumberOElements) * Math.log(2.0)));
+    }
+
+    /**
+     * Constructs an empty Bloom filter with a given false positive probability. The number of bits per
+     * element and the number of hash functions is estimated
+     * to match the false positive probability.
+     *
+     * @param falsePositiveProbability is the desired false positive probability.
+     * @param expectedNumberOfElements is the expected number of elements in the Bloom filter.
+     */
+    public BloomFilter(double falsePositiveProbability, int expectedNumberOfElements) {
+        this(Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2))) / Math.log(2), // c = k / ln(2)
+             expectedNumberOfElements,
+             (int)Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))); // k = ceil(-log_2(false prob.))
+    }
+
+    /**
+     * Construct a new Bloom filter based on existing Bloom filter data.
+     *
+     * @param bitSetSize defines how many bits should be used for the filter.
+     * @param expectedNumberOfFilterElements defines the maximum number of elements the filter is expected to contain.
+     * @param actualNumberOfFilterElements specifies how many elements have been inserted into the <code>filterData</code> BitSet.
+     * @param filterData a BitSet representing an existing Bloom filter.
+     */
+    public BloomFilter(int bitSetSize, int expectedNumberOfFilterElements, int actualNumberOfFilterElements, BitSet filterData) {
+        this(bitSetSize, expectedNumberOfFilterElements);
+        this.bitset = filterData;
+        this.numberOfAddedElements = actualNumberOfFilterElements;
+    }
+
+    /**
+     * Generates a digest based on the contents of a String.
+     *
+     * @param val specifies the input data.
+     * @param charset specifies the encoding of the input data.
+     * @return digest as long.
+     */
+    public static long createHash(String val, Charset charset) {
+        return createHash(val.getBytes(charset));
+    }
+
+    /**
+     * Generates a digest based on the contents of a String.
+     *
+     * @param val specifies the input data. The encoding is expected to be UTF-8.
+     * @return digest as long.
+     */
+    public static long createHash(String val) {
+        return createHash(val, charset);
+    }
+
+    /**
+     * Generates a digest based on the contents of an array of bytes.
+     *
+     * @param data specifies input data.
+     * @return digest as long.
+     */
+    public static long createHash(byte[] data) {
+        long h = 0;
+        byte[] res;
+
+        synchronized (digestFunction) {
+            res = digestFunction.digest(data);
+        }
+
+        for (int i = 0; i < 4; i++) {
+            h <<= 8;
+            h |= ((int) res[i]) & 0xFF;
+        }
+        return h;
+    }
+
+    /**
+     * Compares the contents of two instances to see if they are equal.
+     *
+     * @param obj is the object to compare to.
+     * @return True if the contents of the objects are equal.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final BloomFilter<E> other = (BloomFilter<E>) obj;        
+        if (this.expectedNumberOfFilterElements != other.expectedNumberOfFilterElements) {
+            return false;
+        }
+        if (this.k != other.k) {
+            return false;
+        }
+        if (this.bitSetSize != other.bitSetSize) {
+            return false;
+        }
+        if (this.bitset != other.bitset && (this.bitset == null || !this.bitset.equals(other.bitset))) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Calculates a hash code for this class.
+     * @return hash code representing the contents of an instance of this class.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 61 * hash + (this.bitset != null ? this.bitset.hashCode() : 0);
+        hash = 61 * hash + this.expectedNumberOfFilterElements;
+        hash = 61 * hash + this.bitSetSize;
+        hash = 61 * hash + this.k;
+        return hash;
+    }
+
+
+    /**
+     * Calculates the expected probability of false positives based on
+     * the number of expected filter elements and the size of the Bloom filter.
+     * <br /><br />
+     * The value returned by this method is the <i>expected</i> rate of false
+     * positives, assuming the number of inserted elements equals the number of
+     * expected elements. If the number of elements in the Bloom filter is less
+     * than the expected value, the true probability of false positives will be lower.
+     *
+     * @return expected probability of false positives.
+     */
+    public double expectedFalsePositiveProbability() {
+        return getFalsePositiveProbability(expectedNumberOfFilterElements);
+    }
+
+    /**
+     * Calculate the probability of a false positive given the specified
+     * number of inserted elements.
+     *
+     * @param numberOfElements number of inserted elements.
+     * @return probability of a false positive.
+     */
+    public double getFalsePositiveProbability(double numberOfElements) {
+        // (1 - e^(-k * n / m)) ^ k
+        return Math.pow((1 - Math.exp(-k * (double) numberOfElements
+                        / (double) bitSetSize)), k);
+
+    }
+
+    /**
+     * Get the current probability of a false positive. The probability is calculated from
+     * the size of the Bloom filter and the current number of elements added to it.
+     *
+     * @return probability of false positives.
+     */
+    public double getFalsePositiveProbability() {
+        return getFalsePositiveProbability(numberOfAddedElements);
+    }
+
+
+    /**
+     * Returns the value chosen for K.<br />
+     * <br />
+     * K is the optimal number of hash functions based on the size
+     * of the Bloom filter and the expected number of inserted elements.
+     *
+     * @return optimal k.
+     */
+    public int getK() {
+        return k;
+    }
+
+    /**
+     * Sets all bits to false in the Bloom filter.
+     */
+    public void clear() {
+        bitset.clear();
+        numberOfAddedElements = 0;
+    }
+
+    /**
+     * Adds an object to the Bloom filter. The output from the object's
+     * toString() method is used as input to the hash functions.
+     *
+     * @param element is an element to register in the Bloom filter.
+     */
+    public void add(E element) {
+       long hash;
+       String valString = element.toString();
+       for (int x = 0; x < k; x++) {
+           hash = createHash(valString + Integer.toString(x));
+           hash = hash % (long)bitSetSize;
+           bitset.set(Math.abs((int)hash), true);
+       }
+       numberOfAddedElements ++;
+    }
+
+    /**
+     * Adds all elements from a Collection to the Bloom filter.
+     * @param c Collection of elements.
+     */
+    public void addAll(Collection<? extends E> c) {
+        for (E element : c)
+            add(element);
+    }
+
+    /**
+     * Returns true if the element could have been inserted into the Bloom filter.
+     * Use getFalsePositiveProbability() to calculate the probability of this
+     * being correct.
+     *
+     * @param element element to check.
+     * @return true if the element could have been inserted into the Bloom filter.
+     */
+    public boolean contains(E element) {
+       long hash;
+       String valString = element.toString();
+       for (int x = 0; x < k; x++) {
+           hash = createHash(valString + Integer.toString(x));
+           hash = hash % (long)bitSetSize;
+           if (!bitset.get(Math.abs((int)hash)))
+               return false;
+       }
+       return true;
+    }
+
+    /**
+     * Returns true if all the elements of a Collection could have been inserted
+     * into the Bloom filter. Use getFalsePositiveProbability() to calculate the
+     * probability of this being correct.
+     * @param c elements to check.
+     * @return true if all the elements in c could have been inserted into the Bloom filter.
+     */
+    public boolean containsAll(Collection<? extends E> c) {
+        for (E element : c)
+            if (!contains(element))
+                return false;
+        return true;
+    }
+
+    /**
+     * Read a single bit from the Bloom filter.
+     * @param bit the bit to read.
+     * @return true if the bit is set, false if it is not.
+     */
+    public boolean getBit(int bit) {
+        return bitset.get(bit);
+    }
+
+    /**
+     * Set a single bit in the Bloom filter.
+     * @param bit is the bit to set.
+     * @param value If true, the bit is set. If false, the bit is cleared.
+     */
+    public void setBit(int bit, boolean value) {
+        bitset.set(bit, value);
+    }
+
+    /**
+     * Return the bit set used to store the Bloom filter.
+     * @return bit set representing the Bloom filter.
+     */
+    public BitSet getBitSet() {
+        return bitset;
+    }
+
+    /**
+     * Returns the number of bits in the Bloom filter. Use count() to retrieve
+     * the number of inserted elements.
+     *
+     * @return the size of the bitset used by the Bloom filter.
+     */
+    public int size() {
+        return this.bitSetSize;
+    }
+
+    /**
+     * Returns the number of elements added to the Bloom filter after it
+     * was constructed or after clear() was called.
+     *
+     * @return number of elements added to the Bloom filter.
+     */
+    public int count() {
+        return this.numberOfAddedElements;
+    }
+
+    /**
+     * Returns the expected number of elements to be inserted into the filter.
+     * This value is the same value as the one passed to the constructor.
+     *
+     * @return expected number of elements.
+     */
+    public int getExpectedNumberOfElements() {
+        return expectedNumberOfFilterElements;
+    }
+
+    /**
+     * Get expected number of bits per element when the Bloom filter is full. This value is set by the constructor
+     * when the Bloom filter is created. See also getBitsPerElement().
+     *
+     * @return expected number of bits per element.
+     */
+    public double getExpectedBitsPerElement() {
+        return this.bitsPerElement;
+    }
+
+    /**
+     * Get actual number of bits per element based on the number of elements that have currently been inserted and the length
+     * of the Bloom filter. See also getExpectedBitsPerElement().
+     *
+     * @return number of bits per element.
+     */
+    public double getBitsPerElement() {
+        return this.bitSetSize / (double)numberOfAddedElements;
+    }
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -19,10 +19,14 @@
 package org.exoplatform.services.jcr.impl.dataflow.persistent;
 
 import org.exoplatform.commons.utils.SecurityHelper;
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.services.jcr.access.AccessControlEntry;
 import org.exoplatform.services.jcr.access.AccessControlList;
 import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
 import org.exoplatform.services.jcr.dataflow.persistent.MandatoryItemsPersistenceListener;
 import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
 import org.exoplatform.services.jcr.datamodel.ItemData;
 import org.exoplatform.services.jcr.datamodel.ItemType;
 import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -43,12 +47,14 @@
 import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
 import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
 import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
 import org.exoplatform.services.rpc.RPCException;
 import org.exoplatform.services.rpc.RPCService;
 import org.exoplatform.services.rpc.RemoteCommand;
 import org.exoplatform.services.rpc.TopologyChangeEvent;
 import org.exoplatform.services.rpc.TopologyChangeListener;
 import org.exoplatform.services.transaction.TransactionService;
+import org.picocontainer.Startable;
 
 import java.io.Serializable;
 import java.security.PrivilegedAction;
@@ -64,6 +70,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.jcr.RepositoryException;
@@ -80,7 +87,7 @@
  * @version $Id$
  */
 public class CacheableWorkspaceDataManager extends WorkspacePersistentDataManager implements Suspendable,
-   TopologyChangeListener
+   TopologyChangeListener, Startable, WorkspaceStorageCacheListener
 {
 
    /**
@@ -98,6 +105,12 @@
     */
    private final TransactionableResourceManager txResourceManager;
 
+   private final AtomicBoolean filtersEnabled = new AtomicBoolean();
+
+   private volatile BloomFilter<String> filterPermissions;
+
+   private volatile BloomFilter<String> filterOwner;
+
    private TransactionManager transactionManager;
 
    /**
@@ -2024,16 +2037,30 @@
 
    /**
     * Init ACL of the node.
-    * 
     * @param parent
     *          - a parent, can be null (get item by id)
-    * @param data
+    * @param node
     *          - an item data
     * @return - an item data with ACL was initialized
     * @throws RepositoryException
     */
    private ItemData initACL(NodeData parent, NodeData node) throws RepositoryException
    {
+      return initACL(parent, node, null);
+   }
+
+   /**
+    * @param parent
+    *          - a parent, can be null (get item by id)
+    * @param node
+    *          - an node data
+    * @param search
+    *          - indicates what we are looking for
+    * @return - an node data with ACL was initialized
+    * @throws RepositoryException
+    */
+   private NodeData initACL(NodeData parent, NodeData node, ACLSearch search) throws RepositoryException
+   {
       if (node != null)
       {
          AccessControlList acl = node.getACL();
@@ -2049,17 +2076,37 @@
             }
             else
             {
+               if (search == null)
+               {
+                  search = new ACLSearch(null, null);
+               }
                // use nearest ancestor ACL... case of get by id
                node =
                   new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(), node
                      .getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
-                     node.getParentIdentifier(), getNearestACAncestorAcl(node));
+                     node.getParentIdentifier(), getNearestACAncestorAcl(node, search));
             }
          }
          else if (!acl.hasPermissions())
          {
             // use nearest ancestor permissions
-            AccessControlList ancestorAcl = getNearestACAncestorAcl(node);
+            if (search == null)
+            {
+               search = new ACLSearch(acl.getOwner(), null);
+            }
+            else
+            {
+               search.setOwner(acl.getOwner());
+               if (search.found())
+               {
+                  return new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(),
+                     node.getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
+                     node.getParentIdentifier(), new AccessControlList(acl.getOwner(), null));
+               }
+            }
+            AccessControlList ancestorAcl =
+               parent != null && parent.getACL() != null && parent.getACL().hasPermissions() ? parent.getACL()
+                  : getNearestACAncestorAcl(node, search);
 
             node =
                new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(), node
@@ -2068,8 +2115,24 @@
          }
          else if (!acl.hasOwner())
          {
+            if (search == null)
+            {
+               search = new ACLSearch(null, acl.getPermissionEntries());
+            }
+            else
+            {
+               search.setPermissions(acl.getPermissionEntries());
+               if (search.found())
+               {
+                  return new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(),
+                     node.getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
+                     node.getParentIdentifier(), new AccessControlList(null, acl.getPermissionEntries()));
+               }
+            }
             // use nearest ancestor owner
-            AccessControlList ancestorAcl = getNearestACAncestorAcl(node);
+            AccessControlList ancestorAcl =
+               parent != null && parent.getACL() != null && parent.getACL().hasOwner() ? parent.getACL()
+                  : getNearestACAncestorAcl(node, search);
 
             node =
                new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(), node
@@ -2088,27 +2151,289 @@
     * 
     * @param node
     *          - item
+    * @param search
+    *          - indicates what we are looking for
     * @return - parent or null
     * @throws RepositoryException
     */
-   private AccessControlList getNearestACAncestorAcl(NodeData node) throws RepositoryException
+   private AccessControlList getNearestACAncestorAcl(NodeData node, ACLSearch search) throws RepositoryException
    {
-
-      if (node.getParentIdentifier() != null)
+      String id = node.getParentIdentifier();
+      if (id != null)
       {
-         NodeData parent = (NodeData)getItemData(node.getParentIdentifier());
-         while (parent != null)
+         boolean filtersEnabled = this.filtersEnabled.get();
+         BloomFilter<String> filterPermissions = this.filterPermissions;
+         BloomFilter<String> filterOwner = this.filterOwner;
+         if (filtersEnabled && filterOwner != null && filterPermissions != null)
          {
-            if (parent.getACL() != null)
+            QPathEntry[] entries = node.getQPath().getEntries();
+            for (int i = entries.length - 2; i >= 0; i--)
             {
-               // has an AC parent
-               return parent.getACL();
+               QPathEntry entry = entries[i];
+               String currentId = entry.getId();
+               if (currentId == null)
+               {
+                  // the path doesn't contain any id so we do a normal call
+                  break;
+               }
+               else if ((!search.hasOwner() && filterOwner.contains(currentId))
+                  || (!search.hasPermissions() && filterPermissions.contains(currentId)))
+               {
+                  id = currentId;
+                  break;
+               }
+               else
+               {
+                  id = currentId;
+               }
             }
-            // going up to the root
-            parent = (NodeData)getItemData(parent.getParentIdentifier());
          }
+         NodeData parent = getACL(id, search);
+         if (parent != null && parent.getACL() != null)
+         {
+            // has an AC parent
+            return parent.getACL();
+         }
       }
       return new AccessControlList();
    }
 
+   /**
+    * Find Item by identifier to get the missing ACL information.
+    * 
+    * @param identifier the id of the node that we are looking for to fill the ACL research
+    * @param search the ACL search describing what we are looking for
+    * @return NodeData, data by identifier
+    */
+   private NodeData getACL(String identifier, ACLSearch search) throws RepositoryException
+   {
+      final ItemData item = getItemData(identifier);
+      return item != null && item.isNode() ? initACL(null, (NodeData)item, search) : null;
+   }
+
+   /**
+    * Gets the list of all the ACL holders
+    * @throws RepositoryException if an error occurs
+    */
+   public List<ACLHolder> getACLHolders() throws RepositoryException
+   {
+      WorkspaceStorageConnection conn = dataContainer.openConnection();
+      try
+      {
+         return conn.getACLHolders();
+      }
+      finally
+      {
+         conn.close();
+      }
+   }
+
+   /**
+    * Reloads the bloom filters
+    * @return <code>true</code> if the filters could be reloaded successfully, <code>false</code> otherwise.
+    */
+   @Managed
+   @ManagedDescription("Reloads the bloom filters used to efficiently manage the ACLs")
+   public boolean reloadFilters()
+   {
+      return loadFilters(false);
+   }
+
+   /**
+    * Clears the bloom filters
+    */
+   protected void clear()
+   {
+      this.filterPermissions = null;
+      this.filterOwner = null;
+   }
+
+   /**
+    * @see org.picocontainer.Startable#start()
+    */
+   public void start()
+   {
+      try
+      {
+         this.cache.addListener(this);
+      }
+      catch (UnsupportedOperationException e)
+      {
+         if (LOG.isDebugEnabled())
+         {
+            LOG.debug("The method addListener is not supported", e);
+         }
+         return;
+      }
+
+      loadFilters(true);
+   }
+
+   /**
+    * Loads the bloom filters
+    * @param cleanOnFail clean everything if an error occurs
+    * @return <code>true</code> if the filters could be loaded successfully, <code>false</code> otherwise.
+    */
+   protected boolean loadFilters(boolean cleanOnFail)
+   {
+      filtersEnabled.set(false);
+      // TODO: Make it configurable
+      this.filterPermissions = new BloomFilter<String>(0.1d, 1000000);
+      this.filterOwner = new BloomFilter<String>(0.1d, 1000000);
+      boolean fails = true;
+      List<ACLHolder> holders = null;
+      try
+      {
+         LOG.info("Getting all the ACL Holders from the persistence layer");
+         holders = getACLHolders();
+         fails = false;
+      }
+      catch (UnsupportedOperationException e)
+      {
+         if (LOG.isDebugEnabled())
+         {
+            LOG.debug("The method getACLHolders is not supported", e);
+         }
+      }
+      catch (RepositoryException e)
+      {
+         LOG.error("Could not load all the ACL loaders", e);
+      }
+      if (fails)
+      {
+         if (cleanOnFail)
+         {
+            clear();
+            cache.removeListener(this);
+         }
+         return false;
+      }
+      else if (holders != null && !holders.isEmpty())
+      {
+         LOG.info("Adding all the ACL Holders found into the BloomFilters");
+         for (int i = 0, length = holders.size(); i < length; i++)
+         {
+            ACLHolder holder = holders.get(i);
+            if (holder.hasOwner())
+            {
+               filterOwner.add(holder.getId());
+            }
+            if (holder.hasPermissions())
+            {
+               filterPermissions.add(holder.getId());
+            }
+         }
+      }
+      filtersEnabled.set(true);
+      return true;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void stop()
+   {
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void onCacheEntryAdded(ItemData data)
+   {
+      onCacheEntryUpdated(data);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void onCacheEntryUpdated(ItemData data)
+   {
+      if (data instanceof NodeData)
+      {
+         NodeData node = (NodeData)data;
+         AccessControlList acl = node.getACL();
+         if (acl == null)
+         {
+            return;
+         }
+         if (acl.hasOwner())
+         {
+            filterOwner.add(node.getIdentifier());
+         }
+         if (acl.hasPermissions())
+         {
+            filterPermissions.add(node.getIdentifier());
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void onCacheEntryRemoved(ItemData data)
+   {
+   }
+
+   /**
+    * Defines what we are really looking for
+    */
+   private static class ACLSearch
+   {
+      private String owner;
+
+      private List<AccessControlEntry> permissions;
+
+      ACLSearch(String owner, List<AccessControlEntry> permissions)
+      {
+         this.owner = owner;
+         this.permissions = permissions;
+      }
+
+      /**
+       * @return <code>true</code> if the owner and the permission have been found, <code>false</code>
+       * otherwise
+       */
+      public boolean found()
+      {
+         return owner != null && permissions != null;
+      }
+
+      /**
+       * @param owner the owner to set
+       */
+      public void setOwner(String owner)
+      {
+         if (this.owner == null)
+         {
+            this.owner = owner;
+         }
+      }
+
+      /**
+       * @param permissions the permissions to set
+       */
+      public void setPermissions(List<AccessControlEntry> permissions)
+      {
+         if (this.permissions == null)
+         {
+            this.permissions = permissions;
+         }
+      }
+
+      /**
+       * @return the owner
+       */
+      public boolean hasOwner()
+      {
+         return owner != null;
+      }
+
+      /**
+       * @return the permissions
+       */
+      public boolean hasPermissions()
+      {
+         return permissions != null;
+      }
+   }
 }
\ No newline at end of file

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -24,6 +24,7 @@
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
 import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
 import org.exoplatform.services.jcr.datamodel.ItemData;
 import org.exoplatform.services.jcr.datamodel.ItemType;
 import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -2216,4 +2217,20 @@
    public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int fromOrderNum)
    {
    }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void addListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException("The cache listeners are not supported by the LinkedWorkspaceStorageCacheImpl");
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void removeListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException("The cache listeners are not supported by the LinkedWorkspaceStorageCacheImpl");
+   }   
 }

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -0,0 +1,174 @@
+/*
+ * 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.dataflow.persistent;
+
+import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
+import org.exoplatform.services.jcr.dataflow.SharedDataManager;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.ItemType;
+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.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.util.Calendar;
+import java.util.List;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.RepositoryException;
+
+/**
+ * Created by The eXo Platform SAS. Data Manager supported ACL Inheritance
+ * 
+ * @author Gennady Azarenkov
+ * @version $Id: ACLInheritanceSupportedWorkspaceDataManager.java 11907 2008-03-13 15:36:21Z ksm $
+ */
+public class ShareableSupportedWorkspaceDataManager implements SharedDataManager
+{
+
+   private static Log LOG = ExoLogger.getLogger("exo.jcr.component.core.ACLInheritanceSupportedWorkspaceDataManager");
+
+   protected final CacheableWorkspaceDataManager persistentManager;
+
+   public ShareableSupportedWorkspaceDataManager(CacheableWorkspaceDataManager persistentManager)
+   {
+      this.persistentManager = persistentManager;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   // ------------ ItemDataConsumer impl ------------
+   public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException
+   {
+      return persistentManager.getChildNodesData(parent);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> patternFilters) throws RepositoryException
+   {
+      return persistentManager.getChildNodesData(parent, patternFilters);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+      throws RepositoryException
+   {
+      return persistentManager.getChildNodesDataByPage(parent, fromOrderNum, limit, childs);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public int getLastOrderNumber(final NodeData parent) throws RepositoryException
+   {
+      return persistentManager.getLastOrderNumber(parent);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public int getChildNodesCount(final NodeData parent) throws RepositoryException
+   {
+      return persistentManager.getChildNodesCount(parent);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public ItemData getItemData(NodeData parent, QPathEntry name) throws RepositoryException
+   {
+      return getItemData(parent, name, ItemType.UNKNOWN);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public ItemData getItemData(NodeData parent, QPathEntry name, ItemType itemType) throws RepositoryException
+   {
+      return persistentManager.getItemData(parent, name, itemType);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public ItemData getItemData(String identifier) throws RepositoryException
+   {
+      return persistentManager.getItemData(identifier);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException
+   {
+      return persistentManager.getChildPropertiesData(parent);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> itemDataFilters)
+      throws RepositoryException
+   {
+      return persistentManager.getChildPropertiesData(parent, itemDataFilters);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException
+   {
+      return persistentManager.listChildPropertiesData(parent);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> getReferencesData(String identifier, boolean skipVersionStorage)
+      throws RepositoryException
+   {
+      return persistentManager.getReferencesData(identifier, skipVersionStorage);
+   }
+
+   // ------------ SharedDataManager ----------------------
+
+   /**
+    * {@inheritDoc}
+    */
+   public void save(ItemStateChangesLog changes) throws InvalidItemStateException, UnsupportedOperationException,
+      RepositoryException
+   {
+      persistentManager.save(changes);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public Calendar getCurrentTime()
+   {
+      return persistentManager.getCurrentTime();
+   }
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -53,12 +53,12 @@
  * @version $Id$ 
  */
 
-public class VersionableWorkspaceDataManager extends ACLInheritanceSupportedWorkspaceDataManager
+public class VersionableWorkspaceDataManager extends ShareableSupportedWorkspaceDataManager
 {
 
    private static Log log = ExoLogger.getLogger("exo.jcr.component.core.VersionableWorkspaceDataManager");
 
-   private ACLInheritanceSupportedWorkspaceDataManager versionDataManager;
+   private ShareableSupportedWorkspaceDataManager versionDataManager;
 
    public VersionableWorkspaceDataManager(CacheableWorkspaceDataManager persistentManager)
    {
@@ -71,7 +71,7 @@
    public void setSystemDataManager(DataManager systemDataManager)
    {
 
-      this.versionDataManager = (ACLInheritanceSupportedWorkspaceDataManager)systemDataManager;
+      this.versionDataManager = (ShareableSupportedWorkspaceDataManager)systemDataManager;
    }
 
    /**

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -28,6 +28,7 @@
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
 import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
 import org.exoplatform.services.jcr.datamodel.IllegalPathException;
 import org.exoplatform.services.jcr.datamodel.InternalQName;
 import org.exoplatform.services.jcr.datamodel.ItemData;
@@ -53,6 +54,11 @@
 import org.exoplatform.services.transaction.ActionNonTxAware;
 import org.infinispan.Cache;
 import org.infinispan.lifecycle.ComponentStatus;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
 
 import java.io.File;
 import java.io.IOException;
@@ -66,6 +72,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -99,6 +106,11 @@
    private final boolean enabled;
    
    protected final BufferedISPNCache cache;
+   
+   /**
+    * The list of all the listeners
+    */
+   private final List<WorkspaceStorageCacheListener> listeners = new CopyOnWriteArrayList<WorkspaceStorageCacheListener>();
 
    private final CacheActionNonTxAware<Void, Void> commitTransaction = new CacheActionNonTxAware<Void, Void>()
    {
@@ -462,6 +474,7 @@
          // do n't nothing
       }
       this.cache = new BufferedISPNCache(parentCache, allowLocalChanges);
+      cache.addListener(new CacheEventListener());
    }
 
    /**
@@ -1560,6 +1573,68 @@
    }
 
    /**
+    * {@inheritDoc}
+    */
+   public void addListener(WorkspaceStorageCacheListener listener)
+   {
+      listeners.add(listener);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void removeListener(WorkspaceStorageCacheListener listener)
+   {
+      listeners.remove(listener);
+   }
+   
+   /**
+    * Called when a cache entry corresponding to the given node has item updated
+    * @param data the item corresponding to the updated cache entry
+    */
+   private void onCacheEntryUpdated(ItemData data)
+   {
+      if (data == null || data instanceof NullItemData)
+      {
+         return;
+      }      
+      for (WorkspaceStorageCacheListener listener : listeners)
+      {
+         try
+         {
+            listener.onCacheEntryUpdated(data);
+         }
+         catch (Exception e)
+         {
+            LOG.warn("The method onCacheEntryUpdated fails for the listener " + listener.getClass(), e);
+         }
+      }      
+   }
+   
+   /**
+    * Called when a cache entry corresponding to the given item has been removed
+    * @param data the item corresponding to the removed cache entry
+    */
+   private void onCacheEntryRemoved(ItemData data)
+   {
+      if (data == null || data instanceof NullItemData)
+      {
+         return;
+      }      
+      for (WorkspaceStorageCacheListener listener : listeners)
+      {
+         try
+         {
+            listener.onCacheEntryRemoved(data);
+         }
+         catch (Exception e)
+         {
+            LOG.warn("The method onCacheEntryRemoved fails for the listener " + listener.getClass(), e);
+         }
+      }      
+   }
+   
+   /**
     * Actions that are not supposed to be called within a transaction
     * 
     * Created by The eXo Platform SAS
@@ -1577,4 +1652,30 @@
          return ISPNCacheWorkspaceStorageCache.this.getTransactionManager();
       }
    }
+   
+   @SuppressWarnings("rawtypes")
+   @Listener
+   public class CacheEventListener
+   {
+
+      @CacheEntryRemoved
+      public void cacheEntryRemoved(CacheEntryRemovedEvent evt)
+      {
+         if (evt.isPre() && evt.getKey() instanceof CacheId)
+         {
+            final ItemData value = (ItemData)evt.getValue();
+            onCacheEntryRemoved(value);
+         }
+      }
+
+      @CacheEntryModified
+      public void cacheEntryModified(CacheEntryModifiedEvent evt)
+      {
+         if (!evt.isPre() && evt.getKey() instanceof CacheId)
+         {
+            final ItemData value = (ItemData)evt.getValue();
+            onCacheEntryUpdated(value);
+         }
+      }
+   }   
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -29,6 +29,7 @@
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
 import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
 import org.exoplatform.services.jcr.datamodel.IllegalPathException;
 import org.exoplatform.services.jcr.datamodel.InternalQName;
 import org.exoplatform.services.jcr.datamodel.ItemData;
@@ -62,6 +63,10 @@
 import org.jboss.cache.config.EvictionRegionConfig;
 import org.jboss.cache.eviction.ExpirationAlgorithmConfig;
 import org.jboss.cache.jmx.JmxRegistrationManager;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
 import org.picocontainer.Startable;
 
 import java.io.File;
@@ -78,6 +83,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -186,7 +192,12 @@
    protected final Fqn<String> childNodesByPatternList;
 
    protected final Fqn<String> rootFqn;
-
+   
+   /**
+    * The list of all the listeners
+    */
+   private final List<WorkspaceStorageCacheListener> listeners = new CopyOnWriteArrayList<WorkspaceStorageCacheListener>();
+   
    private final CacheActionNonTxAware<Void, Void> commitTransaction = new CacheActionNonTxAware<Void, Void>()
    {
       @Override
@@ -680,7 +691,8 @@
       createResidentNode(childPropsByPatternList);
       createResidentNode(childNodesByPatternList);
       createResidentNode(itemsRoot);
-
+      this.cache.addCacheListener(new CacheEventListener());
+      
       if (jmxManager != null)
       {
          SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
@@ -2079,8 +2091,69 @@
          createResidentNode(itemsRoot);
       }
    }
+   /**
+    * {@inheritDoc}
+    */
+   public void addListener(WorkspaceStorageCacheListener listener)
+   {
+      listeners.add(listener);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void removeListener(WorkspaceStorageCacheListener listener)
+   {
+      listeners.remove(listener);
+   }
    
    /**
+    * Called when a cache entry corresponding to the given node has item updated
+    * @param data the item corresponding to the updated cache entry
+    */
+   private void onCacheEntryUpdated(ItemData data)
+   {
+      if (data == null || data instanceof NullItemData)
+      {
+         return;
+      }
+      for (WorkspaceStorageCacheListener listener : listeners)
+      {
+         try
+         {
+            listener.onCacheEntryUpdated(data);
+         }
+         catch (Exception e)
+         {
+            LOG.warn("The method onCacheEntryUpdated fails for the listener " + listener.getClass(), e);
+         }
+      }      
+   }
+   
+   /**
+    * Called when a cache entry corresponding to the given item has been removed
+    * @param data the item corresponding to the removed cache entry
+    */
+   private void onCacheEntryRemoved(ItemData data)
+   {
+      if (data == null || data instanceof NullItemData)
+      {
+         return;
+      }
+      for (WorkspaceStorageCacheListener listener : listeners)
+      {
+         try
+         {
+            listener.onCacheEntryRemoved(data);
+         }
+         catch (Exception e)
+         {
+            LOG.warn("The method onCacheEntryRemoved fails for the listener " + listener.getClass(), e);
+         }
+      }      
+   }
+   
+   /**
     * Actions that are not supposed to be called within a transaction
     * 
     * Created by The eXo Platform SAS
@@ -2098,4 +2171,30 @@
          return JBossCacheWorkspaceStorageCache.this.getTransactionManager();
       }
    }
+   
+   @org.jboss.cache.notifications.annotation.CacheListener
+   @SuppressWarnings("unchecked")
+   public class CacheEventListener
+   {
+
+      @NodeRemoved
+      public void nodeRemoved(NodeRemovedEvent ne)
+      {
+         if (ne.isPre() && ne.getFqn().isChildOf(itemsRoot))
+         {
+            final Map<Serializable, Object> data = ne.getData();
+            onCacheEntryRemoved((ItemData)(data == null ? null : data.get(ITEM_DATA)));
+         }
+      }
+
+      @NodeModified
+      public void nodeModified(NodeModifiedEvent ne)
+      {
+         if (!ne.isPre() && ne.getFqn().isChildOf(itemsRoot))
+         {
+            final Map<Serializable, Object> data = ne.getData();
+            onCacheEntryUpdated((ItemData)(data == null ? null : data.get(ITEM_DATA)));
+         }
+      }
+   }
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -26,6 +26,7 @@
 import org.exoplatform.services.jcr.datamodel.QPathEntry;
 import org.exoplatform.services.jcr.datamodel.ValueData;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
 import org.exoplatform.services.log.ExoLogger;
 import org.exoplatform.services.log.Log;
@@ -289,6 +290,15 @@
       throw new UnsupportedOperationException();
 
    }
+   
+   /**
+    * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+    */
+   public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+      UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException();
+   }
 
    class MapKey
    {

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -37,6 +37,7 @@
 import org.exoplatform.services.jcr.datamodel.ValueData;
 import org.exoplatform.services.jcr.impl.Constants;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.ByteArrayPersistedValueData;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.CleanableFilePersistedValueData;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.StreamPersistedValueData;
@@ -1228,6 +1229,15 @@
          throw new RepositoryException(e);
       }
    }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+      UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException("This method is not supported by the old JDBCWorkspaceDataContainer, use CQJDBCWorkspaceDataContainer instead.");
+   }   
 
    /**
     * {@inheritDoc}
@@ -1500,7 +1510,7 @@
             }
 
             QPathEntry qpe =
-               new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)), parent.getInt(COLUMN_INDEX));
+               new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)), parent.getInt(COLUMN_INDEX), caid);
             qrpath.add(qpe);
             caid = parent.getString(COLUMN_PARENTID);
          }
@@ -2213,7 +2223,7 @@
          if (parentPath != null)
          {
             // get by parent and name
-            qpath = QPath.makeChildPath(parentPath, qname, cindex);
+            qpath = QPath.makeChildPath(parentPath, qname, cindex, cid);
             parentCid = cpid;
          }
          else
@@ -2227,7 +2237,7 @@
             }
             else
             {
-               qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex);
+               qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex, cid);
                parentCid = cpid;
             }
          }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -20,6 +20,7 @@
 
 import org.exoplatform.services.jcr.access.AccessControlEntry;
 import org.exoplatform.services.jcr.access.AccessControlList;
+import org.exoplatform.services.jcr.core.ExtendedPropertyType;
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
 import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
@@ -33,6 +34,7 @@
 import org.exoplatform.services.jcr.datamodel.ValueData;
 import org.exoplatform.services.jcr.impl.Constants;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.StreamPersistedValueData;
 import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
 import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
@@ -133,6 +135,13 @@
     */
    protected String UPDATE_VALUE;
 
+   /**
+    * FIND_ACL_HOLDERS.
+    */
+   protected String FIND_ACL_HOLDERS;
+
+   protected PreparedStatement findACLHolders;
+
    protected PreparedStatement findNodesByParentIdCQ;
 
    protected PreparedStatement findPropertiesByParentIdCQ;
@@ -177,7 +186,64 @@
    {
       super(dbConnection, readOnly, containerName, valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
    }
+   
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+      UnsupportedOperationException
+   {
+      checkIfOpened();
+      ResultSet resultSet = null;
+      try
+      {
+         // query will return all the ACL holder
+         resultSet = findACLHolders();
+         Map<String, ACLHolder> mHolders = new HashMap<String, ACLHolder>();
+         
+         while (resultSet.next())
+         {
+            String cpid = resultSet.getString(COLUMN_PARENTID);
+            ACLHolder holder = mHolders.get(cpid);
+            if (holder == null)
+            {
+               holder = new ACLHolder(cpid);
+               mHolders.put(cpid, holder);
+            }
+            int cptype = resultSet.getInt(COLUMN_PTYPE);
 
+            if (cptype == ExtendedPropertyType.PERMISSION)
+            {
+               holder.setPermissions(true);
+            }
+            else
+            {
+               holder.setOwner(true);
+            }
+         }
+         return new ArrayList<ACLHolder>(mHolders.values());
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      finally
+      {
+         if (resultSet != null)
+         {
+            try
+            {
+               resultSet.close();
+            }
+            catch (SQLException e)
+            {
+               LOG.error("Can't close the ResultSet: " + e);
+            }
+         }
+      }
+   }   
+
    /**
     * {@inheritDoc}
     */
@@ -928,7 +994,7 @@
          if (parentPath != null)
          {
             // get by parent and name
-            qpath = QPath.makeChildPath(parentPath, qname, cindex);
+            qpath = QPath.makeChildPath(parentPath, qname, cindex, cid);
             parentCid = cpid;
          }
          else
@@ -942,7 +1008,7 @@
             }
             else
             {
-               qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex);
+               qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex, cid);
                parentCid = cpid;
             }
          }
@@ -1094,13 +1160,13 @@
             }
 
             QPathEntry qpe1 =
-               new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)), result.getInt(COLUMN_INDEX));
+               new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)), result.getInt(COLUMN_INDEX), result.getString(COLUMN_ID));
             boolean isChild = caid.equals(result.getString(COLUMN_ID));
             caid = result.getString(COLUMN_PARENTID);
             if (result.next())
             {
                QPathEntry qpe2 =
-                  new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)), result.getInt(COLUMN_INDEX));
+                  new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)), result.getInt(COLUMN_INDEX), result.getString(COLUMN_ID));
                if (isChild)
                {
                   // The child is the first result then we have the parent
@@ -1166,6 +1232,11 @@
 
       try
       {
+         if (findACLHolders != null)
+         {
+            findACLHolders.close();
+         }
+
          if (findNodesByParentIdCQ != null)
          {
             findNodesByParentIdCQ.close();
@@ -1222,6 +1293,8 @@
       }
    }
 
+   protected abstract ResultSet findACLHolders() throws SQLException;
+
    protected abstract ResultSet findItemQPathByIdentifierCQ(String identifier) throws SQLException;
 
    protected abstract ResultSet findChildNodesByParentIdentifierCQ(String parentIdentifier) throws SQLException;

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -217,6 +217,10 @@
             + " P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or"
             + " P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
             + " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID";
+      
+      FIND_ACL_HOLDERS = "select I.PARENT_ID, I.P_TYPE" +
+      		" from JCR_MITEM I" +
+      		" where I.I_CLASS=2 and (I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')";
    }
 
    /**
@@ -1010,4 +1014,18 @@
    {
       return PATTERN_ESCAPE_STRING;
    }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findACLHolders() throws SQLException
+   {
+      if (findACLHolders == null)
+      {
+         findACLHolders = dbConnection.prepareStatement(FIND_ACL_HOLDERS);
+      }
+
+      return findACLHolders.executeQuery();
+   }
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -222,6 +222,10 @@
             + " P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or"
             + " P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
             + " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID";
+      
+      FIND_ACL_HOLDERS = "select I.PARENT_ID, I.P_TYPE" +
+            " from JCR_SITEM I" +
+            " where I.I_CLASS=2 and I.CONTAINER_NAME=? and (I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')";      
    }
 
    /**
@@ -923,4 +927,24 @@
    {
       return PATTERN_ESCAPE_STRING;
    }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findACLHolders() throws SQLException
+   {
+      if (findACLHolders == null)
+      {
+         findACLHolders = dbConnection.prepareStatement(FIND_ACL_HOLDERS);
+      }
+      else
+      {
+         findACLHolders.clearParameters();
+      }
+
+      findACLHolders.setString(1, containerName);
+
+      return findACLHolders.executeQuery();
+   }   
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -22,6 +22,7 @@
 import org.exoplatform.services.jcr.datamodel.PropertyData;
 import org.exoplatform.services.jcr.datamodel.QPathEntry;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
 import org.exoplatform.services.jcr.statistics.JCRStatisticsManager;
 import org.exoplatform.services.jcr.statistics.Statistics;
@@ -128,6 +129,12 @@
     * <code>getLastOrderNumber(NodeData parent)</code>
     */
    private static final String GET_LAST_ORDER_NUMBER_DESCR = "getLastOrderNumber";
+   
+   /**
+    * The description of the statistics corresponding to the method 
+    * <code>getACLHolders()</code>
+    */
+   private static final String GET_ACL_HOLDERS = "getACLHolders";
 
    /**
     * The description of the statistics corresponding to the method 
@@ -192,6 +199,7 @@
       ALL_STATISTICS.put(LIST_CHILD_PROPERTIES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS,
          LIST_CHILD_PROPERTIES_DATA_DESCR));
       ALL_STATISTICS.put(GET_REFERENCES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, GET_REFERENCES_DATA_DESCR));
+      ALL_STATISTICS.put(GET_ACL_HOLDERS, new Statistics(GLOBAL_STATISTICS, GET_ACL_HOLDERS));
       // Write Methods
       // Commit
       ALL_STATISTICS.put(COMMIT_DESCR, new Statistics(GLOBAL_STATISTICS, COMMIT_DESCR));
@@ -617,4 +625,23 @@
          s.end();
       }
    }
+   
+   
+   /**
+    * {@inheritDoc}
+    */
+   public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+      UnsupportedOperationException
+   {
+      Statistics s = ALL_STATISTICS.get(GET_ACL_HOLDERS);
+      try
+      {
+         s.begin();
+         return wcs.getACLHolders();
+      }
+      finally
+      {
+         s.end();
+      }
+   }
 }

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -24,6 +24,7 @@
 import org.exoplatform.services.jcr.datamodel.PropertyData;
 import org.exoplatform.services.jcr.datamodel.QPathEntry;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 
 import java.util.List;
 
@@ -387,4 +388,17 @@
     * @return boolean, true if connection is open and ready, false - otherwise
     */
    boolean isOpened();
+
+   /**
+    * Returns all the nodes that hold some ACL info like owner or permissions
+    * 
+    * @return a list of all the ACL holders for this workspace
+    * @throws RepositoryException
+    *           if some exception occured
+    * @throws IllegalStateException
+    *           if connection is closed
+    * @throws UnsupportedOperationException
+    *           if operation is not supported
+    */
+   List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException, UnsupportedOperationException;
 }

Modified: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -22,6 +22,7 @@
 import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
 import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
 import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
 import org.exoplatform.services.jcr.datamodel.ItemData;
 import org.exoplatform.services.jcr.datamodel.ItemType;
 import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -439,6 +440,20 @@
       {
       }
 
+      /**
+       * @see org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache#addListener(org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener)
+       */
+      public void addListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException
+      {
+      }
+
+      /**
+       * @see org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache#removeListener(org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener)
+       */
+      public void removeListener(WorkspaceStorageCacheListener listener) throws UnsupportedOperationException
+      {
+      }
+
    }
 
    private static class MyWorkspaceStorageConnection implements WorkspaceStorageConnection
@@ -599,6 +614,15 @@
          return null;
       }
 
+      /**
+       * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+       */
+      public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+         UnsupportedOperationException
+      {
+         return null;
+      }
+
    };
 
    private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase

Modified: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -949,8 +949,15 @@
          
          return children;
       }
-      
-      
+
+      /**
+       * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+       */
+      public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+         UnsupportedOperationException
+      {
+         return null;
+      }
    };
    
    private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase

Modified: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java	2011-08-17 14:16:40 UTC (rev 4769)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java	2011-08-18 06:39:44 UTC (rev 4770)
@@ -34,6 +34,7 @@
 import org.exoplatform.services.jcr.datamodel.QPathEntry;
 import org.exoplatform.services.jcr.impl.Constants;
 import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspaceStorageCacheBaseCase;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache;
@@ -315,6 +316,17 @@
          return getChildNodesData(parent);
       }
 
+
+      /**
+       * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+       */
+      @Override
+      public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+         UnsupportedOperationException
+      {
+         return null;
+      }
+
    };
    
    private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase



More information about the exo-jcr-commits mailing list