Author: sergiykarpenko
Date: 2011-08-18 02:39:44 -0400 (Thu, 18 Aug 2011)
New Revision: 4770
Added:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java
Removed:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java
Log:
EXOJCR-1456: use bloom filters to store ids of nodes with ACL
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCache.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -275,16 +275,31 @@
/**
* Start buffering process.
*/
- public void beginTransaction();
+ void beginTransaction();
/**
* Sort changes and commit data to the cache.
*/
- public void commitTransaction();
+ void commitTransaction();
/**
* Forget about changes
*/
- public void rollbackTransaction();
+ void rollbackTransaction();
+ /**
+ * Adds a new listener
+ * @param listener the listener to register
+ * @throws UnsupportedOperationException in case the listeners are not supported by
the
+ * implementation
+ */
+ void addListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException;
+
+ /**
+ * Removes a listener
+ * @param listener the listener to remove
+ * @throws UnsupportedOperationException in case the listeners are not supported by
the
+ * implementation
+ */
+ void removeListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException;
}
Added:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java
(rev 0)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/dataflow/persistent/WorkspaceStorageCacheListener.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.dataflow.persistent;
+
+import org.exoplatform.services.jcr.datamodel.ItemData;
+
+/**
+ * This class allows other class to be notified when a given cache event occurs
+ *
+ * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas
Filotto</a>
+ * @version $Id$
+ *
+ */
+public interface WorkspaceStorageCacheListener
+{
+ /**
+ * Called when a cache entry corresponding to the given item has been added
+ * @param data the item corresponding to the added cache entry
+ */
+ void onCacheEntryAdded(ItemData data);
+
+ /**
+ * Called when a cache entry corresponding to the given item has been updated
+ * @param data the item corresponding to the updated cache entry
+ */
+ void onCacheEntryUpdated(ItemData data);
+
+ /**
+ * Called when a cache entry corresponding to the given item has been removed
+ * @param data the item corresponding to the removed cache entry
+ */
+ void onCacheEntryRemoved(ItemData data);
+}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPath.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -469,6 +469,34 @@
}
/**
+ * Make child path using QName and Item index. <br/>
+ *
+ * @param parent
+ * - parent QPath
+ * @param name
+ * - Item QName
+ * @param itemIndex
+ * - Item index
+ * @param id
+ * - Item id
+ * @return new QPath
+ */
+ public static QPath makeChildPath(final QPath parent, final QName name, final int
itemIndex, String id)
+ {
+
+ QPathEntry[] parentEntries = parent.getEntries();
+ QPathEntry[] names = new QPathEntry[parentEntries.length + 1];
+ int index = 0;
+ for (QPathEntry pname : parentEntries)
+ {
+ names[index++] = pname;
+ }
+ names[index] = new QPathEntry(name.getNamespace(), name.getName(), itemIndex, id);
+
+ QPath path = new QPath(names);
+ return path;
+ }
+ /**
* Make child path using array of QPath entries (relative path). <br/>
*
* @param parent
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/datamodel/QPathEntry.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -36,6 +36,8 @@
private String cachedToStringShowIndex;
+ private String id;
+
/**
* QPathEntry constructor.
*
@@ -46,10 +48,27 @@
*/
public QPathEntry(InternalQName qName, int index)
{
+ this(qName, index, null);
+ }
+
+
+ /**
+ * QPathEntry constructor.
+ *
+ * @param qName
+ * - InternalQName (full qualified name)
+ * @param index
+ * - Item index
+ * @param id
+ * - Item id
+ */
+ public QPathEntry(InternalQName qName, int index, String id)
+ {
super(qName.getNamespace(), qName.getName());
this.index = index > 0 ? index : 1;
+ this.id = id;
}
-
+
/**
* QPathEntry constructor.
*
@@ -62,8 +81,26 @@
*/
public QPathEntry(String namespace, String name, int index)
{
+ this(namespace, name, index, null);
+ }
+
+ /**
+ * QPathEntry constructor.
+ *
+ * @param namespace
+ * - namespace URI
+ * @param name
+ * - Item name
+ * @param index
+ * - Item index
+ * @param id
+ * - Item id
+ */
+ public QPathEntry(String namespace, String name, int index, String id)
+ {
super(namespace, name);
this.index = index > 0 ? index : 1;
+ this.id = id;
}
/**
@@ -89,6 +126,16 @@
InternalQName qname = InternalQName.parse(qnameString);
return new QPathEntry(qname, Integer.valueOf(indexString));
+ }
+
+ /**
+ * Return Item id, can be null since it could not be set
+ *
+ * @return the id of the item
+ */
+ public String getId()
+ {
+ return id;
}
/**
Added:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java
(rev 0)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLHolder.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.dataflow.persistent;
+
+/**
+ * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas
Filotto</a>
+ * @version $Id$
+ *
+ */
+public class ACLHolder
+{
+ /**
+ * The id of the node that holds some ACL info
+ */
+ private final String id;
+
+ /**
+ * A flag indicating whether or not the node has owner set
+ */
+ private boolean owner;
+
+ /**
+ * A flag indicating whether or not the node has permissions set
+ */
+ private boolean permissions;
+
+ public ACLHolder(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @return the owner
+ */
+ public boolean hasOwner()
+ {
+ return owner;
+ }
+
+ /**
+ * @return the permissions
+ */
+ public boolean hasPermissions()
+ {
+ return permissions;
+ }
+
+ /**
+ * @param owner the owner to set
+ */
+ public void setOwner(boolean owner)
+ {
+ this.owner = owner;
+ }
+
+ /**
+ * @param permissions the permissions to set
+ */
+ public void setPermissions(boolean permissions)
+ {
+ this.permissions = permissions;
+ }
+}
Deleted:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ACLInheritanceSupportedWorkspaceDataManager.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2009 eXo Platform SAS.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
- */
-package org.exoplatform.services.jcr.impl.dataflow.persistent;
-
-import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
-import org.exoplatform.services.jcr.dataflow.SharedDataManager;
-import org.exoplatform.services.jcr.datamodel.ItemData;
-import org.exoplatform.services.jcr.datamodel.ItemType;
-import org.exoplatform.services.jcr.datamodel.NodeData;
-import org.exoplatform.services.jcr.datamodel.PropertyData;
-import org.exoplatform.services.jcr.datamodel.QPathEntry;
-import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-
-import java.util.Calendar;
-import java.util.List;
-
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.RepositoryException;
-
-/**
- * Created by The eXo Platform SAS. Data Manager supported ACL Inheritance
- *
- * @author Gennady Azarenkov
- * @version $Id: ACLInheritanceSupportedWorkspaceDataManager.java 11907 2008-03-13
15:36:21Z ksm $
- */
-public class ACLInheritanceSupportedWorkspaceDataManager implements SharedDataManager
-{
-
- private static Log LOG =
ExoLogger.getLogger("exo.jcr.component.core.ACLInheritanceSupportedWorkspaceDataManager");
-
- protected final CacheableWorkspaceDataManager persistentManager;
-
- public ACLInheritanceSupportedWorkspaceDataManager(CacheableWorkspaceDataManager
persistentManager)
- {
- this.persistentManager = persistentManager;
- }
-
- /**
- * {@inheritDoc}
- */
- // ------------ ItemDataConsumer impl ------------
- public List<NodeData> getChildNodesData(NodeData parent) throws
RepositoryException
- {
- return persistentManager.getChildNodesData(parent);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<NodeData> getChildNodesData(NodeData parent,
List<QPathEntryFilter> patternFilters) throws RepositoryException
- {
- return persistentManager.getChildNodesData(parent, patternFilters);
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit,
List<NodeData> childs)
- throws RepositoryException
- {
- return persistentManager.getChildNodesDataByPage(parent, fromOrderNum, limit,
childs);
- }
-
- /**
- * {@inheritDoc}
- */
- public int getLastOrderNumber(final NodeData parent) throws RepositoryException
- {
- return persistentManager.getLastOrderNumber(parent);
- }
-
- /**
- * {@inheritDoc}
- */
- public int getChildNodesCount(final NodeData parent) throws RepositoryException
- {
- return persistentManager.getChildNodesCount(parent);
- }
-
- /**
- * {@inheritDoc}
- */
- public ItemData getItemData(NodeData parent, QPathEntry name) throws
RepositoryException
- {
- return getItemData(parent, name, ItemType.UNKNOWN);
- }
-
- /**
- * {@inheritDoc}
- */
- public ItemData getItemData(NodeData parent, QPathEntry name, ItemType itemType)
throws RepositoryException
- {
- return persistentManager.getItemData(parent, name, itemType);
- }
-
- /**
- * {@inheritDoc}
- */
- public ItemData getItemData(String identifier) throws RepositoryException
- {
- return persistentManager.getItemData(identifier);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<PropertyData> getChildPropertiesData(NodeData parent) throws
RepositoryException
- {
- return persistentManager.getChildPropertiesData(parent);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<PropertyData> getChildPropertiesData(NodeData parent,
List<QPathEntryFilter> itemDataFilters)
- throws RepositoryException
- {
- return persistentManager.getChildPropertiesData(parent, itemDataFilters);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<PropertyData> listChildPropertiesData(NodeData parent) throws
RepositoryException
- {
- return persistentManager.listChildPropertiesData(parent);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<PropertyData> getReferencesData(String identifier, boolean
skipVersionStorage)
- throws RepositoryException
- {
- return persistentManager.getReferencesData(identifier, skipVersionStorage);
- }
-
- // ------------ SharedDataManager ----------------------
-
- /**
- * {@inheritDoc}
- */
- public void save(ItemStateChangesLog changes) throws InvalidItemStateException,
UnsupportedOperationException,
- RepositoryException
- {
- persistentManager.save(changes);
- }
-
- /**
- * {@inheritDoc}
- */
- public Calendar getCurrentTime()
- {
- return persistentManager.getCurrentTime();
- }
-}
Added:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java
(rev 0)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/BloomFilter.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2011 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.dataflow.persistent;
+
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.BitSet;
+import java.util.Collection;
+
+/**
+ * Implementation of a Bloom-filter, as described here:
+ *
http://en.wikipedia.org/wiki/Bloom_filter
+ *
+ * Inspired by the SimpleBloomFilter-class written by Ian Clarke. This
+ * implementation provides a more evenly distributed Hash-function by
+ * using a proper digest instead of the Java RNG. Many of the changes
+ * were proposed in comments in his blog:
+ *
http://blog.locut.us/2008/01/12/a-decent-stand-alone-java-bloom-filter-im...
+ *
+ * @param <E> Object type that is to be inserted into the Bloom filter, e.g. String
or Integer.
+ * @author Magnus Skjegstad <magnus(a)skjegstad.com>
+ */
+public class BloomFilter<E> implements Serializable {
+ private BitSet bitset;
+ private int bitSetSize;
+ private double bitsPerElement;
+ private int expectedNumberOfFilterElements; // expected (maximum) number of elements
to be added
+ private int numberOfAddedElements; // number of elements actually added to the Bloom
filter
+ private int k; // number of hash functions
+
+ static final Charset charset = Charset.forName("UTF-8"); // encoding used
for storing hash values as strings
+
+ static final String hashName = "MD5"; // MD5 gives good enough accuracy in
most circumstances. Change to SHA1 if it's needed
+ static final MessageDigest digestFunction;
+ static { // The digest method is reused between instances
+ MessageDigest tmp;
+ try {
+ tmp = java.security.MessageDigest.getInstance(hashName);
+ } catch (NoSuchAlgorithmException e) {
+ tmp = null;
+ }
+ digestFunction = tmp;
+ }
+
+ /**
+ * Constructs an empty Bloom filter. The total length of the Bloom filter will be
+ * c*n.
+ *
+ * @param c is the number of bits used per element.
+ * @param n is the expected number of elements the filter will contain.
+ * @param k is the number of hash functions used.
+ */
+ public BloomFilter(double c, int n, int k) {
+ this.expectedNumberOfFilterElements = n;
+ this.k = k;
+ this.bitsPerElement = c;
+ this.bitSetSize = (int)Math.ceil(c * n);
+ numberOfAddedElements = 0;
+ this.bitset = new BitSet(bitSetSize);
+ }
+
+ /**
+ * Constructs an empty Bloom filter. The optimal number of hash functions (k) is
estimated from the total size of the Bloom
+ * and the number of expected elements.
+ *
+ * @param bitSetSize defines how many bits should be used in total for the filter.
+ * @param expectedNumberOElements defines the maximum number of elements the filter
is expected to contain.
+ */
+ public BloomFilter(int bitSetSize, int expectedNumberOElements) {
+ this(bitSetSize / (double)expectedNumberOElements,
+ expectedNumberOElements,
+ (int) Math.round((bitSetSize / (double)expectedNumberOElements) *
Math.log(2.0)));
+ }
+
+ /**
+ * Constructs an empty Bloom filter with a given false positive probability. The
number of bits per
+ * element and the number of hash functions is estimated
+ * to match the false positive probability.
+ *
+ * @param falsePositiveProbability is the desired false positive probability.
+ * @param expectedNumberOfElements is the expected number of elements in the Bloom
filter.
+ */
+ public BloomFilter(double falsePositiveProbability, int expectedNumberOfElements) {
+ this(Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2))) /
Math.log(2), // c = k / ln(2)
+ expectedNumberOfElements,
+ (int)Math.ceil(-(Math.log(falsePositiveProbability) / Math.log(2)))); // k =
ceil(-log_2(false prob.))
+ }
+
+ /**
+ * Construct a new Bloom filter based on existing Bloom filter data.
+ *
+ * @param bitSetSize defines how many bits should be used for the filter.
+ * @param expectedNumberOfFilterElements defines the maximum number of elements the
filter is expected to contain.
+ * @param actualNumberOfFilterElements specifies how many elements have been inserted
into the <code>filterData</code> BitSet.
+ * @param filterData a BitSet representing an existing Bloom filter.
+ */
+ public BloomFilter(int bitSetSize, int expectedNumberOfFilterElements, int
actualNumberOfFilterElements, BitSet filterData) {
+ this(bitSetSize, expectedNumberOfFilterElements);
+ this.bitset = filterData;
+ this.numberOfAddedElements = actualNumberOfFilterElements;
+ }
+
+ /**
+ * Generates a digest based on the contents of a String.
+ *
+ * @param val specifies the input data.
+ * @param charset specifies the encoding of the input data.
+ * @return digest as long.
+ */
+ public static long createHash(String val, Charset charset) {
+ return createHash(val.getBytes(charset));
+ }
+
+ /**
+ * Generates a digest based on the contents of a String.
+ *
+ * @param val specifies the input data. The encoding is expected to be UTF-8.
+ * @return digest as long.
+ */
+ public static long createHash(String val) {
+ return createHash(val, charset);
+ }
+
+ /**
+ * Generates a digest based on the contents of an array of bytes.
+ *
+ * @param data specifies input data.
+ * @return digest as long.
+ */
+ public static long createHash(byte[] data) {
+ long h = 0;
+ byte[] res;
+
+ synchronized (digestFunction) {
+ res = digestFunction.digest(data);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ h <<= 8;
+ h |= ((int) res[i]) & 0xFF;
+ }
+ return h;
+ }
+
+ /**
+ * Compares the contents of two instances to see if they are equal.
+ *
+ * @param obj is the object to compare to.
+ * @return True if the contents of the objects are equal.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final BloomFilter<E> other = (BloomFilter<E>) obj;
+ if (this.expectedNumberOfFilterElements != other.expectedNumberOfFilterElements)
{
+ return false;
+ }
+ if (this.k != other.k) {
+ return false;
+ }
+ if (this.bitSetSize != other.bitSetSize) {
+ return false;
+ }
+ if (this.bitset != other.bitset && (this.bitset == null ||
!this.bitset.equals(other.bitset))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Calculates a hash code for this class.
+ * @return hash code representing the contents of an instance of this class.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 61 * hash + (this.bitset != null ? this.bitset.hashCode() : 0);
+ hash = 61 * hash + this.expectedNumberOfFilterElements;
+ hash = 61 * hash + this.bitSetSize;
+ hash = 61 * hash + this.k;
+ return hash;
+ }
+
+
+ /**
+ * Calculates the expected probability of false positives based on
+ * the number of expected filter elements and the size of the Bloom filter.
+ * <br /><br />
+ * The value returned by this method is the <i>expected</i> rate of
false
+ * positives, assuming the number of inserted elements equals the number of
+ * expected elements. If the number of elements in the Bloom filter is less
+ * than the expected value, the true probability of false positives will be lower.
+ *
+ * @return expected probability of false positives.
+ */
+ public double expectedFalsePositiveProbability() {
+ return getFalsePositiveProbability(expectedNumberOfFilterElements);
+ }
+
+ /**
+ * Calculate the probability of a false positive given the specified
+ * number of inserted elements.
+ *
+ * @param numberOfElements number of inserted elements.
+ * @return probability of a false positive.
+ */
+ public double getFalsePositiveProbability(double numberOfElements) {
+ // (1 - e^(-k * n / m)) ^ k
+ return Math.pow((1 - Math.exp(-k * (double) numberOfElements
+ / (double) bitSetSize)), k);
+
+ }
+
+ /**
+ * Get the current probability of a false positive. The probability is calculated
from
+ * the size of the Bloom filter and the current number of elements added to it.
+ *
+ * @return probability of false positives.
+ */
+ public double getFalsePositiveProbability() {
+ return getFalsePositiveProbability(numberOfAddedElements);
+ }
+
+
+ /**
+ * Returns the value chosen for K.<br />
+ * <br />
+ * K is the optimal number of hash functions based on the size
+ * of the Bloom filter and the expected number of inserted elements.
+ *
+ * @return optimal k.
+ */
+ public int getK() {
+ return k;
+ }
+
+ /**
+ * Sets all bits to false in the Bloom filter.
+ */
+ public void clear() {
+ bitset.clear();
+ numberOfAddedElements = 0;
+ }
+
+ /**
+ * Adds an object to the Bloom filter. The output from the object's
+ * toString() method is used as input to the hash functions.
+ *
+ * @param element is an element to register in the Bloom filter.
+ */
+ public void add(E element) {
+ long hash;
+ String valString = element.toString();
+ for (int x = 0; x < k; x++) {
+ hash = createHash(valString + Integer.toString(x));
+ hash = hash % (long)bitSetSize;
+ bitset.set(Math.abs((int)hash), true);
+ }
+ numberOfAddedElements ++;
+ }
+
+ /**
+ * Adds all elements from a Collection to the Bloom filter.
+ * @param c Collection of elements.
+ */
+ public void addAll(Collection<? extends E> c) {
+ for (E element : c)
+ add(element);
+ }
+
+ /**
+ * Returns true if the element could have been inserted into the Bloom filter.
+ * Use getFalsePositiveProbability() to calculate the probability of this
+ * being correct.
+ *
+ * @param element element to check.
+ * @return true if the element could have been inserted into the Bloom filter.
+ */
+ public boolean contains(E element) {
+ long hash;
+ String valString = element.toString();
+ for (int x = 0; x < k; x++) {
+ hash = createHash(valString + Integer.toString(x));
+ hash = hash % (long)bitSetSize;
+ if (!bitset.get(Math.abs((int)hash)))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if all the elements of a Collection could have been inserted
+ * into the Bloom filter. Use getFalsePositiveProbability() to calculate the
+ * probability of this being correct.
+ * @param c elements to check.
+ * @return true if all the elements in c could have been inserted into the Bloom
filter.
+ */
+ public boolean containsAll(Collection<? extends E> c) {
+ for (E element : c)
+ if (!contains(element))
+ return false;
+ return true;
+ }
+
+ /**
+ * Read a single bit from the Bloom filter.
+ * @param bit the bit to read.
+ * @return true if the bit is set, false if it is not.
+ */
+ public boolean getBit(int bit) {
+ return bitset.get(bit);
+ }
+
+ /**
+ * Set a single bit in the Bloom filter.
+ * @param bit is the bit to set.
+ * @param value If true, the bit is set. If false, the bit is cleared.
+ */
+ public void setBit(int bit, boolean value) {
+ bitset.set(bit, value);
+ }
+
+ /**
+ * Return the bit set used to store the Bloom filter.
+ * @return bit set representing the Bloom filter.
+ */
+ public BitSet getBitSet() {
+ return bitset;
+ }
+
+ /**
+ * Returns the number of bits in the Bloom filter. Use count() to retrieve
+ * the number of inserted elements.
+ *
+ * @return the size of the bitset used by the Bloom filter.
+ */
+ public int size() {
+ return this.bitSetSize;
+ }
+
+ /**
+ * Returns the number of elements added to the Bloom filter after it
+ * was constructed or after clear() was called.
+ *
+ * @return number of elements added to the Bloom filter.
+ */
+ public int count() {
+ return this.numberOfAddedElements;
+ }
+
+ /**
+ * Returns the expected number of elements to be inserted into the filter.
+ * This value is the same value as the one passed to the constructor.
+ *
+ * @return expected number of elements.
+ */
+ public int getExpectedNumberOfElements() {
+ return expectedNumberOfFilterElements;
+ }
+
+ /**
+ * Get expected number of bits per element when the Bloom filter is full. This value
is set by the constructor
+ * when the Bloom filter is created. See also getBitsPerElement().
+ *
+ * @return expected number of bits per element.
+ */
+ public double getExpectedBitsPerElement() {
+ return this.bitsPerElement;
+ }
+
+ /**
+ * Get actual number of bits per element based on the number of elements that have
currently been inserted and the length
+ * of the Bloom filter. See also getExpectedBitsPerElement().
+ *
+ * @return number of bits per element.
+ */
+ public double getBitsPerElement() {
+ return this.bitSetSize / (double)numberOfAddedElements;
+ }
+}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/CacheableWorkspaceDataManager.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -19,10 +19,14 @@
package org.exoplatform.services.jcr.impl.dataflow.persistent;
import org.exoplatform.commons.utils.SecurityHelper;
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.AccessControlList;
import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
import
org.exoplatform.services.jcr.dataflow.persistent.MandatoryItemsPersistenceListener;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -43,12 +47,14 @@
import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
import org.exoplatform.services.rpc.RPCException;
import org.exoplatform.services.rpc.RPCService;
import org.exoplatform.services.rpc.RemoteCommand;
import org.exoplatform.services.rpc.TopologyChangeEvent;
import org.exoplatform.services.rpc.TopologyChangeListener;
import org.exoplatform.services.transaction.TransactionService;
+import org.picocontainer.Startable;
import java.io.Serializable;
import java.security.PrivilegedAction;
@@ -64,6 +70,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jcr.RepositoryException;
@@ -80,7 +87,7 @@
* @version $Id$
*/
public class CacheableWorkspaceDataManager extends WorkspacePersistentDataManager
implements Suspendable,
- TopologyChangeListener
+ TopologyChangeListener, Startable, WorkspaceStorageCacheListener
{
/**
@@ -98,6 +105,12 @@
*/
private final TransactionableResourceManager txResourceManager;
+ private final AtomicBoolean filtersEnabled = new AtomicBoolean();
+
+ private volatile BloomFilter<String> filterPermissions;
+
+ private volatile BloomFilter<String> filterOwner;
+
private TransactionManager transactionManager;
/**
@@ -2024,16 +2037,30 @@
/**
* Init ACL of the node.
- *
* @param parent
* - a parent, can be null (get item by id)
- * @param data
+ * @param node
* - an item data
* @return - an item data with ACL was initialized
* @throws RepositoryException
*/
private ItemData initACL(NodeData parent, NodeData node) throws RepositoryException
{
+ return initACL(parent, node, null);
+ }
+
+ /**
+ * @param parent
+ * - a parent, can be null (get item by id)
+ * @param node
+ * - an node data
+ * @param search
+ * - indicates what we are looking for
+ * @return - an node data with ACL was initialized
+ * @throws RepositoryException
+ */
+ private NodeData initACL(NodeData parent, NodeData node, ACLSearch search) throws
RepositoryException
+ {
if (node != null)
{
AccessControlList acl = node.getACL();
@@ -2049,17 +2076,37 @@
}
else
{
+ if (search == null)
+ {
+ search = new ACLSearch(null, null);
+ }
// use nearest ancestor ACL... case of get by id
node =
new TransientNodeData(node.getQPath(), node.getIdentifier(),
node.getPersistedVersion(), node
.getPrimaryTypeName(), node.getMixinTypeNames(),
node.getOrderNumber(),
- node.getParentIdentifier(), getNearestACAncestorAcl(node));
+ node.getParentIdentifier(), getNearestACAncestorAcl(node, search));
}
}
else if (!acl.hasPermissions())
{
// use nearest ancestor permissions
- AccessControlList ancestorAcl = getNearestACAncestorAcl(node);
+ if (search == null)
+ {
+ search = new ACLSearch(acl.getOwner(), null);
+ }
+ else
+ {
+ search.setOwner(acl.getOwner());
+ if (search.found())
+ {
+ return new TransientNodeData(node.getQPath(), node.getIdentifier(),
node.getPersistedVersion(),
+ node.getPrimaryTypeName(), node.getMixinTypeNames(),
node.getOrderNumber(),
+ node.getParentIdentifier(), new AccessControlList(acl.getOwner(),
null));
+ }
+ }
+ AccessControlList ancestorAcl =
+ parent != null && parent.getACL() != null &&
parent.getACL().hasPermissions() ? parent.getACL()
+ : getNearestACAncestorAcl(node, search);
node =
new TransientNodeData(node.getQPath(), node.getIdentifier(),
node.getPersistedVersion(), node
@@ -2068,8 +2115,24 @@
}
else if (!acl.hasOwner())
{
+ if (search == null)
+ {
+ search = new ACLSearch(null, acl.getPermissionEntries());
+ }
+ else
+ {
+ search.setPermissions(acl.getPermissionEntries());
+ if (search.found())
+ {
+ return new TransientNodeData(node.getQPath(), node.getIdentifier(),
node.getPersistedVersion(),
+ node.getPrimaryTypeName(), node.getMixinTypeNames(),
node.getOrderNumber(),
+ node.getParentIdentifier(), new AccessControlList(null,
acl.getPermissionEntries()));
+ }
+ }
// use nearest ancestor owner
- AccessControlList ancestorAcl = getNearestACAncestorAcl(node);
+ AccessControlList ancestorAcl =
+ parent != null && parent.getACL() != null &&
parent.getACL().hasOwner() ? parent.getACL()
+ : getNearestACAncestorAcl(node, search);
node =
new TransientNodeData(node.getQPath(), node.getIdentifier(),
node.getPersistedVersion(), node
@@ -2088,27 +2151,289 @@
*
* @param node
* - item
+ * @param search
+ * - indicates what we are looking for
* @return - parent or null
* @throws RepositoryException
*/
- private AccessControlList getNearestACAncestorAcl(NodeData node) throws
RepositoryException
+ private AccessControlList getNearestACAncestorAcl(NodeData node, ACLSearch search)
throws RepositoryException
{
-
- if (node.getParentIdentifier() != null)
+ String id = node.getParentIdentifier();
+ if (id != null)
{
- NodeData parent = (NodeData)getItemData(node.getParentIdentifier());
- while (parent != null)
+ boolean filtersEnabled = this.filtersEnabled.get();
+ BloomFilter<String> filterPermissions = this.filterPermissions;
+ BloomFilter<String> filterOwner = this.filterOwner;
+ if (filtersEnabled && filterOwner != null && filterPermissions
!= null)
{
- if (parent.getACL() != null)
+ QPathEntry[] entries = node.getQPath().getEntries();
+ for (int i = entries.length - 2; i >= 0; i--)
{
- // has an AC parent
- return parent.getACL();
+ QPathEntry entry = entries[i];
+ String currentId = entry.getId();
+ if (currentId == null)
+ {
+ // the path doesn't contain any id so we do a normal call
+ break;
+ }
+ else if ((!search.hasOwner() && filterOwner.contains(currentId))
+ || (!search.hasPermissions() &&
filterPermissions.contains(currentId)))
+ {
+ id = currentId;
+ break;
+ }
+ else
+ {
+ id = currentId;
+ }
}
- // going up to the root
- parent = (NodeData)getItemData(parent.getParentIdentifier());
}
+ NodeData parent = getACL(id, search);
+ if (parent != null && parent.getACL() != null)
+ {
+ // has an AC parent
+ return parent.getACL();
+ }
}
return new AccessControlList();
}
+ /**
+ * Find Item by identifier to get the missing ACL information.
+ *
+ * @param identifier the id of the node that we are looking for to fill the ACL
research
+ * @param search the ACL search describing what we are looking for
+ * @return NodeData, data by identifier
+ */
+ private NodeData getACL(String identifier, ACLSearch search) throws
RepositoryException
+ {
+ final ItemData item = getItemData(identifier);
+ return item != null && item.isNode() ? initACL(null, (NodeData)item,
search) : null;
+ }
+
+ /**
+ * Gets the list of all the ACL holders
+ * @throws RepositoryException if an error occurs
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException
+ {
+ WorkspaceStorageConnection conn = dataContainer.openConnection();
+ try
+ {
+ return conn.getACLHolders();
+ }
+ finally
+ {
+ conn.close();
+ }
+ }
+
+ /**
+ * Reloads the bloom filters
+ * @return <code>true</code> if the filters could be reloaded
successfully, <code>false</code> otherwise.
+ */
+ @Managed
+ @ManagedDescription("Reloads the bloom filters used to efficiently manage the
ACLs")
+ public boolean reloadFilters()
+ {
+ return loadFilters(false);
+ }
+
+ /**
+ * Clears the bloom filters
+ */
+ protected void clear()
+ {
+ this.filterPermissions = null;
+ this.filterOwner = null;
+ }
+
+ /**
+ * @see org.picocontainer.Startable#start()
+ */
+ public void start()
+ {
+ try
+ {
+ this.cache.addListener(this);
+ }
+ catch (UnsupportedOperationException e)
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("The method addListener is not supported", e);
+ }
+ return;
+ }
+
+ loadFilters(true);
+ }
+
+ /**
+ * Loads the bloom filters
+ * @param cleanOnFail clean everything if an error occurs
+ * @return <code>true</code> if the filters could be loaded successfully,
<code>false</code> otherwise.
+ */
+ protected boolean loadFilters(boolean cleanOnFail)
+ {
+ filtersEnabled.set(false);
+ // TODO: Make it configurable
+ this.filterPermissions = new BloomFilter<String>(0.1d, 1000000);
+ this.filterOwner = new BloomFilter<String>(0.1d, 1000000);
+ boolean fails = true;
+ List<ACLHolder> holders = null;
+ try
+ {
+ LOG.info("Getting all the ACL Holders from the persistence layer");
+ holders = getACLHolders();
+ fails = false;
+ }
+ catch (UnsupportedOperationException e)
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("The method getACLHolders is not supported", e);
+ }
+ }
+ catch (RepositoryException e)
+ {
+ LOG.error("Could not load all the ACL loaders", e);
+ }
+ if (fails)
+ {
+ if (cleanOnFail)
+ {
+ clear();
+ cache.removeListener(this);
+ }
+ return false;
+ }
+ else if (holders != null && !holders.isEmpty())
+ {
+ LOG.info("Adding all the ACL Holders found into the BloomFilters");
+ for (int i = 0, length = holders.size(); i < length; i++)
+ {
+ ACLHolder holder = holders.get(i);
+ if (holder.hasOwner())
+ {
+ filterOwner.add(holder.getId());
+ }
+ if (holder.hasPermissions())
+ {
+ filterPermissions.add(holder.getId());
+ }
+ }
+ }
+ filtersEnabled.set(true);
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop()
+ {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onCacheEntryAdded(ItemData data)
+ {
+ onCacheEntryUpdated(data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onCacheEntryUpdated(ItemData data)
+ {
+ if (data instanceof NodeData)
+ {
+ NodeData node = (NodeData)data;
+ AccessControlList acl = node.getACL();
+ if (acl == null)
+ {
+ return;
+ }
+ if (acl.hasOwner())
+ {
+ filterOwner.add(node.getIdentifier());
+ }
+ if (acl.hasPermissions())
+ {
+ filterPermissions.add(node.getIdentifier());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onCacheEntryRemoved(ItemData data)
+ {
+ }
+
+ /**
+ * Defines what we are really looking for
+ */
+ private static class ACLSearch
+ {
+ private String owner;
+
+ private List<AccessControlEntry> permissions;
+
+ ACLSearch(String owner, List<AccessControlEntry> permissions)
+ {
+ this.owner = owner;
+ this.permissions = permissions;
+ }
+
+ /**
+ * @return <code>true</code> if the owner and the permission have been
found, <code>false</code>
+ * otherwise
+ */
+ public boolean found()
+ {
+ return owner != null && permissions != null;
+ }
+
+ /**
+ * @param owner the owner to set
+ */
+ public void setOwner(String owner)
+ {
+ if (this.owner == null)
+ {
+ this.owner = owner;
+ }
+ }
+
+ /**
+ * @param permissions the permissions to set
+ */
+ public void setPermissions(List<AccessControlEntry> permissions)
+ {
+ if (this.permissions == null)
+ {
+ this.permissions = permissions;
+ }
+ }
+
+ /**
+ * @return the owner
+ */
+ public boolean hasOwner()
+ {
+ return owner != null;
+ }
+
+ /**
+ * @return the permissions
+ */
+ public boolean hasPermissions()
+ {
+ return permissions != null;
+ }
+ }
}
\ No newline at end of file
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/LinkedWorkspaceStorageCacheImpl.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -24,6 +24,7 @@
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -2216,4 +2217,20 @@
public void addChildNodesByPage(NodeData parent, List<NodeData> childs, int
fromOrderNum)
{
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException("The cache listeners are not supported
by the LinkedWorkspaceStorageCacheImpl");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException("The cache listeners are not supported
by the LinkedWorkspaceStorageCacheImpl");
+ }
}
Added:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java
(rev 0)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/ShareableSupportedWorkspaceDataManager.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.dataflow.persistent;
+
+import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
+import org.exoplatform.services.jcr.dataflow.SharedDataManager;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.ItemType;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.util.Calendar;
+import java.util.List;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.RepositoryException;
+
+/**
+ * Created by The eXo Platform SAS. Data Manager supported ACL Inheritance
+ *
+ * @author Gennady Azarenkov
+ * @version $Id: ACLInheritanceSupportedWorkspaceDataManager.java 11907 2008-03-13
15:36:21Z ksm $
+ */
+public class ShareableSupportedWorkspaceDataManager implements SharedDataManager
+{
+
+ private static Log LOG =
ExoLogger.getLogger("exo.jcr.component.core.ACLInheritanceSupportedWorkspaceDataManager");
+
+ protected final CacheableWorkspaceDataManager persistentManager;
+
+ public ShareableSupportedWorkspaceDataManager(CacheableWorkspaceDataManager
persistentManager)
+ {
+ this.persistentManager = persistentManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ // ------------ ItemDataConsumer impl ------------
+ public List<NodeData> getChildNodesData(NodeData parent) throws
RepositoryException
+ {
+ return persistentManager.getChildNodesData(parent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<NodeData> getChildNodesData(NodeData parent,
List<QPathEntryFilter> patternFilters) throws RepositoryException
+ {
+ return persistentManager.getChildNodesData(parent, patternFilters);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int limit,
List<NodeData> childs)
+ throws RepositoryException
+ {
+ return persistentManager.getChildNodesDataByPage(parent, fromOrderNum, limit,
childs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getLastOrderNumber(final NodeData parent) throws RepositoryException
+ {
+ return persistentManager.getLastOrderNumber(parent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getChildNodesCount(final NodeData parent) throws RepositoryException
+ {
+ return persistentManager.getChildNodesCount(parent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemData getItemData(NodeData parent, QPathEntry name) throws
RepositoryException
+ {
+ return getItemData(parent, name, ItemType.UNKNOWN);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemData getItemData(NodeData parent, QPathEntry name, ItemType itemType)
throws RepositoryException
+ {
+ return persistentManager.getItemData(parent, name, itemType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemData getItemData(String identifier) throws RepositoryException
+ {
+ return persistentManager.getItemData(identifier);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<PropertyData> getChildPropertiesData(NodeData parent) throws
RepositoryException
+ {
+ return persistentManager.getChildPropertiesData(parent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<PropertyData> getChildPropertiesData(NodeData parent,
List<QPathEntryFilter> itemDataFilters)
+ throws RepositoryException
+ {
+ return persistentManager.getChildPropertiesData(parent, itemDataFilters);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<PropertyData> listChildPropertiesData(NodeData parent) throws
RepositoryException
+ {
+ return persistentManager.listChildPropertiesData(parent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<PropertyData> getReferencesData(String identifier, boolean
skipVersionStorage)
+ throws RepositoryException
+ {
+ return persistentManager.getReferencesData(identifier, skipVersionStorage);
+ }
+
+ // ------------ SharedDataManager ----------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void save(ItemStateChangesLog changes) throws InvalidItemStateException,
UnsupportedOperationException,
+ RepositoryException
+ {
+ persistentManager.save(changes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Calendar getCurrentTime()
+ {
+ return persistentManager.getCurrentTime();
+ }
+}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/VersionableWorkspaceDataManager.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -53,12 +53,12 @@
* @version $Id$
*/
-public class VersionableWorkspaceDataManager extends
ACLInheritanceSupportedWorkspaceDataManager
+public class VersionableWorkspaceDataManager extends
ShareableSupportedWorkspaceDataManager
{
private static Log log =
ExoLogger.getLogger("exo.jcr.component.core.VersionableWorkspaceDataManager");
- private ACLInheritanceSupportedWorkspaceDataManager versionDataManager;
+ private ShareableSupportedWorkspaceDataManager versionDataManager;
public VersionableWorkspaceDataManager(CacheableWorkspaceDataManager
persistentManager)
{
@@ -71,7 +71,7 @@
public void setSystemDataManager(DataManager systemDataManager)
{
- this.versionDataManager =
(ACLInheritanceSupportedWorkspaceDataManager)systemDataManager;
+ this.versionDataManager =
(ShareableSupportedWorkspaceDataManager)systemDataManager;
}
/**
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/infinispan/ISPNCacheWorkspaceStorageCache.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -28,6 +28,7 @@
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
import org.exoplatform.services.jcr.datamodel.IllegalPathException;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
@@ -53,6 +54,11 @@
import org.exoplatform.services.transaction.ActionNonTxAware;
import org.infinispan.Cache;
import org.infinispan.lifecycle.ComponentStatus;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import java.io.File;
import java.io.IOException;
@@ -66,6 +72,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
@@ -99,6 +106,11 @@
private final boolean enabled;
protected final BufferedISPNCache cache;
+
+ /**
+ * The list of all the listeners
+ */
+ private final List<WorkspaceStorageCacheListener> listeners = new
CopyOnWriteArrayList<WorkspaceStorageCacheListener>();
private final CacheActionNonTxAware<Void, Void> commitTransaction = new
CacheActionNonTxAware<Void, Void>()
{
@@ -462,6 +474,7 @@
// do n't nothing
}
this.cache = new BufferedISPNCache(parentCache, allowLocalChanges);
+ cache.addListener(new CacheEventListener());
}
/**
@@ -1560,6 +1573,68 @@
}
/**
+ * {@inheritDoc}
+ */
+ public void addListener(WorkspaceStorageCacheListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeListener(WorkspaceStorageCacheListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Called when a cache entry corresponding to the given node has item updated
+ * @param data the item corresponding to the updated cache entry
+ */
+ private void onCacheEntryUpdated(ItemData data)
+ {
+ if (data == null || data instanceof NullItemData)
+ {
+ return;
+ }
+ for (WorkspaceStorageCacheListener listener : listeners)
+ {
+ try
+ {
+ listener.onCacheEntryUpdated(data);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("The method onCacheEntryUpdated fails for the listener " +
listener.getClass(), e);
+ }
+ }
+ }
+
+ /**
+ * Called when a cache entry corresponding to the given item has been removed
+ * @param data the item corresponding to the removed cache entry
+ */
+ private void onCacheEntryRemoved(ItemData data)
+ {
+ if (data == null || data instanceof NullItemData)
+ {
+ return;
+ }
+ for (WorkspaceStorageCacheListener listener : listeners)
+ {
+ try
+ {
+ listener.onCacheEntryRemoved(data);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("The method onCacheEntryRemoved fails for the listener " +
listener.getClass(), e);
+ }
+ }
+ }
+
+ /**
* Actions that are not supposed to be called within a transaction
*
* Created by The eXo Platform SAS
@@ -1577,4 +1652,30 @@
return ISPNCacheWorkspaceStorageCache.this.getTransactionManager();
}
}
+
+ @SuppressWarnings("rawtypes")
+ @Listener
+ public class CacheEventListener
+ {
+
+ @CacheEntryRemoved
+ public void cacheEntryRemoved(CacheEntryRemovedEvent evt)
+ {
+ if (evt.isPre() && evt.getKey() instanceof CacheId)
+ {
+ final ItemData value = (ItemData)evt.getValue();
+ onCacheEntryRemoved(value);
+ }
+ }
+
+ @CacheEntryModified
+ public void cacheEntryModified(CacheEntryModifiedEvent evt)
+ {
+ if (!evt.isPre() && evt.getKey() instanceof CacheId)
+ {
+ final ItemData value = (ItemData)evt.getValue();
+ onCacheEntryUpdated(value);
+ }
+ }
+ }
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -29,6 +29,7 @@
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
import org.exoplatform.services.jcr.datamodel.IllegalPathException;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
@@ -62,6 +63,10 @@
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.eviction.ExpirationAlgorithmConfig;
import org.jboss.cache.jmx.JmxRegistrationManager;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
import org.picocontainer.Startable;
import java.io.File;
@@ -78,6 +83,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
@@ -186,7 +192,12 @@
protected final Fqn<String> childNodesByPatternList;
protected final Fqn<String> rootFqn;
-
+
+ /**
+ * The list of all the listeners
+ */
+ private final List<WorkspaceStorageCacheListener> listeners = new
CopyOnWriteArrayList<WorkspaceStorageCacheListener>();
+
private final CacheActionNonTxAware<Void, Void> commitTransaction = new
CacheActionNonTxAware<Void, Void>()
{
@Override
@@ -680,7 +691,8 @@
createResidentNode(childPropsByPatternList);
createResidentNode(childNodesByPatternList);
createResidentNode(itemsRoot);
-
+ this.cache.addCacheListener(new CacheEventListener());
+
if (jmxManager != null)
{
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
@@ -2079,8 +2091,69 @@
createResidentNode(itemsRoot);
}
}
+ /**
+ * {@inheritDoc}
+ */
+ public void addListener(WorkspaceStorageCacheListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeListener(WorkspaceStorageCacheListener listener)
+ {
+ listeners.remove(listener);
+ }
/**
+ * Called when a cache entry corresponding to the given node has item updated
+ * @param data the item corresponding to the updated cache entry
+ */
+ private void onCacheEntryUpdated(ItemData data)
+ {
+ if (data == null || data instanceof NullItemData)
+ {
+ return;
+ }
+ for (WorkspaceStorageCacheListener listener : listeners)
+ {
+ try
+ {
+ listener.onCacheEntryUpdated(data);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("The method onCacheEntryUpdated fails for the listener " +
listener.getClass(), e);
+ }
+ }
+ }
+
+ /**
+ * Called when a cache entry corresponding to the given item has been removed
+ * @param data the item corresponding to the removed cache entry
+ */
+ private void onCacheEntryRemoved(ItemData data)
+ {
+ if (data == null || data instanceof NullItemData)
+ {
+ return;
+ }
+ for (WorkspaceStorageCacheListener listener : listeners)
+ {
+ try
+ {
+ listener.onCacheEntryRemoved(data);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("The method onCacheEntryRemoved fails for the listener " +
listener.getClass(), e);
+ }
+ }
+ }
+
+ /**
* Actions that are not supposed to be called within a transaction
*
* Created by The eXo Platform SAS
@@ -2098,4 +2171,30 @@
return JBossCacheWorkspaceStorageCache.this.getTransactionManager();
}
}
+
+ @org.jboss.cache.notifications.annotation.CacheListener
+ @SuppressWarnings("unchecked")
+ public class CacheEventListener
+ {
+
+ @NodeRemoved
+ public void nodeRemoved(NodeRemovedEvent ne)
+ {
+ if (ne.isPre() && ne.getFqn().isChildOf(itemsRoot))
+ {
+ final Map<Serializable, Object> data = ne.getData();
+ onCacheEntryRemoved((ItemData)(data == null ? null : data.get(ITEM_DATA)));
+ }
+ }
+
+ @NodeModified
+ public void nodeModified(NodeModifiedEvent ne)
+ {
+ if (!ne.isPre() && ne.getFqn().isChildOf(itemsRoot))
+ {
+ final Map<Serializable, Object> data = ne.getData();
+ onCacheEntryUpdated((ItemData)(data == null ? null : data.get(ITEM_DATA)));
+ }
+ }
+ }
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/inmemory/InmemoryStorageConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -26,6 +26,7 @@
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
@@ -289,6 +290,15 @@
throw new UnsupportedOperationException();
}
+
+ /**
+ * @see
org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException();
+ }
class MapKey
{
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/JDBCStorageConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -37,6 +37,7 @@
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import
org.exoplatform.services.jcr.impl.dataflow.persistent.ByteArrayPersistedValueData;
import
org.exoplatform.services.jcr.impl.dataflow.persistent.CleanableFilePersistedValueData;
import org.exoplatform.services.jcr.impl.dataflow.persistent.StreamPersistedValueData;
@@ -1228,6 +1229,15 @@
throw new RepositoryException(e);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException("This method is not supported by the
old JDBCWorkspaceDataContainer, use CQJDBCWorkspaceDataContainer instead.");
+ }
/**
* {@inheritDoc}
@@ -1500,7 +1510,7 @@
}
QPathEntry qpe =
- new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)),
parent.getInt(COLUMN_INDEX));
+ new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)),
parent.getInt(COLUMN_INDEX), caid);
qrpath.add(qpe);
caid = parent.getString(COLUMN_PARENTID);
}
@@ -2213,7 +2223,7 @@
if (parentPath != null)
{
// get by parent and name
- qpath = QPath.makeChildPath(parentPath, qname, cindex);
+ qpath = QPath.makeChildPath(parentPath, qname, cindex, cid);
parentCid = cpid;
}
else
@@ -2227,7 +2237,7 @@
}
else
{
- qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex);
+ qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex, cid);
parentCid = cpid;
}
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/CQJDBCStorageConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -20,6 +20,7 @@
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.AccessControlList;
+import org.exoplatform.services.jcr.core.ExtendedPropertyType;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
@@ -33,6 +34,7 @@
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import org.exoplatform.services.jcr.impl.dataflow.persistent.StreamPersistedValueData;
import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
@@ -133,6 +135,13 @@
*/
protected String UPDATE_VALUE;
+ /**
+ * FIND_ACL_HOLDERS.
+ */
+ protected String FIND_ACL_HOLDERS;
+
+ protected PreparedStatement findACLHolders;
+
protected PreparedStatement findNodesByParentIdCQ;
protected PreparedStatement findPropertiesByParentIdCQ;
@@ -177,7 +186,64 @@
{
super(dbConnection, readOnly, containerName, valueStorageProvider, maxBufferSize,
swapDirectory, swapCleaner);
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ checkIfOpened();
+ ResultSet resultSet = null;
+ try
+ {
+ // query will return all the ACL holder
+ resultSet = findACLHolders();
+ Map<String, ACLHolder> mHolders = new HashMap<String, ACLHolder>();
+
+ while (resultSet.next())
+ {
+ String cpid = resultSet.getString(COLUMN_PARENTID);
+ ACLHolder holder = mHolders.get(cpid);
+ if (holder == null)
+ {
+ holder = new ACLHolder(cpid);
+ mHolders.put(cpid, holder);
+ }
+ int cptype = resultSet.getInt(COLUMN_PTYPE);
+ if (cptype == ExtendedPropertyType.PERMISSION)
+ {
+ holder.setPermissions(true);
+ }
+ else
+ {
+ holder.setOwner(true);
+ }
+ }
+ return new ArrayList<ACLHolder>(mHolders.values());
+ }
+ catch (SQLException e)
+ {
+ throw new RepositoryException(e);
+ }
+ finally
+ {
+ if (resultSet != null)
+ {
+ try
+ {
+ resultSet.close();
+ }
+ catch (SQLException e)
+ {
+ LOG.error("Can't close the ResultSet: " + e);
+ }
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -928,7 +994,7 @@
if (parentPath != null)
{
// get by parent and name
- qpath = QPath.makeChildPath(parentPath, qname, cindex);
+ qpath = QPath.makeChildPath(parentPath, qname, cindex, cid);
parentCid = cpid;
}
else
@@ -942,7 +1008,7 @@
}
else
{
- qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex);
+ qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex, cid);
parentCid = cpid;
}
}
@@ -1094,13 +1160,13 @@
}
QPathEntry qpe1 =
- new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)),
result.getInt(COLUMN_INDEX));
+ new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)),
result.getInt(COLUMN_INDEX), result.getString(COLUMN_ID));
boolean isChild = caid.equals(result.getString(COLUMN_ID));
caid = result.getString(COLUMN_PARENTID);
if (result.next())
{
QPathEntry qpe2 =
- new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)),
result.getInt(COLUMN_INDEX));
+ new QPathEntry(InternalQName.parse(result.getString(COLUMN_NAME)),
result.getInt(COLUMN_INDEX), result.getString(COLUMN_ID));
if (isChild)
{
// The child is the first result then we have the parent
@@ -1166,6 +1232,11 @@
try
{
+ if (findACLHolders != null)
+ {
+ findACLHolders.close();
+ }
+
if (findNodesByParentIdCQ != null)
{
findNodesByParentIdCQ.close();
@@ -1222,6 +1293,8 @@
}
}
+ protected abstract ResultSet findACLHolders() throws SQLException;
+
protected abstract ResultSet findItemQPathByIdentifierCQ(String identifier) throws
SQLException;
protected abstract ResultSet findChildNodesByParentIdentifierCQ(String
parentIdentifier) throws SQLException;
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -217,6 +217,10 @@
+ "
P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner'
or"
+ "
P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
+ " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID";
+
+ FIND_ACL_HOLDERS = "select I.PARENT_ID, I.P_TYPE" +
+ " from JCR_MITEM I" +
+ " where I.I_CLASS=2 and
(
I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or
I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')";
}
/**
@@ -1010,4 +1014,18 @@
{
return PATTERN_ESCAPE_STRING;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ResultSet findACLHolders() throws SQLException
+ {
+ if (findACLHolders == null)
+ {
+ findACLHolders = dbConnection.prepareStatement(FIND_ACL_HOLDERS);
+ }
+
+ return findACLHolders.executeQuery();
+ }
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/SingleDbJDBCConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -222,6 +222,10 @@
+ "
P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner'
or"
+ "
P.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')"
+ " and V.PROPERTY_ID=P.ID order by I.N_ORDER_NUM, I.ID";
+
+ FIND_ACL_HOLDERS = "select I.PARENT_ID, I.P_TYPE" +
+ " from JCR_SITEM I" +
+ " where I.I_CLASS=2 and I.CONTAINER_NAME=? and
(
I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]owner' or
I.NAME='[http://www.exoplatform.com/jcr/exo/1.0]permissions')";
}
/**
@@ -923,4 +927,24 @@
{
return PATTERN_ESCAPE_STRING;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ResultSet findACLHolders() throws SQLException
+ {
+ if (findACLHolders == null)
+ {
+ findACLHolders = dbConnection.prepareStatement(FIND_ACL_HOLDERS);
+ }
+ else
+ {
+ findACLHolders.clearParameters();
+ }
+
+ findACLHolders.setString(1, containerName);
+
+ return findACLHolders.executeQuery();
+ }
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -22,6 +22,7 @@
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
import org.exoplatform.services.jcr.statistics.JCRStatisticsManager;
import org.exoplatform.services.jcr.statistics.Statistics;
@@ -128,6 +129,12 @@
* <code>getLastOrderNumber(NodeData parent)</code>
*/
private static final String GET_LAST_ORDER_NUMBER_DESCR =
"getLastOrderNumber";
+
+ /**
+ * The description of the statistics corresponding to the method
+ * <code>getACLHolders()</code>
+ */
+ private static final String GET_ACL_HOLDERS = "getACLHolders";
/**
* The description of the statistics corresponding to the method
@@ -192,6 +199,7 @@
ALL_STATISTICS.put(LIST_CHILD_PROPERTIES_DATA_DESCR, new
Statistics(GLOBAL_STATISTICS,
LIST_CHILD_PROPERTIES_DATA_DESCR));
ALL_STATISTICS.put(GET_REFERENCES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS,
GET_REFERENCES_DATA_DESCR));
+ ALL_STATISTICS.put(GET_ACL_HOLDERS, new Statistics(GLOBAL_STATISTICS,
GET_ACL_HOLDERS));
// Write Methods
// Commit
ALL_STATISTICS.put(COMMIT_DESCR, new Statistics(GLOBAL_STATISTICS, COMMIT_DESCR));
@@ -617,4 +625,23 @@
s.end();
}
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ Statistics s = ALL_STATISTICS.get(GET_ACL_HOLDERS);
+ try
+ {
+ s.begin();
+ return wcs.getACLHolders();
+ }
+ finally
+ {
+ s.end();
+ }
+ }
}
Modified:
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/storage/WorkspaceStorageConnection.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -24,6 +24,7 @@
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import java.util.List;
@@ -387,4 +388,17 @@
* @return boolean, true if connection is open and ready, false - otherwise
*/
boolean isOpened();
+
+ /**
+ * Returns all the nodes that hold some ACL info like owner or permissions
+ *
+ * @return a list of all the ACL holders for this workspace
+ * @throws RepositoryException
+ * if some exception occured
+ * @throws IllegalStateException
+ * if connection is closed
+ * @throws UnsupportedOperationException
+ * if operation is not supported
+ */
+ List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException, UnsupportedOperationException;
}
Modified:
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestCacheableWorkspaceDataManager.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -22,6 +22,7 @@
import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache;
+import org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
@@ -439,6 +440,20 @@
{
}
+ /**
+ * @see
org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache#addListener(org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener)
+ */
+ public void addListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException
+ {
+ }
+
+ /**
+ * @see
org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCache#removeListener(org.exoplatform.services.jcr.dataflow.persistent.WorkspaceStorageCacheListener)
+ */
+ public void removeListener(WorkspaceStorageCacheListener listener) throws
UnsupportedOperationException
+ {
+ }
+
}
private static class MyWorkspaceStorageConnection implements
WorkspaceStorageConnection
@@ -599,6 +614,15 @@
return null;
}
+ /**
+ * @see
org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ return null;
+ }
+
};
private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase
Modified:
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/TestWorkspaceStorageCacheInClusterMode.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -949,8 +949,15 @@
return children;
}
-
-
+
+ /**
+ * @see
org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+ */
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ return null;
+ }
};
private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase
Modified:
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java
===================================================================
---
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java 2011-08-17
14:16:40 UTC (rev 4769)
+++
jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCache.java 2011-08-18
06:39:44 UTC (rev 4770)
@@ -34,6 +34,7 @@
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import
org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager;
import
org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspaceStorageCacheBaseCase;
import
org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache;
@@ -315,6 +316,17 @@
return getChildNodesData(parent);
}
+
+ /**
+ * @see
org.exoplatform.services.jcr.storage.WorkspaceStorageConnection#getACLHolders()
+ */
+ @Override
+ public List<ACLHolder> getACLHolders() throws RepositoryException,
IllegalStateException,
+ UnsupportedOperationException
+ {
+ return null;
+ }
+
};
private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase