[exo-jcr-commits] exo-jcr SVN: r4832 - in jcr/trunk: exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent and 14 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Fri Sep 2 06:32:09 EDT 2011


Author: sergiykarpenko
Date: 2011-09-02 06:32:08 -0400 (Fri, 02 Sep 2011)
New Revision: 4832

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
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorksapceDataManagerBloomFilter.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/config/MappedParametrizedObjectEntry.java
   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/BufferedISPNCache.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/BufferedJBossCache.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/WorkspaceDataContainer.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/BaseStandaloneTest.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
   jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/exo-jcr-configuration.xml
   jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/workspace-persistence-storage.xml
Log:
EXOJCR-1412: use bloom filters to store ids of nodes with ACL (modified patch from EXOJCR-1456)


Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/config/MappedParametrizedObjectEntry.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/config/MappedParametrizedObjectEntry.java	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/config/MappedParametrizedObjectEntry.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -227,6 +227,55 @@
    }
 
    /**
+    * Parse named parameter as Double.
+    * 
+    * @param name
+    *          parameter name
+    * @param defaultValue
+    *          default Double value
+    * @return Double value
+    */
+   public Double getParameterDouble(String name, Double defaultValue)
+   {
+      for (int i = 0; i < parameters.size(); i++)
+      {
+         SimpleParameterEntry p = parameters.get(i);
+         if (p.getName().equals(name))
+         {
+            try
+            {
+               return StringNumberParser.parseDouble(p.getValue());
+            }
+            catch (NumberFormatException e)
+            {
+               //LOG.warn(name + ": unparseable Long. " + e);
+            }
+         }
+      }
+      return defaultValue;
+   }
+
+   /**
+    * Parse named parameter as Double.
+    * 
+    * @param name
+    *          parameter name
+    * @return Double value
+    * @throws RepositoryConfigurationException
+    */
+   public Double getParameterDouble(String name) throws RepositoryConfigurationException
+   {
+      try
+      {
+         return StringNumberParser.parseDouble(getParameterValue(name));
+      }
+      catch (NumberFormatException e)
+      {
+         throw new RepositoryConfigurationException(name + ": unparseable Long. " + e, e);
+      }
+   }
+
+   /**
     * Parse named parameter using {@link StringNumberParser.parseTime} and return time in
     * milliseconds (Long value).
     * 

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 10:32:08 UTC (rev 4832)
@@ -0,0 +1,43 @@
+/*
+ * 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);
+}

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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;
    }
 
    /**

Copied: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java (from rev 4787, 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-09-02 10:32:08 UTC (rev 4832)
@@ -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;
+   }
+}

Copied: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java (from rev 4787, 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-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -19,10 +19,15 @@
 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.config.WorkspaceEntry;
 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;
@@ -49,6 +54,7 @@
 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,9 +87,13 @@
  * @version $Id$
  */
 public class CacheableWorkspaceDataManager extends WorkspacePersistentDataManager implements Suspendable,
-   TopologyChangeListener
+   TopologyChangeListener, Startable, WorkspaceStorageCacheListener
 {
 
+   private final static double ACL_BF_FALSE_PROPBABILITY_DEFAULT = 0.1d;
+
+   private final static int ACL_BF_ELEMENTS_NUMBER_DEFAULT = 1000000;
+
    /**
     * Items cache.
     */
@@ -98,6 +109,19 @@
     */
    private final TransactionableResourceManager txResourceManager;
 
+   private final AtomicBoolean filtersEnabled = new AtomicBoolean();
+
+   /**
+    * Bloom filter parameters.
+    */
+   private final double bfProbability;
+
+   private final int bfElementNumber;
+
+   private volatile BloomFilter<String> filterPermissions;
+
+   private volatile BloomFilter<String> filterOwner;
+
    private TransactionManager transactionManager;
 
    /**
@@ -348,6 +372,8 @@
    /**
     * CacheableWorkspaceDataManager constructor.
     * 
+    * @param wsConfig 
+    *          WorkspaceEntry used to fetch bloom filter parameters
     * @param dataContainer
     *          Workspace data container (persistent level)
     * @param cache
@@ -361,11 +387,30 @@
     * @param rpcService
     *          the service for executing commands on all nodes of cluster
     */
-   public CacheableWorkspaceDataManager(WorkspaceDataContainer dataContainer, WorkspaceStorageCache cache,
-      SystemDataContainerHolder systemDataContainerHolder, TransactionableResourceManager txResourceManager,
-      TransactionService transactionService, RPCService rpcService)
+   public CacheableWorkspaceDataManager(WorkspaceEntry wsConfig, WorkspaceDataContainer dataContainer,
+      WorkspaceStorageCache cache, SystemDataContainerHolder systemDataContainerHolder,
+      TransactionableResourceManager txResourceManager, TransactionService transactionService, RPCService rpcService)
    {
       super(dataContainer, systemDataContainerHolder, txResourceManager);
+
+      bfProbability =
+         wsConfig.getContainer().getParameterDouble(WorkspaceDataContainer.ACL_BF_FALSE_PROPBABILITY,
+            ACL_BF_FALSE_PROPBABILITY_DEFAULT);
+      if (bfProbability < 0 || bfProbability > 1)
+      {
+         throw new IllegalArgumentException("Parameter " + WorkspaceDataContainer.ACL_BF_FALSE_PROPBABILITY
+            + " is invalid, must be between 0 and 1.");
+      }
+
+      bfElementNumber =
+         wsConfig.getContainer().getParameterInteger(WorkspaceDataContainer.ACL_BF_ELEMENTS_NUMBER,
+            ACL_BF_ELEMENTS_NUMBER_DEFAULT);
+      if (bfElementNumber <= 0)
+      {
+         throw new IllegalArgumentException("Parameter " + WorkspaceDataContainer.ACL_BF_ELEMENTS_NUMBER
+            + " is invalid, can not be less then 1.");
+      }
+
       this.cache = cache;
 
       this.requestCache = new ConcurrentHashMap<Integer, DataRequest>();
@@ -381,6 +426,8 @@
    /**
     * CacheableWorkspaceDataManager constructor.
     * 
+    * @param wsConfig 
+    *          WorkspaceEntry used to fetch bloom filter parameters
     * @param dataContainer
     *          Workspace data container (persistent level)
     * @param cache
@@ -391,16 +438,18 @@
     *          the resource manager used to manage the whole tx
     * @param transactionService TransactionService         
     */
-   public CacheableWorkspaceDataManager(WorkspaceDataContainer dataContainer, WorkspaceStorageCache cache,
-      SystemDataContainerHolder systemDataContainerHolder, TransactionableResourceManager txResourceManager,
-      TransactionService transactionService)
+   public CacheableWorkspaceDataManager(WorkspaceEntry wsConfig, WorkspaceDataContainer dataContainer,
+      WorkspaceStorageCache cache, SystemDataContainerHolder systemDataContainerHolder,
+      TransactionableResourceManager txResourceManager, TransactionService transactionService)
    {
-      this(dataContainer, cache, systemDataContainerHolder, txResourceManager, transactionService, null);
+      this(wsConfig, dataContainer, cache, systemDataContainerHolder, txResourceManager, transactionService, null);
    }
 
    /**
     * CacheableWorkspaceDataManager constructor.
     * 
+    * @param wsConfig 
+    *          WorkspaceEntry used to fetch bloom filter parameters
     * @param dataContainer
     *          Workspace data container (persistent level)
     * @param cache
@@ -410,11 +459,30 @@
     * @param txResourceManager
     *          the resource manager used to manage the whole tx
     */
-   public CacheableWorkspaceDataManager(WorkspaceDataContainer dataContainer, WorkspaceStorageCache cache,
-      SystemDataContainerHolder systemDataContainerHolder, TransactionableResourceManager txResourceManager,
-      RPCService rpcService)
+   public CacheableWorkspaceDataManager(WorkspaceEntry wsConfig, WorkspaceDataContainer dataContainer,
+      WorkspaceStorageCache cache, SystemDataContainerHolder systemDataContainerHolder,
+      TransactionableResourceManager txResourceManager, RPCService rpcService)
    {
       super(dataContainer, systemDataContainerHolder, txResourceManager);
+
+      bfProbability =
+         wsConfig.getContainer().getParameterDouble(WorkspaceDataContainer.ACL_BF_FALSE_PROPBABILITY,
+            ACL_BF_FALSE_PROPBABILITY_DEFAULT);
+      if (bfProbability < 0 || bfProbability > 1)
+      {
+         throw new IllegalArgumentException("Parameter " + WorkspaceDataContainer.ACL_BF_FALSE_PROPBABILITY
+            + " is invalid, must be between 0 and 1.");
+      }
+
+      bfElementNumber =
+         wsConfig.getContainer().getParameterInteger(WorkspaceDataContainer.ACL_BF_ELEMENTS_NUMBER,
+            ACL_BF_ELEMENTS_NUMBER_DEFAULT);
+      if (bfElementNumber <= 0)
+      {
+         throw new IllegalArgumentException("Parameter " + WorkspaceDataContainer.ACL_BF_ELEMENTS_NUMBER
+            + " is invalid, can not be less then 1.");
+      }
+
       this.cache = cache;
 
       this.requestCache = new ConcurrentHashMap<Integer, DataRequest>();
@@ -439,6 +507,8 @@
    /**
     * CacheableWorkspaceDataManager constructor.
     * 
+    * @param wsConfig 
+    *          WorkspaceEntry used to fetch bloom filter parameters
     * @param dataContainer
     *          Workspace data container (persistent level)
     * @param cache
@@ -448,15 +518,18 @@
     * @param txResourceManager
     *          the resource manager used to manage the whole tx
     */
-   public CacheableWorkspaceDataManager(WorkspaceDataContainer dataContainer, WorkspaceStorageCache cache,
-      SystemDataContainerHolder systemDataContainerHolder, TransactionableResourceManager txResourceManager)
+   public CacheableWorkspaceDataManager(WorkspaceEntry wsConfig, WorkspaceDataContainer dataContainer,
+      WorkspaceStorageCache cache, SystemDataContainerHolder systemDataContainerHolder,
+      TransactionableResourceManager txResourceManager)
    {
-      this(dataContainer, cache, systemDataContainerHolder, txResourceManager, (RPCService)null);
+      this(wsConfig, dataContainer, cache, systemDataContainerHolder, txResourceManager, (RPCService)null);
    }
 
    /**
     * CacheableWorkspaceDataManager constructor.
     * 
+    * @param wsConfig 
+    *          WorkspaceEntry used to fetch bloom filter parameters
     * @param dataContainer
     *          Workspace data container (persistent level)
     * @param cache
@@ -464,10 +537,10 @@
     * @param systemDataContainerHolder
     *          System Workspace data container (persistent level)
     */
-   public CacheableWorkspaceDataManager(WorkspaceDataContainer dataContainer, WorkspaceStorageCache cache,
-      SystemDataContainerHolder systemDataContainerHolder)
+   public CacheableWorkspaceDataManager(WorkspaceEntry wsConfig, WorkspaceDataContainer dataContainer,
+      WorkspaceStorageCache cache, SystemDataContainerHolder systemDataContainerHolder)
    {
-      this(dataContainer, cache, systemDataContainerHolder, null, (RPCService)null);
+      this(wsConfig, dataContainer, cache, systemDataContainerHolder, null, (RPCService)null);
    }
 
    /**
@@ -712,8 +785,22 @@
     * {@inheritDoc}
     */
    @Override
-   public ItemData getItemData(final String identifier) throws RepositoryException
+   public ItemData getItemData(String identifier) throws RepositoryException
    {
+      return getItemData(identifier, true);
+   }
+
+   /**
+    * Do the same thing as getItemData(identifier), but ACL initialization can be specified.
+    * If doInitACL is true (default value for getItemData(identifier)) ACL will be initialized.
+    * 
+    * @param identifier
+    * @param doInitACL
+    * @return
+    * @throws RepositoryException
+    */
+   private ItemData getItemData(final String identifier, final boolean doInitACL) throws RepositoryException
+   {
       if (cache.isEnabled())
       {
          // 1. Try from cache
@@ -766,7 +853,14 @@
             public ItemData run() throws RepositoryException
             {
                ItemData item = CacheableWorkspaceDataManager.super.getItemData(identifier);
-               return item != null && item.isNode() ? initACL(null, (NodeData)item) : item;
+               if (item != null && item.isNode() && doInitACL)
+               {
+                  return initACL(null, (NodeData)item);
+               }
+               else
+               {
+                  return item;
+               }
             }
          });
       }
@@ -2024,16 +2118,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();
@@ -2043,23 +2151,43 @@
             {
                // use parent ACL
                node =
-                  new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(), node
-                     .getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
+                  new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(),
+                     node.getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
                      node.getParentIdentifier(), parent.getACL());
             }
             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));
+                  new TransientNodeData(node.getQPath(), node.getIdentifier(), node.getPersistedVersion(),
+                     node.getPrimaryTypeName(), node.getMixinTypeNames(), node.getOrderNumber(),
+                     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 +2196,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 +2232,281 @@
     * 
     * @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, false);
+      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);
+      this.filterPermissions = new BloomFilter<String>(bfProbability, bfElementNumber);
+      this.filterOwner = new BloomFilter<String>(bfProbability, bfElementNumber);
+      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());
+         }
+      }
+   }
+
+   /**
+    * 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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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/BufferedISPNCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/BufferedISPNCache.java	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/BufferedISPNCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -404,8 +404,6 @@
       {
          // force writeLock on next read
          cache.withFlags(Flag.FORCE_WRITE_LOCK);
-
-         setCacheLocalMode();
          Object existingObject = cache.get(key);
 
          // if found value is really set! add to it.

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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,9 @@
 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.event.CacheEntryModifiedEvent;
 
 import java.io.File;
 import java.io.IOException;
@@ -66,6 +70,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 +104,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 +472,7 @@
          // do n't nothing
       }
       this.cache = new BufferedISPNCache(parentCache, allowLocalChanges);
+      cache.addListener(new CacheEventListener());
    }
 
    /**
@@ -1560,6 +1571,45 @@
    }
 
    /**
+    * {@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);
+         }
+      }      
+   }
+   
+   /**
     * Actions that are not supposed to be called within a transaction
     * 
     * Created by The eXo Platform SAS
@@ -1577,4 +1627,20 @@
          return ISPNCacheWorkspaceStorageCache.this.getTransactionManager();
       }
    }
+   
+   @SuppressWarnings("rawtypes")
+   @Listener
+   public class CacheEventListener
+   {
+
+      @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/BufferedJBossCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -546,11 +546,30 @@
    {
       CompressedChangesBuffer changesContainer = getChangesBufferSafe();
       changesContainer.add(new PutKeyValueContainer(fqn, key, value, parentCache, changesContainer.getHistoryIndex(),
-         local.get(), useExpiration, expirationTimeOut));
+         local.get(), useExpiration, expirationTimeOut, false));
 
       return parentCache.get(fqn, key);
    }
+   
+   protected Object putWithNotification(Fqn fqn, Serializable key, Object value)
+   {
+      CompressedChangesBuffer changesContainer = getChangesBufferSafe();
+      changesContainer.add(new PutKeyValueContainer(fqn, key, value, parentCache, changesContainer.getHistoryIndex(),
+         local.get(), useExpiration, expirationTimeOut, true));
 
+      return parentCache.get(fqn, key);
+   }
+
+   protected Object putWithNotification(Fqn fqn, Serializable key, Object value, boolean putIfAbsent)
+   {
+      if (putIfAbsent)
+      {
+         putIfAbsent(fqn, key, value, true);
+         return null;
+      }
+      return putWithNotification(fqn, key, value);
+   }
+
    /**
     * in case putIfAbsent is set to <code>true</code> this method will 
     * call cache.putIfAbsent(Fqn fqn, Serializable key, Object value)
@@ -571,13 +590,18 @@
     */
    protected Object putIfAbsent(Fqn fqn, Serializable key, Object value)
    {
+      return putIfAbsent(fqn, key, value, false);
+   }
+   
+   protected Object putIfAbsent(Fqn fqn, Serializable key, Object value, boolean eventNotification)
+   {
       CompressedChangesBuffer changesContainer = getChangesBufferSafe();
       changesContainer.add(new PutIfAbsentKeyValueContainer(fqn, key, value, parentCache, changesContainer.getHistoryIndex(),
-         local.get(), useExpiration, expirationTimeOut));
+         local.get(), useExpiration, expirationTimeOut, eventNotification));
       return null;
    }
    
-   public Object putInBuffer(Fqn fqn, Serializable key, Object value)
+   public Object putInBuffer(Fqn fqn, Serializable key, Object value, boolean eventNotification)
    {
       CompressedChangesBuffer changesContainer = getChangesBufferSafe();
 
@@ -585,7 +609,7 @@
       Object prevObject = getObjectFromChangesContainer(changesContainer, fqn, key);
 
       changesContainer.add(new PutKeyValueContainer(fqn, key, value, parentCache, changesContainer.getHistoryIndex(),
-         local.get(), useExpiration, expirationTimeOut));
+         local.get(), useExpiration, expirationTimeOut, eventNotification));
 
       if (prevObject != null)
       {
@@ -691,9 +715,17 @@
    {
       CompressedChangesBuffer changesContainer = getChangesBufferSafe();
       changesContainer.add(new RemoveNodeContainer(fqn, parentCache, changesContainer.getHistoryIndex(), local.get(),
-         useExpiration, expirationTimeOut));
+         useExpiration, expirationTimeOut, false));
       return true;
    }
+   
+   protected boolean removeNode(Fqn fqn, boolean eventNotification)
+   {
+      CompressedChangesBuffer changesContainer = getChangesBufferSafe();
+      changesContainer.add(new RemoveNodeContainer(fqn, parentCache, changesContainer.getHistoryIndex(), local.get(),
+         useExpiration, expirationTimeOut, eventNotification));
+      return true;
+   }
 
    /* (non-Javadoc)
     * @see org.jboss.cache.Cache#removeNode(java.lang.String)
@@ -829,9 +861,11 @@
       protected final boolean useExpiration;
 
       protected final long timeOut;
+      
+      protected final boolean eventNotification;
 
       public ChangesContainer(Fqn fqn, ChangesType changesType, Cache<Serializable, Object> cache, int historicalIndex,
-         boolean localMode, boolean useExpiration, long timeOut)
+         boolean localMode, boolean useExpiration, long timeOut, boolean eventNotification)
       {
          super();
          this.fqn = fqn;
@@ -841,6 +875,7 @@
          this.localMode = localMode;
          this.useExpiration = useExpiration;
          this.timeOut = timeOut;
+         this.eventNotification = eventNotification;
       }
 
       /**
@@ -882,14 +917,16 @@
          return result == 0 ? historicalIndex - o.getHistoricalIndex() : result;
       }
 
-      protected void setCacheLocalMode()
+      protected void setOptionOverrides()
       {
          cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(localMode);
+         cache.getInvocationContext().getOptionOverrides().setSuppressEventNotification(!eventNotification);
       }
 
       public final void putExpiration(Fqn efqn)
       {
-         setCacheLocalMode();
+         cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(localMode);
+         cache.getInvocationContext().getOptionOverrides().setSuppressEventNotification(true);
          cache.put(efqn, ExpirationAlgorithmConfig.EXPIRATION_KEY, new Long(System.currentTimeMillis() + timeOut));
       }
 
@@ -911,7 +948,7 @@
       public PutObjectContainer(Fqn fqn, Map<? extends Serializable, ? extends Object> data,
          Cache<Serializable, Object> cache, int historicalIndex, boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.PUT, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.PUT, cache, historicalIndex, local, useExpiration, timeOut, false);
 
          this.data = data;
       }
@@ -919,13 +956,13 @@
       @Override
       public void apply()
       {
-         setCacheLocalMode();
-         cache.put(fqn, data);
-
          if (useExpiration)
          {
             putExpiration(fqn);
          }
+         
+         setOptionOverrides();
+         cache.put(fqn, data);
       }
    }
 
@@ -939,9 +976,9 @@
       private final Object value;
 
       public PutIfAbsentKeyValueContainer(Fqn fqn, Serializable key, Object value, Cache<Serializable, Object> cache,
-         int historicalIndex, boolean local, boolean useExpiration, long timeOut)
+         int historicalIndex, boolean local, boolean useExpiration, long timeOut, boolean eventNotification)
       {
-         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut, eventNotification);
          this.key = key;
          this.value = value;
       }
@@ -960,7 +997,7 @@
             putExpiration(fqn);
          }
 
-         setCacheLocalMode();
+         setOptionOverrides();
          cache.put(fqn, key, value);
       }
            
@@ -981,9 +1018,9 @@
       private final Object value;
 
       public PutKeyValueContainer(Fqn fqn, Serializable key, Object value, Cache<Serializable, Object> cache,
-         int historicalIndex, boolean local, boolean useExpiration, long timeOut)
+         int historicalIndex, boolean local, boolean useExpiration, long timeOut, boolean eventNotification)
       {
-         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut, eventNotification);
          this.key = key;
          this.value = value;
       }
@@ -996,7 +1033,7 @@
             putExpiration(fqn);
          }
 
-         setCacheLocalMode();
+         setOptionOverrides();
          cache.put(fqn, key, value);
       }
    }
@@ -1016,7 +1053,7 @@
       public AddToListContainer(Fqn fqn, Serializable key, Object value, Cache<Serializable, Object> cache,
          boolean forceModify, int historicalIndex, boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut, false);
          this.key = key;
          this.value = value;
          this.forceModify = forceModify;
@@ -1045,7 +1082,7 @@
                putExpiration(fqn);
             }
 
-            setCacheLocalMode();
+            setOptionOverrides();
             cache.put(fqn, key, newSet);
          }
          else if (existingObject != null)
@@ -1057,6 +1094,7 @@
          {
             // to prevent consistency issue since we don't have the list in the local cache, we are in cluster env
             // and we are in a non local mode, we clear the list in order to enforce other cluster nodes to reload it from the db
+            setOptionOverrides();
             cache.put(fqn, key, null);
          }
       }
@@ -1082,7 +1120,7 @@
       public AddToPatternListContainer(Fqn fqn, Serializable patternKey, Serializable listKey, ItemData value,
          Cache<Serializable, Object> cache, int historicalIndex, boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut, false);
          this.patternKey = patternKey;
          this.listKey = listKey;
          this.value = value;
@@ -1095,12 +1133,12 @@
          {
             // to prevent consistency issue since we don't have the list in the local cache, we are in cluster env
             // and we are in a non local mode, we remove all the patterns in order to enforce other cluster nodes to reload them from the db
+            setOptionOverrides();
             cache.removeNode(fqn);
             return;
          }
          // force writeLock on next read
          cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
-
          Iterator<Object> patternNames = cache.getChildrenNames(fqn).iterator();
          while (patternNames.hasNext())
          {
@@ -1126,7 +1164,14 @@
                   continue;
                }
                Set<String> newSet = new HashSet<String>((Set<String>)setObject);
-               newSet.add(value.getIdentifier());
+               newSet.add(value.getIdentifier());               
+
+               if (useExpiration)
+               {
+                  putExpiration(fqn);
+               }
+
+               setOptionOverrides();
                cache.put(patternFqn, listKey, newSet);
             }
          }
@@ -1151,7 +1196,7 @@
       public RemoveFromListContainer(Fqn fqn, Serializable key, Object value, Cache<Serializable, Object> cache,
          int historicalIndex, boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut, false);
          this.key = key;
          this.value = value;
       }
@@ -1161,8 +1206,6 @@
       {
          // force writeLock on next read
          cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
-         // object found by FQN and key;
-         setCacheLocalMode();
          Object existingObject = cache.get(getFqn(), key);
          // if found value is really set! add to it.
          if (existingObject instanceof Set)
@@ -1175,7 +1218,7 @@
                putExpiration(fqn);
             }
 
-            setCacheLocalMode();
+            setOptionOverrides();
             cache.put(fqn, key, newSet);
          }
       }
@@ -1201,7 +1244,7 @@
       public RemoveFromPatternListContainer(Fqn fqn, Serializable patternKey, Serializable listKey, ItemData value,
          Cache<Serializable, Object> cache, int historicalIndex, boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut, false);
          this.patternKey = patternKey;
          this.listKey = listKey;
          this.value = value;
@@ -1212,9 +1255,6 @@
       {
          // force writeLock on next read
          cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
-         // object found by FQN and key;
-         setCacheLocalMode();
-
          Iterator<Object> patternNames = cache.getChildrenNames(fqn).iterator();
          while (patternNames.hasNext())
          {
@@ -1240,6 +1280,13 @@
                }
                Set<String> newSet = new HashSet<String>((Set<String>)setObject);
                newSet.remove(value.getIdentifier());
+
+               if (useExpiration)
+               {
+                  putExpiration(fqn);
+               }
+
+               setOptionOverrides();
                cache.put(patternFqn, listKey, newSet);
             }
          }
@@ -1262,14 +1309,14 @@
       public RemoveKeyContainer(Fqn fqn, Serializable key, Cache<Serializable, Object> cache, int historicalIndex,
          boolean local, boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+         super(fqn, ChangesType.REMOVE_KEY, cache, historicalIndex, local, useExpiration, timeOut, false);
          this.key = key;
       }
 
       @Override
       public void apply()
       {
-         setCacheLocalMode();
+         setOptionOverrides();
          cache.remove(fqn, key);
       }
 
@@ -1284,13 +1331,19 @@
       public RemoveNodeContainer(Fqn fqn, Cache<Serializable, Object> cache, int historicalIndex, boolean local,
          boolean useExpiration, long timeOut)
       {
-         super(fqn, ChangesType.REMOVE, cache, historicalIndex, local, useExpiration, timeOut);
+         this(fqn, cache, historicalIndex, local, useExpiration, timeOut, false);
       }
 
+      public RemoveNodeContainer(Fqn fqn, Cache<Serializable, Object> cache, int historicalIndex, boolean local,
+         boolean useExpiration, long timeOut, boolean eventNotification)
+      {
+         super(fqn, ChangesType.REMOVE, cache, historicalIndex, local, useExpiration, timeOut, eventNotification);
+      }
+
       @Override
       public void apply()
       {
-         setCacheLocalMode();
+         setOptionOverrides();
          cache.removeNode(fqn);
       }
    }

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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,8 @@
 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.event.NodeModifiedEvent;
 import org.picocontainer.Startable;
 
 import java.io.File;
@@ -78,6 +81,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 +190,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 +689,8 @@
       createResidentNode(childPropsByPatternList);
       createResidentNode(childNodesByPatternList);
       createResidentNode(itemsRoot);
-
+      this.cache.addCacheListener(new CacheEventListener());
+      
       if (jmxManager != null)
       {
          SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
@@ -733,6 +743,7 @@
       if (!cacheRoot.hasChild(fqn))
       {
          cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+         cache.getInvocationContext().getOptionOverrides().setSuppressEventNotification(true);
          cacheRoot.addChild(fqn).setResident(true);
       }
       else
@@ -1501,8 +1512,8 @@
       }
 
       // add in ITEMS
-      return (ItemData) cache.put(makeItemFqn(node.getIdentifier()), ITEM_DATA, node,
-               modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
+      return (ItemData)cache.putWithNotification(makeItemFqn(node.getIdentifier()), ITEM_DATA, node,
+         modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
    }
 
    /**
@@ -1570,7 +1581,8 @@
       }
       // add in ITEMS
       // NullNodeData must never be returned inside internal cache operations. 
-      ItemData returnedData = (ItemData)cache.putInBuffer(makeItemFqn(node.getIdentifier()), ITEM_DATA, node);
+      ItemData returnedData =
+         (ItemData)cache.putInBuffer(makeItemFqn(node.getIdentifier()), ITEM_DATA, node, modifyListsOfChild != ModifyChildOption.NOT_MODIFY);
       return (returnedData instanceof NullItemData) ? null : returnedData;
    }
 
@@ -1623,8 +1635,8 @@
       // add in ITEMS
       // NullItemData must never be returned inside internal cache operations. 
       ItemData returnedData =
-               (ItemData) cache.put(makeItemFqn(prop.getIdentifier()), ITEM_DATA, prop,
-                        modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
+         (ItemData)cache.putWithNotification(makeItemFqn(prop.getIdentifier()), ITEM_DATA, prop,
+            modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
       return (returnedData instanceof NullItemData) ? null : (PropertyData) returnedData;
    }
 
@@ -1690,7 +1702,7 @@
       }
 
       // remove from ITEMS
-      cache.removeNode(makeItemFqn(item.getIdentifier()));
+      cache.removeNode(makeItemFqn(item.getIdentifier()), true);
    }
 
    /**
@@ -1700,7 +1712,7 @@
     */
    protected void updateMixin(NodeData node)
    {
-      NodeData prevData = (NodeData)cache.put(makeItemFqn(node.getIdentifier()), ITEM_DATA, node);
+      NodeData prevData = (NodeData)cache.putWithNotification(makeItemFqn(node.getIdentifier()), ITEM_DATA, node);
       // prevent update NullNodeData
       if (prevData != null && !(prevData instanceof NullItemData))
       {
@@ -1832,7 +1844,7 @@
                      .getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(), prevNode
                      .getParentIdentifier(), inheritACL ? acl : prevNode.getACL()); // TODO check ACL
                // update this node
-               cache.put(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
+               cache.putWithNotification(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
             }
             else
             {
@@ -1850,7 +1862,7 @@
                TransientPropertyData newProp =
                   new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(), prevProp
                      .getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
-               cache.put(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
+               cache.putWithNotification(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
             }
          }
       }
@@ -1885,7 +1897,7 @@
          TransientPropertyData newProp =
             new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(), prevProp
                .getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
-         cache.put(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
+         cache.putWithNotification(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
       }
 
       // update child nodes
@@ -1901,7 +1913,7 @@
                .getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(), prevNode
                .getParentIdentifier(), inheritACL ? acl : prevNode.getACL()); // TODO check ACL
          // update this node
-         cache.put(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
+         cache.putWithNotification(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
          // update childs recursive
          updateTreePath(newNode.getIdentifier(), newNode.getQPath(), inheritACL ? acl : null);
       }
@@ -1932,7 +1944,7 @@
                prevNode.getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(), prevNode
                   .getParentIdentifier(), acl);
          // update this node
-         cache.put(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
+         cache.putWithNotification(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
          // update childs recursive
          updateChildsACL(newNode.getIdentifier(), acl);
       }
@@ -2079,8 +2091,46 @@
          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);
+         }
+      }      
+   }
+   
+   /**
     * Actions that are not supposed to be called within a transaction
     * 
     * Created by The eXo Platform SAS
@@ -2098,4 +2148,20 @@
          return JBossCacheWorkspaceStorageCache.this.getTransactionManager();
       }
    }
+   
+   @org.jboss.cache.notifications.annotation.CacheListener
+   @SuppressWarnings("unchecked")
+   public class CacheEventListener
+   {
+
+      @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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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;
@@ -1222,6 +1223,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}
@@ -1494,7 +1504,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);
          }
@@ -2207,7 +2217,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
@@ -2221,7 +2231,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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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')";
    }
 
    /**
@@ -1009,4 +1013,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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -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')";      
    }
 
    /**
@@ -922,4 +926,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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -23,6 +23,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.impl.storage.jdbc.JDBCWorkspaceDataContainer;
 import org.exoplatform.services.jcr.statistics.JCRStatisticsManager;
 import org.exoplatform.services.jcr.statistics.Statistics;
@@ -142,6 +143,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 
@@ -208,6 +215,7 @@
          LIST_CHILD_PROPERTIES_DATA_DESCR));
       ALL_STATISTICS.put(GET_REFERENCES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, GET_REFERENCES_DATA_DESCR));
       ALL_STATISTICS.put(GET_VALUE_DESCR, new Statistics(GLOBAL_STATISTICS, GET_VALUE_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));
@@ -669,4 +677,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/WorkspaceDataContainer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceDataContainer.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -60,6 +60,10 @@
 
    public final static String CHECK_SNS_NEW_CONNECTION = "check-sns-new-connection";
 
+   public final static String ACL_BF_FALSE_PROPBABILITY = "acl-bloomfilter-false-positive-probability";
+
+   public final static String ACL_BF_ELEMENTS_NUMBER = "acl-bloomfilter-elements-number";
+
    /**
     * [G.A] do we need it here or in WorkspaceDataManager better??
     * 

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -25,6 +25,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 java.util.List;
 
@@ -421,4 +422,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/BaseStandaloneTest.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/BaseStandaloneTest.java	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/BaseStandaloneTest.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -25,14 +25,23 @@
 import org.exoplatform.services.jcr.core.CredentialsImpl;
 import org.exoplatform.services.jcr.core.ManageableRepository;
 import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
+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.datamodel.ValueData;
 import org.exoplatform.services.jcr.impl.core.ItemImpl;
 import org.exoplatform.services.jcr.impl.core.NodeImpl;
 import org.exoplatform.services.jcr.impl.core.RepositoryImpl;
 import org.exoplatform.services.jcr.impl.core.SessionImpl;
+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.serialization.ReaderSpoolFileHolder;
 import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
 import org.exoplatform.services.jcr.impl.util.io.FileCleanerHolder;
 import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
 import org.exoplatform.services.log.ExoLogger;
 import org.exoplatform.services.log.Log;
 
@@ -43,10 +52,12 @@
 import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
 
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.PathNotFoundException;
@@ -509,4 +520,259 @@
          assertTrue(finded);
       }
    }
+
+   /**
+    * Test WorkspaceDataContainer.
+    * Does nothing, must be extended in tests.
+    * 
+    * @author <a href="mailto:skarpenko at exoplatform.com">Sergiy Karpenko</a>
+    * @version $Id: exo-jboss-codetemplates.xml 34360 18.08.2011 skarpenko $
+    */
+   public class TestWorkspaceDataContainer implements WorkspaceDataContainer
+   {
+      @Override
+      public String getInfo()
+      {
+         return null;
+      }
+
+      @Override
+      public String getName()
+      {
+         return null;
+      }
+
+      @Override
+      public String getUniqueName()
+      {
+         return null;
+      }
+
+      @Override
+      public String getStorageVersion()
+      {
+         return null;
+      }
+
+      @Override
+      public Calendar getCurrentTime()
+      {
+         return null;
+      }
+
+      @Override
+      public boolean isSame(WorkspaceDataContainer another)
+      {
+         return false;
+      }
+
+      @Override
+      public WorkspaceStorageConnection openConnection() throws RepositoryException
+      {
+         return null;
+      }
+
+      @Override
+      public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
+      {
+         return null;
+      }
+
+      @Override
+      public WorkspaceStorageConnection reuseConnection(WorkspaceStorageConnection original) throws RepositoryException
+      {
+         return null;
+      }
+
+      @Override
+      public boolean isCheckSNSNewConnection()
+      {
+         return false;
+      }
+   }
+
+   /**
+    * Test WorkspaceStorageConnection.
+    * Does nothing, must be extended in tests.
+    * 
+    * @author <a href="mailto:skarpenko at exoplatform.com">Sergiy Karpenko</a>
+    * @version $Id: exo-jboss-codetemplates.xml 34360 18.08.2011 skarpenko $
+    */
+   public class TestWorkspaceStorageConnection implements WorkspaceStorageConnection
+   {
+
+      @Override
+      public ItemData getItemData(NodeData parentData, QPathEntry name) throws RepositoryException,
+         IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public ItemData getItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
+         IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> pattern)
+         throws RepositoryException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public int getChildNodesCount(NodeData parent) throws RepositoryException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public int getLastOrderNumber(NodeData parent) throws RepositoryException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException,
+         IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> pattern)
+         throws RepositoryException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException,
+         IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException,
+         IllegalStateException, UnsupportedOperationException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void add(NodeData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void add(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void update(NodeData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void update(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void rename(NodeData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void delete(NodeData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void delete(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+         InvalidItemStateException, IllegalStateException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void commit() throws IllegalStateException, RepositoryException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void rollback() throws IllegalStateException, RepositoryException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public void close() throws IllegalStateException, RepositoryException
+      {
+      }
+
+      @Override
+      public boolean isOpened()
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      @Override
+      public List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException,
+         UnsupportedOperationException
+      {
+         throw new UnsupportedOperationException("TestWorkspaceStorageConnection: operation is unsupported.");
+      }
+
+      /**
+       * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getValue(java.lang.String, int, int)
+       */
+      @Override
+      public ValueData getValue(String propertyId, int orderNumb, int persistedVersion) throws IllegalStateException,
+         RepositoryException
+      {
+         return null;
+      }
+
+      /**
+       * @see org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getChildNodesDataByPage(org.exoplatform.services.jcr.datamodel.NodeData, int, int, java.util.List)
+       */
+      @Override
+      public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit, List<NodeData> childs)
+         throws RepositoryException
+      {
+         return false;
+      }
+
+   }
+
 }

Added: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorksapceDataManagerBloomFilter.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorksapceDataManagerBloomFilter.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorksapceDataManagerBloomFilter.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -0,0 +1,301 @@
+/*
+ * 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 org.exoplatform.commons.utils.QName;
+import org.exoplatform.services.jcr.JcrImplBaseTest;
+import org.exoplatform.services.jcr.access.AccessControlEntry;
+import org.exoplatform.services.jcr.access.AccessControlList;
+import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
+import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.QPath;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * @author <a href="mailto:skarpenko at exoplatform.com">Sergiy Karpenko</a>
+ * @version $Id: exo-jboss-codetemplates.xml 34360 18.08.2011 skarpenko $
+ *
+ */
+public class TestCacheableWorksapceDataManagerBloomFilter extends JcrImplBaseTest
+{
+   private MyWorkspaceDataContainer dataContainer;
+
+   private CacheableWorkspaceDataManager mgr;
+
+   public void setUp() throws Exception
+   {
+      super.setUp();
+      dataContainer = new MyWorkspaceDataContainer();
+      WorkspaceStorageCache cache = new MyWorkspaceStorageCache();
+      WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws");
+      WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
+      mgr =
+         new CacheableWorkspaceDataManager(wconf, dataContainer, cache, new SystemDataContainerHolder(dataContainer));
+   }
+
+   protected void tearDown() throws Exception
+   {
+      dataContainer.clear();
+      dataContainer = null;
+      mgr.clear();
+      mgr = null;
+      super.tearDown();
+   }
+
+   public void testGetItemWithACL() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("2", path = new QPath(new QPathEntry[]{new QPathEntry("", "2", 1, "2")}),
+         "3", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(1, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemWithoutACL() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("3", path = new QPath(new QPathEntry[]{new QPathEntry("", "3", 1, "3")}),
+         "4", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, null));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(2, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentWithSingleOwner() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, null));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(3, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentWithSinglePermission() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, null));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(3, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentsWithCrossEmptyACL() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, null));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(3, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentsWithCrossEmptyACL2() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, null));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(3, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentsWithSameOwnerlessACL() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(2, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   public void testGetItemParentsWithSamePermissionlessACL() throws Exception
+   {
+      QPath path;
+      dataContainer.add(new PersistedNodeData("4", path = new QPath(new QPathEntry[]{new QPathEntry("", "4", 1, "4")}),
+         "5", 1, 1, null, null, new AccessControlList("owner", new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("3", path = QPath.makeChildPath(path, new QName("", "3"), 1, "3"), "4",
+         1, 1, null, null, new AccessControlList(null, new ArrayList<AccessControlEntry>())));
+      dataContainer.add(new PersistedNodeData("2", path = QPath.makeChildPath(path, new QName("", "2"), 1, "2"), "3",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      dataContainer.add(new PersistedNodeData("1", path = QPath.makeChildPath(path, new QName("", "1"), 1, "1"), "2",
+         1, 1, null, null, new AccessControlList("owner", null)));
+      mgr.reloadFilters();
+      mgr.getItemData("1");
+      assertEquals(2, dataContainer.getTotalCalls());
+      dataContainer.clear();
+   }
+
+   class MyWorkspaceDataContainer extends TestWorkspaceDataContainer
+   {
+
+      protected Map<String, NodeData> nodesById = new HashMap<String, NodeData>();
+
+      protected List<ACLHolder> holders = new ArrayList<ACLHolder>();
+
+      private int totalCalls;
+
+      protected synchronized void incrementCalls()
+      {
+         totalCalls++;
+      }
+
+      public WorkspaceStorageConnection openConnection() throws RepositoryException
+      {
+         return new MyWorkspaceStorageConnection(this);
+      }
+
+      public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
+      {
+         return openConnection();
+      }
+
+      public void clear()
+      {
+         nodesById.clear();
+         holders.clear();
+         totalCalls = 0;
+      }
+
+      public void add(NodeData node)
+      {
+         nodesById.put(node.getIdentifier(), node);
+         AccessControlList acl = node.getACL();
+         if (acl != null)
+         {
+            ACLHolder holder = new ACLHolder(node.getIdentifier());
+            holder.setOwner(acl.hasOwner());
+            holder.setPermissions(acl.hasPermissions());
+            holders.add(holder);
+         }
+      }
+
+      /**
+       * @return the totalCalls
+       */
+      public int getTotalCalls()
+      {
+         return totalCalls;
+      }
+
+   }
+
+   class MyWorkspaceStorageConnection extends TestWorkspaceStorageConnection
+   {
+      private final MyWorkspaceDataContainer container;
+
+      public MyWorkspaceStorageConnection(MyWorkspaceDataContainer myWorkspaceDataContainer)
+      {
+         container = myWorkspaceDataContainer;
+      }
+
+      /**
+       * @see org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager#getItemData(java.lang.String)
+       */
+      @Override
+      public ItemData getItemData(String identifier) throws RepositoryException
+      {
+         container.incrementCalls();
+         return container.nodesById.get(identifier);
+      }
+
+      /**
+       * @see org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager#getACLHolders()
+       */
+      @Override
+      public List<ACLHolder> getACLHolders() throws RepositoryException
+      {
+         return container.holders;
+      }
+   }
+
+   class MyWorkspaceStorageCache extends LinkedWorkspaceStorageCacheImpl
+   {
+      public MyWorkspaceStorageCache() throws RepositoryConfigurationException
+      {
+         super("", false, 0, 0, 0, 0, false, false, 0, false);
+      }
+
+      public boolean isEnabled()
+      {
+         return false;
+      }
+   }
+
+}

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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -16,12 +16,14 @@
  */
 package org.exoplatform.services.jcr.impl.dataflow.persistent;
 
-import junit.framework.TestCase;
-
+import org.exoplatform.services.jcr.JcrImplBaseTest;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
 import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
 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;
@@ -52,7 +54,7 @@
  *          nicolas.filotto at exoplatform.com
  * 29 mars 2010  
  */
-public class TestCacheableWorkspaceDataManager extends TestCase
+public class TestCacheableWorkspaceDataManager extends JcrImplBaseTest
 {
 
    private static final int READER = 100;
@@ -66,13 +68,16 @@
    private MyWorkspaceStorageConnection con;
 
    @Override
-   protected void setUp() throws Exception
+   public void setUp() throws Exception
    {
       super.setUp();
       this.con = new MyWorkspaceStorageConnection();
       this.wdc = new MyWorkspaceDataContainer(con);
+      WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws");
+      WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
       this.cwdm =
-         new CacheableWorkspaceDataManager(wdc, new MyWorkspaceStorageCache(), new SystemDataContainerHolder(wdc));
+         new CacheableWorkspaceDataManager(wconf, wdc, new MyWorkspaceStorageCache(),
+            new SystemDataContainerHolder(wdc));
    }
 
    @Override
@@ -439,6 +444,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
@@ -611,6 +630,15 @@
          return false;
       }
       
+      /**
+       * @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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -21,6 +21,8 @@
 import org.exoplatform.commons.utils.QName;
 import org.exoplatform.services.jcr.JcrImplBaseTest;
 import org.exoplatform.services.jcr.access.AccessControlList;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
 import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
@@ -72,10 +74,12 @@
       {
          MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection();
          WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con);
+         WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws");
+         WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
          CacheableWorkspaceDataManager cwdmNode1 =
-            new CacheableWorkspaceDataManager(wdc, cache1 = getCacheImpl(), new SystemDataContainerHolder(wdc));
+            new CacheableWorkspaceDataManager(wconf, wdc, cache1 = getCacheImpl(), new SystemDataContainerHolder(wdc));
          CacheableWorkspaceDataManager cwdmNode2 =
-            new CacheableWorkspaceDataManager(wdc, cache2 = getCacheImpl(), new SystemDataContainerHolder(wdc));
+            new CacheableWorkspaceDataManager(wconf, wdc, cache2 = getCacheImpl(), new SystemDataContainerHolder(wdc));
          NodeData parentNode = new PersistedNodeData("parent-id", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node")), Constants.ROOT_UUID, 1, 0,
             Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
          // Test getChildNodesData
@@ -329,10 +333,12 @@
          // testConsistency
          con = new MyWorkspaceStorageConnection(true);
          wdc = new MyWorkspaceDataContainer(con);
+         wsc = repository.getWorkspaceContainer("ws");
+         wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
          cwdmNode1 =
-            new CacheableWorkspaceDataManager(wdc, cache1, new SystemDataContainerHolder(wdc));
+            new CacheableWorkspaceDataManager(wconf, wdc, cache1, new SystemDataContainerHolder(wdc));
          cwdmNode2 =
-            new CacheableWorkspaceDataManager(wdc, cache2, new SystemDataContainerHolder(wdc));
+            new CacheableWorkspaceDataManager(wconf, wdc, cache2, new SystemDataContainerHolder(wdc));
          parentNode = new PersistedNodeData("parent2-id", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent2-node")), Constants.ROOT_UUID, 1, 0,
             Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
          // Test getChildNodesData
@@ -962,6 +968,15 @@
          return false;
       }
       
+
+      /**
+       * @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-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java	2011-09-02 10:32:08 UTC (rev 4832)
@@ -22,6 +22,7 @@
 import org.exoplatform.services.jcr.config.CacheEntry;
 import org.exoplatform.services.jcr.config.SimpleParameterEntry;
 import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
 import org.exoplatform.services.jcr.dataflow.ItemState;
 import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
 import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
@@ -35,6 +36,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.CacheableWorkspaceDataManager;
 import org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspaceStorageCacheBaseCase;
 import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
@@ -83,8 +85,10 @@
    {
       MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection();
       WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con);
+      WorkspaceContainerFacade wsc = repository.getWorkspaceContainer("ws");
+      WorkspaceEntry wconf = (WorkspaceEntry)wsc.getComponent(WorkspaceEntry.class);
       final CacheableWorkspaceDataManager cwdm =
-         new CacheableWorkspaceDataManager(wdc, getCacheImpl(), new SystemDataContainerHolder(wdc));
+         new CacheableWorkspaceDataManager(wconf, wdc, getCacheImpl(), new SystemDataContainerHolder(wdc));
       String idNode = "foo1";
       executeConcurrentReadNWrite(con, cwdm, Mode.READ_FIRST, idNode);
       assertNotNull(cwdm.getItemData(idNode));
@@ -327,6 +331,17 @@
          return false;
       }
 
+
+      /**
+       * @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

Modified: jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/exo-jcr-configuration.xml
===================================================================
--- jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/exo-jcr-configuration.xml	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/exo-jcr-configuration.xml	2011-09-02 10:32:08 UTC (rev 4832)
@@ -262,11 +262,7 @@
             operation but in same time Observation'll not notified, has
             default value true</entry>
           </row>
-        </tbody>
-      </tgroup>
 
-      <tgroup cols="2">
-        <tbody>
           <row>
             <entry>lazy-node-iterator-page-size</entry>
 
@@ -274,6 +270,20 @@
             can be retrieved from storage per request. The default value is
             100</entry>
           </row>
+
+          <row>
+            <entry>acl-bloomfilter-false-positive-probability</entry>
+
+            <entry>ACL Bloom-filter desired false positive probability. Range
+            [0..1]. Default value 0.1d.</entry>
+          </row>
+
+          <row>
+            <entry>acl-bloomfilter-elements-number</entry>
+
+            <entry>Expected number of ACL-elements in the Bloom-filter.
+            Default value 1000000.</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>

Modified: jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/workspace-persistence-storage.xml
===================================================================
--- jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/workspace-persistence-storage.xml	2011-09-02 09:58:10 UTC (rev 4831)
+++ jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/jcr/configuration/workspace-persistence-storage.xml	2011-09-02 10:32:08 UTC (rev 4832)
@@ -24,6 +24,8 @@
     &lt;property name="max-buffer-size" value="200K"/&gt;
     &lt;property name="swap-directory" value="target/temp/swap/ws"/&gt;
     &lt;property name="lazy-node-iterator-page-size" value="50"/&gt;
+    &lt;property name="acl-bloomfilter-false-positive-probability" value="0.1d"/&gt;
+    &lt;property name="acl-bloomfilter-elements-number" value="1000000"/&gt;
   &lt;/properties&gt;</programlisting>
 
   <para>Properties are Container specific parameters:</para>
@@ -51,6 +53,26 @@
   child nodes iterator settings. Defines size of page, the number of nodes
   that are retrieved from persistent storage at once.</para>
 
+  <para><emphasis
+  role="bold">acl-bloomfilter-false-positive-probability</emphasis>: ACL
+  Bloom-filter settings. ACL Bloom-filter desired false positive probability.
+  Range [0..1]. Default value 0.1d.</para>
+
+  <para><emphasis role="bold">acl-bloomfilter-elements-number</emphasis>: ACL
+  Bloom-filter settings. Expected number of ACL-elements in the Bloom-filter.
+  Default value 1000000.</para>
+
+  <note>
+    <para>Bloom-filter used to avoid read nodes that definitely do not have
+    ACL. <emphasis
+    role="bold">acl-bloomfilter-false-positive-probability</emphasis> and
+    <emphasis role="bold">acl-bloomfilter-elements-number</emphasis> used to
+    configure such filters.</para>
+
+    <para>More about Bloom filters you can read here <ulink
+    url="http://en.wikipedia.org/wiki/Bloom_filter">http://en.wikipedia.org/wiki/Bloom_filter</ulink>.</para>
+  </note>
+
   <para>eXo JCR has an RDB (JDBC) based, production ready <emphasis
   role="bold">Workspace Data Container</emphasis>.</para>
 



More information about the exo-jcr-commits mailing list