[exo-jcr-commits] exo-jcr SVN: r4265 - in jcr/branches/1.12.x: exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache and 4 other directories.
do-not-reply at jboss.org
do-not-reply at jboss.org
Thu Apr 21 07:38:58 EDT 2011
Author: paristote
Date: 2011-04-21 07:38:57 -0400 (Thu, 21 Apr 2011)
New Revision: 4265
Added:
jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCacheInClusterMode.java
jcr/branches/1.12.x/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-jbosscache-data-no-mux.xml
jcr/branches/1.12.x/patch/1.12.9-GA/JCR-1604/readme.txt
Modified:
jcr/branches/1.12.x/exo.jcr.component.core/pom.xml
jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java
jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/search/TestSearch.java
Log:
JCR-1604
What is the problem to fix?
JCR addNode within a transaction causes javax.transaction.HeuristicMixedException (internally, org.exoplatform.services.transaction.TransactionException caused by org.jboss.cache.lock.TimeoutException) in the first access to the node
How is the problem fixed?
Perform loading data in cache outside the current transaction
Modified: jcr/branches/1.12.x/exo.jcr.component.core/pom.xml
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/pom.xml 2011-04-20 14:29:25 UTC (rev 4264)
+++ jcr/branches/1.12.x/exo.jcr.component.core/pom.xml 2011-04-21 11:38:57 UTC (rev 4265)
@@ -383,11 +383,26 @@
<name>emma.coverage.out.file</name>
<value>target/emma/coverage.ec</value>
</property>
- <!-- Uncomment the line below if you want to enable the statistics -->
- <!--property>
- <name>JDBCWorkspaceDataContainer.statistics.enabled</name>
- <value>true</value>
- </property-->
+ <!-- Uncomment the line below if you want to enable the statistics -->
+ <!--property>
+ <name>JDBCWorkspaceDataContainer.statistics.enabled</name>
+ <value>true</value>
+ </property-->
+ <!-- We add this system property due to some incompatibility between IPv6 and
+ some JVM of Linux distributions such as Ubuntu and Fedora-->
+ <property>
+ <name>java.net.preferIPv4Stack</name>
+ <value>true</value>
+ </property>
+ <!-- Avoid the firewall -->
+ <property>
+ <name>bind.address</name>
+ <value>127.0.0.1</value>
+ </property>
+ <property>
+ <name>jgroups.stack</name>
+ <value>udp</value>
+ </property>
</systemProperties>
<includes>
<include>org/exoplatform/services/jcr/api/**/Test*.java</include>
@@ -575,6 +590,21 @@
<name>JDBCWorkspaceDataContainer.statistics.enabled</name>
<value>true</value>
</property-->
+ <!-- We add this system property due to some incompatibility between IPv6 and
+ some JVM of Linux distributions such as Ubuntu and Fedora-->
+ <property>
+ <name>java.net.preferIPv4Stack</name>
+ <value>true</value>
+ </property>
+ <!-- Avoid the firewall -->
+ <property>
+ <name>bind.address</name>
+ <value>127.0.0.1</value>
+ </property>
+ <property>
+ <name>jgroups.stack</name>
+ <value>udp</value>
+ </property>
</systemProperties>
<includes>
<include>org/apache/jackrabbit/test/api/*Test.java</include>
@@ -647,6 +677,21 @@
<name>JDBCWorkspaceDataContainer.statistics.enabled</name>
<value>true</value>
</property-->
+ <!-- We add this system property due to some incompatibility between IPv6 and
+ some JVM of Linux distributions such as Ubuntu and Fedora-->
+ <property>
+ <name>java.net.preferIPv4Stack</name>
+ <value>true</value>
+ </property>
+ <!-- Avoid the firewall -->
+ <property>
+ <name>bind.address</name>
+ <value>127.0.0.1</value>
+ </property>
+ <property>
+ <name>jgroups.stack</name>
+ <value>udp</value>
+ </property>
</systemProperties>
<includes>
<!-- From default tests -->
@@ -725,6 +770,21 @@
<name>JDBCWorkspaceDataContainer.statistics.enabled</name>
<value>true</value>
</property-->
+ <!-- We add this system property due to some incompatibility between IPv6 and
+ some JVM of Linux distributions such as Ubuntu and Fedora-->
+ <property>
+ <name>java.net.preferIPv4Stack</name>
+ <value>true</value>
+ </property>
+ <!-- Avoid the firewall -->
+ <property>
+ <name>bind.address</name>
+ <value>127.0.0.1</value>
+ </property>
+ <property>
+ <name>jgroups.stack</name>
+ <value>udp</value>
+ </property>
</systemProperties>
<includes>
<include>**/**/reading_/Test*.java</include>
Modified: jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java 2011-04-20 14:29:25 UTC (rev 4264)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/BufferedJBossCache.java 2011-04-21 11:38:57 UTC (rev 4265)
@@ -40,6 +40,7 @@
import java.util.Map;
import java.util.Set;
+import javax.transaction.Status;
import javax.transaction.TransactionManager;
/**
@@ -64,6 +65,8 @@
private final boolean useExpiration;
private final long expirationTimeOut;
+
+ private final TransactionManager tm;
protected static final Log LOG =
ExoLogger.getLogger("org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.BufferedJBossCache");
@@ -71,6 +74,7 @@
public BufferedJBossCache(Cache<Serializable, Object> parentCache, boolean useExpiration, long expirationTimeOut)
{
super();
+ this.tm = ((CacheSPI<Serializable, Object>)parentCache).getTransactionManager();
this.parentCache = parentCache;
this.useExpiration = useExpiration;
this.expirationTimeOut = expirationTimeOut;
@@ -109,7 +113,53 @@
//log.info("After=" + changesContainer.toString());
for (ChangesContainer cacheChange : containers)
{
- cacheChange.apply();
+ boolean isTxCreated = false;
+ try
+ {
+ if (cacheChange.isTxRequired() && tm != null && tm.getStatus() == Status.STATUS_NO_TRANSACTION)
+ {
+ // No tx exists so we create a new tx
+ if (LOG.isTraceEnabled()) LOG.trace("No Tx is active we then create a new tx");
+ tm.begin();
+ isTxCreated = true;
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Could not create a new tx", e);
+ }
+ try
+ {
+ cacheChange.apply();
+ }
+ catch (RuntimeException e)
+ {
+ if (isTxCreated)
+ {
+ try
+ {
+ if (LOG.isTraceEnabled()) LOG.trace("An error occurs the tx will be rollbacked");
+ tm.rollback();
+ }
+ catch (Exception e1)
+ {
+ LOG.warn("Could not rollback the tx", e1);
+ }
+ }
+ throw e;
+ }
+ if (isTxCreated)
+ {
+ try
+ {
+ if (LOG.isTraceEnabled()) LOG.trace("The tx will be committed");
+ tm.commit();
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Could not commit the tx", e);
+ }
+ }
}
}
finally
@@ -118,6 +168,7 @@
changesContainer = null;
}
}
+
/**
* Tries to get buffer and if it is null throws an exception otherwise returns buffer.
@@ -471,6 +522,31 @@
return parentCache.get(fqn, key);
}
+ /**
+ * in case putIfAbsent is set to <code>true</code> this method will call cache.putIfAbsent(Fqn fqn, Serializable key, Object value)
+ * otherwise it will call cache.put(Fqn fqn, Serializable key, Object value)
+ */
+ protected Object put(Fqn fqn, Serializable key, Object value, boolean putIfAbsent)
+ {
+ if (putIfAbsent)
+ {
+ putIfAbsent(fqn, key, value);
+ return null;
+ }
+ return put(fqn, key, value);
+ }
+
+ /**
+ * This method will create and add a ChangesContainer that will put the value only if no value has been added
+ */
+ protected Object putIfAbsent(Fqn fqn, Serializable key, Object value)
+ {
+ CompressedChangesBuffer changesContainer = getChangesBufferSafe();
+ changesContainer.add(new PutIfAbsentKeyValueContainer(fqn, key, value, parentCache, changesContainer
+ .getHistoryIndex(), local.get(), useExpiration, expirationTimeOut));
+ return null;
+ }
+
public Object putInBuffer(Fqn fqn, Serializable key, Object value)
{
CompressedChangesBuffer changesContainer = getChangesBufferSafe();
@@ -758,6 +834,11 @@
}
public abstract void apply();
+
+ public boolean isTxRequired()
+ {
+ return false;
+ }
}
/**
@@ -789,6 +870,48 @@
}
/**
+ * PutIfAbsent container.
+ */
+ public static class PutIfAbsentKeyValueContainer extends ChangesContainer
+ {
+ private final Serializable key;
+
+ private final Object value;
+
+ public PutIfAbsentKeyValueContainer(Fqn fqn, Serializable key, Object value, Cache<Serializable, Object> cache,
+ int historicalIndex, boolean local, boolean useExpiration, long timeOut)
+ {
+ super(fqn, ChangesType.PUT_KEY, cache, historicalIndex, local, useExpiration, timeOut);
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public void apply()
+ {
+ cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
+ if (cache.get(fqn, key) != null)
+ {
+ // skip
+ return;
+ }
+ if (useExpiration)
+ {
+ putExpiration(fqn);
+ }
+
+ setCacheLocalMode();
+ cache.put(fqn, key, value);
+ }
+
+ @Override
+ public boolean isTxRequired()
+ {
+ return true;
+ }
+ }
+
+ /**
* Put container.
*/
public static class PutKeyValueContainer extends ChangesContainer
@@ -871,6 +994,12 @@
+ existingObject.getClass().getName());
}
}
+
+ @Override
+ public boolean isTxRequired()
+ {
+ return true;
+ }
}
/**
@@ -913,6 +1042,12 @@
cache.put(fqn, key, newSet);
}
}
+
+ @Override
+ public boolean isTxRequired()
+ {
+ return false;
+ }
}
/**
Modified: jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java 2011-04-20 14:29:25 UTC (rev 4264)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java 2011-04-21 11:38:57 UTC (rev 4265)
@@ -60,6 +60,7 @@
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
+import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
/**
@@ -417,7 +418,7 @@
cache.setLocal(false);
if (!inTransaction)
{
- cache.commitTransaction();
+ dedicatedTxCommit();
}
}
}
@@ -556,23 +557,20 @@
cache.setLocal(true);
- // remove previous all
- cache.removeNode(makeRefFqn(identifier));
-
Set<Object> set = new HashSet<Object>();
for (PropertyData prop : refProperties)
{
putProperty(prop, ModifyChildOption.NOT_MODIFY);
set.add(prop.getIdentifier());
}
- cache.put(makeRefFqn(identifier), ITEM_LIST, set);
+ cache.putIfAbsent(makeRefFqn(identifier), ITEM_LIST, set);
}
finally
{
cache.setLocal(false);
if (!inTransaction)
{
- cache.commitTransaction();
+ dedicatedTxCommit();
}
}
}
@@ -592,8 +590,6 @@
}
cache.setLocal(true);
- // remove previous all (to be sure about consistency)
- cache.removeNode(makeChildListFqn(childNodesList, parent.getIdentifier()));
if (childs.size() > 0)
{
@@ -603,12 +599,13 @@
putNode(child, ModifyChildOption.NOT_MODIFY);
set.add(child.getIdentifier());
}
- cache.put(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST, set);
+ cache.putIfAbsent(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST, set);
}
else
{
// cache fact of empty childs list
- cache.put(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST, new HashSet<Object>());
+ cache.putIfAbsent(makeChildListFqn(childNodesList, parent.getIdentifier()), ITEM_LIST,
+ new HashSet<Object>());
}
}
finally
@@ -616,7 +613,7 @@
cache.setLocal(false);
if (!inTransaction)
{
- cache.commitTransaction();
+ dedicatedTxCommit();
}
}
}
@@ -634,8 +631,6 @@
cache.beginTransaction();
}
cache.setLocal(true);
- // remove previous all (to be sure about consistency)
- cache.removeNode(makeChildListFqn(childPropsList, parent.getIdentifier()));
if (childs.size() > 0)
{
// add all new
@@ -645,7 +640,7 @@
putProperty(child, ModifyChildOption.NOT_MODIFY);
set.add(child.getIdentifier());
}
- cache.put(makeChildListFqn(childPropsList, parent.getIdentifier()), ITEM_LIST, set);
+ cache.putIfAbsent(makeChildListFqn(childPropsList, parent.getIdentifier()), ITEM_LIST, set);
}
else
@@ -658,7 +653,7 @@
cache.setLocal(false);
if (!inTransaction)
{
- cache.commitTransaction();
+ dedicatedTxCommit();
}
}
}
@@ -669,17 +664,6 @@
public void addChildPropertiesList(NodeData parent, List<PropertyData> childProperties)
{
// TODO not implemented, will force read from DB
- // try
- // {
- // cache.beginTransaction();
- // cache.setLocal(true);
- //
- // }
- // finally
- // {
- // cache.setLocal(false);
- // cache.commitTransaction();
- // }
}
/**
@@ -945,18 +929,19 @@
if (node.getParentIdentifier() != null)
{
// add in CHILD_NODES
- cache.put(
- makeChildFqn(childNodes, node.getParentIdentifier(), node.getQPath().getEntries()[node.getQPath()
- .getEntries().length - 1]), ITEM_ID, node.getIdentifier());
+ cache.put(makeChildFqn(childNodes, node.getParentIdentifier(), node.getQPath().getEntries()[node.getQPath()
+ .getEntries().length - 1]), ITEM_ID, node.getIdentifier(),
+ modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
if (modifyListsOfChild != ModifyChildOption.NOT_MODIFY)
{
- cache.addToList(makeChildListFqn(childNodesList, node.getParentIdentifier()), ITEM_LIST,
- node.getIdentifier(), modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
+ cache.addToList(makeChildListFqn(childNodesList, node.getParentIdentifier()), ITEM_LIST, node
+ .getIdentifier(), modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
}
}
// add in ITEMS
- return (ItemData)cache.put(makeItemFqn(node.getIdentifier()), ITEM_DATA, node);
+ return (ItemData)cache.put(makeItemFqn(node.getIdentifier()), ITEM_DATA, node,
+ modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
}
protected ItemData putNodeInBufferedCache(NodeData node, ModifyChildOption modifyListsOfChild)
@@ -965,14 +950,13 @@
if (node.getParentIdentifier() != null)
{
// add in CHILD_NODES
- cache.put(
- makeChildFqn(childNodes, node.getParentIdentifier(), node.getQPath().getEntries()[node.getQPath()
- .getEntries().length - 1]), ITEM_ID, node.getIdentifier());
+ cache.put(makeChildFqn(childNodes, node.getParentIdentifier(), node.getQPath().getEntries()[node.getQPath()
+ .getEntries().length - 1]), ITEM_ID, node.getIdentifier());
if (modifyListsOfChild != ModifyChildOption.NOT_MODIFY)
{
- cache.addToList(makeChildListFqn(childNodesList, node.getParentIdentifier()), ITEM_LIST,
- node.getIdentifier(), modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
+ cache.addToList(makeChildListFqn(childNodesList, node.getParentIdentifier()), ITEM_LIST, node
+ .getIdentifier(), modifyListsOfChild == ModifyChildOption.FORCE_MODIFY);
}
}
// add in ITEMS
@@ -988,9 +972,8 @@
protected PropertyData putProperty(PropertyData prop, ModifyChildOption modifyListsOfChild)
{
// add in CHILD_PROPS
- cache.put(
- makeChildFqn(childProps, prop.getParentIdentifier(),
- prop.getQPath().getEntries()[prop.getQPath().getEntries().length - 1]), ITEM_ID, prop.getIdentifier());
+ cache.put(makeChildFqn(childProps, prop.getParentIdentifier(), prop.getQPath().getEntries()[prop.getQPath()
+ .getEntries().length - 1]), ITEM_ID, prop.getIdentifier(), modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
if (modifyListsOfChild != ModifyChildOption.NOT_MODIFY)
{
@@ -1025,7 +1008,8 @@
}
// add in ITEMS
- return (PropertyData)cache.put(makeItemFqn(prop.getIdentifier()), ITEM_DATA, prop);
+ return (PropertyData)cache.put(makeItemFqn(prop.getIdentifier()), ITEM_DATA, prop,
+ modifyListsOfChild == ModifyChildOption.NOT_MODIFY);
}
protected void removeItem(ItemData item)
@@ -1041,8 +1025,8 @@
.getQPath().getEntries().length - 1]));
// remove from CHILD_NODES_LIST of parent
- cache.removeFromList(makeChildListFqn(childNodesList, item.getParentIdentifier()), ITEM_LIST,
- item.getIdentifier());
+ cache.removeFromList(makeChildListFqn(childNodesList, item.getParentIdentifier()), ITEM_LIST, item
+ .getIdentifier());
// remove from CHILD_NODES as parent
cache.removeNode(makeChildListFqn(childNodes, item.getIdentifier()));
@@ -1066,8 +1050,8 @@
.getQPath().getEntries().length - 1]));
// remove from CHILD_PROPS_LIST
- cache.removeFromList(makeChildListFqn(childPropsList, item.getParentIdentifier()), ITEM_LIST,
- item.getIdentifier());
+ cache.removeFromList(makeChildListFqn(childPropsList, item.getParentIdentifier()), ITEM_LIST, item
+ .getIdentifier());
}
// remove from ITEMS
cache.removeNode(makeItemFqn(item.getIdentifier()));
@@ -1096,10 +1080,10 @@
}
/**
- * Update Node hierachy in case of same-name siblings reorder.
- * Assumes the new (updated) nodes already putted in the cache. Previous name of updated nodes will be calculated
- * and that node will be deleted (if has same id as the new node). Childs paths will be updated to a new node path.
- *
+ * Update Node hierarchy in case of same-name siblings reorder.
+ * Assumes the new (updated) nodes already put in the cache. Previous name of updated nodes will be calculated
+ * and that node will be deleted (if has same id as the new node). Children paths will be updated to a new node path. *
+ *
* @param node NodeData
* @param prevNode NodeData
*/
@@ -1205,9 +1189,9 @@
NodeData prevNode = (NodeData)data;
TransientNodeData newNode =
- new TransientNodeData(newPath, prevNode.getIdentifier(), prevNode.getPersistedVersion(),
- prevNode.getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(),
- prevNode.getParentIdentifier(), inheritACL ? acl : prevNode.getACL()); // TODO check ACL
+ new TransientNodeData(newPath, prevNode.getIdentifier(), prevNode.getPersistedVersion(), prevNode
+ .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);
}
@@ -1225,8 +1209,8 @@
}
TransientPropertyData newProp =
- new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(),
- prevProp.getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
+ new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(), prevProp
+ .getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
cache.put(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
}
}
@@ -1260,8 +1244,8 @@
QPath
.makeChildPath(rootPath, prevProp.getQPath().getEntries()[prevProp.getQPath().getEntries().length - 1]);
TransientPropertyData newProp =
- new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(),
- prevProp.getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
+ new TransientPropertyData(newPath, prevProp.getIdentifier(), prevProp.getPersistedVersion(), prevProp
+ .getType(), prevProp.getParentIdentifier(), prevProp.isMultiValued(), prevProp.getValues());
cache.put(makeItemFqn(newProp.getIdentifier()), ITEM_DATA, newProp);
}
@@ -1274,9 +1258,9 @@
QPath
.makeChildPath(rootPath, prevNode.getQPath().getEntries()[prevNode.getQPath().getEntries().length - 1]);
TransientNodeData newNode =
- new TransientNodeData(newPath, prevNode.getIdentifier(), prevNode.getPersistedVersion(),
- prevNode.getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(),
- prevNode.getParentIdentifier(), inheritACL ? acl : prevNode.getACL()); // TODO check ACL
+ new TransientNodeData(newPath, prevNode.getIdentifier(), prevNode.getPersistedVersion(), prevNode
+ .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);
// update childs recursive
@@ -1292,22 +1276,22 @@
*/
protected void updateChildsACL(final String parentId, final AccessControlList acl)
{
- for (Iterator<NodeData> iter = new ChildNodesIterator<NodeData>(parentId); iter.hasNext();)
+ loop : for (Iterator<NodeData> iter = new ChildNodesIterator<NodeData>(parentId); iter.hasNext();)
{
NodeData prevNode = iter.next();
- // is ACL changes on this node (i.e. ACL inheritance brokes)
+ // is ACL changes on this node (i.e. ACL inheritance broken)
for (InternalQName mixin : prevNode.getMixinTypeNames())
{
if (mixin.equals(Constants.EXO_PRIVILEGEABLE) || mixin.equals(Constants.EXO_OWNEABLE))
{
- continue;
+ continue loop;
}
}
// recreate with new path for child Nodes only
TransientNodeData newNode =
new TransientNodeData(prevNode.getQPath(), prevNode.getIdentifier(), prevNode.getPersistedVersion(),
- prevNode.getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(),
- prevNode.getParentIdentifier(), acl);
+ prevNode.getPrimaryTypeName(), prevNode.getMixinTypeNames(), prevNode.getOrderNumber(), prevNode
+ .getParentIdentifier(), acl);
// update this node
cache.put(makeItemFqn(newNode.getIdentifier()), ITEM_DATA, newNode);
// update childs recursive
@@ -1347,4 +1331,44 @@
NOT_MODIFY, MODIFY, FORCE_MODIFY
}
+ /**
+ * Allows to commit the cache changes in a dedicated XA Tx in order to avoid potential
+ * deadlocks
+ */
+ private void dedicatedTxCommit()
+ {
+ // Ensure that the commit is done in a dedicated tx to avoid deadlock due
+ // to global XA Tx
+ TransactionManager tm = getTransactionManager();
+ Transaction tx = null;
+ try
+ {
+ if (tm != null)
+ {
+ try
+ {
+ tx = tm.suspend();
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Cannot suspend the current transaction", e);
+ }
+ }
+ cache.commitTransaction();
+ }
+ finally
+ {
+ if (tx != null)
+ {
+ try
+ {
+ tm.resume(tx);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Cannot resume the current transaction", e);
+ }
+ }
+ }
+ }
}
Modified: jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/search/TestSearch.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/search/TestSearch.java 2011-04-20 14:29:25 UTC (rev 4264)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/search/TestSearch.java 2011-04-21 11:38:57 UTC (rev 4265)
@@ -71,6 +71,60 @@
return ntFile;
}
+ public void testSpecialCharacters() throws Exception
+ {
+ Node queryNode = testNode.addNode("node-testSpecialCharacters");
+ String[] specialChar = {"a", "+", "-", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^","\"", "~", "*", "?", ":", "\\", "&", "$", "@", "%", "|", "(1+1):2", "\\(1\\+1\\)\\:2"};
+ StringBuilder buffer = new StringBuilder();
+ for (String sChar : specialChar)
+ {
+ buffer.append(' ').append(sChar);
+ }
+ queryNode.setProperty("full-content", buffer.toString());
+ session.save();
+ for (String sChar : specialChar)
+ {
+ QueryManager manager = session.getWorkspace().getQueryManager();
+ String sqlQuery = "SELECT * FROM nt:base WHERE jcr:path LIKE '" + testNode.getPath() + "/%' and contains(*, '" + sChar + "')";
+ Query query = manager.createQuery(sqlQuery, Query.SQL);
+
+ QueryResult queryResult;
+ try
+ {
+ queryResult = query.execute();
+ assertNotNull(queryResult.getNodes());
+ if (queryResult.getNodes().hasNext())
+ System.out.println("It works well with '" + sChar + "' path =" + queryResult.getNodes().nextNode().getPath());
+ else
+ System.err.println("No results could be found for '" + sChar + "'");
+ }
+ catch (Exception e)
+ {
+ System.err.println("It fails with '" + sChar + "' let's escape it");
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < sChar.length(); i++)
+ {
+ sb.append('\\').append(sChar.charAt(i));
+ }
+ sChar = sb.toString();
+ sqlQuery = "SELECT * FROM nt:base WHERE jcr:path LIKE '" + testNode.getPath() + "/%' and contains(*, '" + sChar + "')";
+ query = manager.createQuery(sqlQuery, Query.SQL);
+ try
+ {
+ queryResult = query.execute();
+ assertNotNull(queryResult.getNodes());
+ if (queryResult.getNodes().hasNext())
+ System.out.println("It works well with '" + sChar + "' path =" + queryResult.getNodes().nextNode().getPath());
+ else
+ System.err.println("No results could be found for '" + sChar + "'");
+ }
+ catch (Exception e1)
+ {
+ System.out.println("It fails also with '" + sChar + "'");
+ }
+ }
+ }
+ }
public void testAllofNodeType() throws Exception
{
Added: jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCacheInClusterMode.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCacheInClusterMode.java (rev 0)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/TestJBossCacheWorkspaceStorageCacheInClusterMode.java 2011-04-21 11:38:57 UTC (rev 4265)
@@ -0,0 +1,648 @@
+/*
+ * 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.jbosscache;
+
+import org.exoplatform.container.configuration.ConfigurationManagerImpl;
+import org.exoplatform.services.jcr.JcrImplBaseTest;
+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.dataflow.ItemState;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
+import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
+import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
+import org.exoplatform.services.jcr.datamodel.InternalQName;
+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.QPath;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.datamodel.ValueData;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ByteArrayPersistedValueData;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.CacheableWorkspaceDataManager;
+import org.exoplatform.services.jcr.impl.storage.SystemDataContainerHolder;
+import org.exoplatform.services.jcr.impl.storage.WorkspaceDataContainerBase;
+import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+import org.exoplatform.services.transaction.TransactionService;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+/**
+ * @author <a href="mailto:nicolas.filotto at exoplatform.com">Nicolas Filotto</a>
+ * @version $Id$
+ */
+public class TestJBossCacheWorkspaceStorageCacheInClusterMode extends JcrImplBaseTest
+{
+
+ public JBossCacheWorkspaceStorageCache getCacheImpl() throws Exception
+ {
+ TransactionService transactionService =
+ (TransactionService)container.getComponentInstanceOfType(TransactionService.class);
+
+ ArrayList<SimpleParameterEntry> list = new ArrayList<SimpleParameterEntry>();
+ list.add(new SimpleParameterEntry(JBossCacheWorkspaceStorageCache.JBOSSCACHE_CONFIG,
+ "jar:/conf/standalone/cluster/test-jbosscache-data-no-mux.xml"));
+ list.add(new SimpleParameterEntry("jbosscache-cluster-name", "TestJBossCacheWorkspaceStorageCacheInClusterMode"));
+
+ CacheEntry entry = new CacheEntry(list);
+ WorkspaceEntry workspaceEntry = new WorkspaceEntry();
+ workspaceEntry.setCache(entry);
+ return new JBossCacheWorkspaceStorageCache(workspaceEntry,
+ transactionService == null ? null : transactionService, new ConfigurationManagerImpl());
+ }
+
+ public void testRaceConditions() throws Exception
+ {
+ JBossCacheWorkspaceStorageCache cache1 = null, cache2 = null;
+ try
+ {
+ MyWorkspaceStorageConnection con = new MyWorkspaceStorageConnection();
+ WorkspaceDataContainer wdc = new MyWorkspaceDataContainer(con);
+ CacheableWorkspaceDataManager cwdmNode1 =
+ new CacheableWorkspaceDataManager(wdc, cache1 = getCacheImpl(), new SystemDataContainerHolder(wdc));
+ CacheableWorkspaceDataManager cwdmNode2 =
+ new CacheableWorkspaceDataManager(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
+ Action readAction = new Action(cwdmNode2)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ cwdm.getChildNodesData(parentNode);
+ }
+ };
+ Action writeAction = new Action(cwdmNode1)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ PlainChangesLog chlog = new PlainChangesLogImpl();
+ cwdm.getChildNodesData(parentNode);
+ chlog.add(ItemState.createAddedState(new PersistedNodeData("id-node" + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), new InternalQName(null, "node")), parentNode.getIdentifier(), 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
+ cwdm.save(chlog);
+ }
+ };
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getChildNodesData(parentNode));
+ assertEquals(2, cwdmNode1.getChildNodesData(parentNode).size());
+ assertNotNull(cwdmNode2.getChildNodesData(parentNode));
+ assertEquals(2, cwdmNode2.getChildNodesData(parentNode).size());
+ parentNode = new PersistedNodeData("parent-id2", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node2")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getChildNodesData(parentNode));
+ assertEquals(2, cwdmNode1.getChildNodesData(parentNode).size());
+ assertNotNull(cwdmNode2.getChildNodesData(parentNode));
+ assertEquals(2, cwdmNode2.getChildNodesData(parentNode).size());
+ // Test getChildPropertiesData
+ readAction = new Action(cwdmNode2)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ cwdm.getChildPropertiesData(parentNode);
+ }
+ };
+ writeAction = new Action(cwdmNode1)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ PlainChangesLog chlog = new PlainChangesLogImpl();
+ cwdm.getChildPropertiesData(parentNode);
+ chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-property" + parentNode.getIdentifier(), QPath.makeChildPath(
+ parentNode.getQPath(), new InternalQName(null, "property")), parentNode.getIdentifier(), 0,
+ PropertyType.STRING, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8"))))));
+ cwdm.save(chlog);
+ }
+ };
+ parentNode = new PersistedNodeData("parent-id3", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node3")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
+ assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode).size());
+ assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
+ assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode).size());
+ parentNode = new PersistedNodeData("parent-id4", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node4")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getChildPropertiesData(parentNode));
+ assertEquals(2, cwdmNode1.getChildPropertiesData(parentNode).size());
+ assertNotNull(cwdmNode2.getChildPropertiesData(parentNode));
+ assertEquals(2, cwdmNode2.getChildPropertiesData(parentNode).size());
+ // Test getReferencesData
+ readAction = new Action(cwdmNode2)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ cwdm.getReferencesData(parentNode.getIdentifier(), false);
+ }
+ };
+ writeAction = new Action(cwdmNode1)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ PlainChangesLog chlog = new PlainChangesLogImpl();
+ cwdm.getReferencesData(parentNode.getIdentifier(), false);
+ chlog.add(ItemState.createAddedState(new PersistedPropertyData("id-reference" + parentNode.getIdentifier(), QPath.makeChildPath(
+ parentNode.getQPath(), new InternalQName(null, "reference")), parentNode.getIdentifier(), 0,
+ PropertyType.REFERENCE, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8"))))));
+ cwdm.save(chlog);
+ }
+ };
+ parentNode = new PersistedNodeData("parent-id5", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node5")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
+ assertEquals(2, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
+ assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
+ assertEquals(2, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
+ parentNode = new PersistedNodeData("parent-id6", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node6")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getReferencesData(parentNode.getIdentifier(), false));
+ assertEquals(2, cwdmNode1.getReferencesData(parentNode.getIdentifier(), false).size());
+ assertNotNull(cwdmNode2.getReferencesData(parentNode.getIdentifier(), false));
+ assertEquals(2, cwdmNode2.getReferencesData(parentNode.getIdentifier(), false).size());
+
+ // Test getItemData by Id
+ readAction = new Action(cwdmNode2)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ cwdm.getItemData(parentNode.getIdentifier());
+ }
+ };
+ writeAction = new Action(cwdmNode1)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ PlainChangesLog chlog = new PlainChangesLogImpl();
+ cwdm.getItemData(parentNode.getIdentifier());
+ chlog.add(ItemState.createUpdatedState(new PersistedNodeData(parentNode.getIdentifier(), parentNode.getQPath(), Constants.ROOT_UUID, 2, 1,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null)));
+ cwdm.save(chlog);
+ }
+ };
+ parentNode = new PersistedNodeData("parent-id7", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node7")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getItemData(parentNode.getIdentifier()));
+ assertEquals(2, cwdmNode1.getItemData(parentNode.getIdentifier()).getPersistedVersion());
+ assertNotNull(cwdmNode2.getItemData(parentNode.getIdentifier()));
+ assertEquals(2, cwdmNode2.getItemData(parentNode.getIdentifier()).getPersistedVersion());
+ parentNode = new PersistedNodeData("parent-id8", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node8")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getItemData(parentNode.getIdentifier()));
+ assertEquals(2, cwdmNode1.getItemData(parentNode.getIdentifier()).getPersistedVersion());
+ assertNotNull(cwdmNode2.getItemData(parentNode.getIdentifier()));
+ assertEquals(2, cwdmNode2.getItemData(parentNode.getIdentifier()).getPersistedVersion());
+
+ // Test getItemData by Path
+ final QPathEntry qpe = new QPathEntry(null, "my-property", 1);
+ readAction = new Action(cwdmNode2)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ cwdm.getItemData(parentNode, qpe, ItemType.PROPERTY);
+ }
+ };
+ writeAction = new Action(cwdmNode1)
+ {
+ public void execute(NodeData parentNode) throws Exception
+ {
+ PlainChangesLog chlog = new PlainChangesLogImpl();
+ cwdm.getItemData(parentNode, qpe, ItemType.PROPERTY);
+ chlog.add(ItemState.createUpdatedState(new PersistedPropertyData("property-by-path"
+ + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), qpe), parentNode
+ .getIdentifier(), 2, PropertyType.STRING, false, Arrays
+ .asList((ValueData)new ByteArrayPersistedValueData(0, "some new data".getBytes("UTF-8"))))));
+ cwdm.save(chlog);
+ }
+ };
+ parentNode = new PersistedNodeData("parent-id9", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node9")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.READ_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY));
+ assertEquals(2, cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
+ assertNotNull(cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY));
+ assertEquals(2, cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
+ parentNode = new PersistedNodeData("parent-id10", QPath.makeChildPath(Constants.ROOT_PATH, new InternalQName(null, "parent-node10")), Constants.ROOT_UUID, 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ executeConcurrentReadNWrite(con, readAction, writeAction, Mode.WRITE_FIRST, parentNode);
+ assertNotNull(cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY));
+ assertEquals(2, cwdmNode1.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
+ assertNotNull(cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY));
+ assertEquals(2, cwdmNode2.getItemData(parentNode, qpe, ItemType.PROPERTY).getPersistedVersion());
+ }
+ finally
+ {
+ if (cache1 != null)
+ {
+ try
+ {
+ cache1.cache.stop();
+ }
+ catch (Exception e)
+ {
+ // ignore me
+ }
+ }
+ if (cache2 != null)
+ {
+ try
+ {
+ cache2.cache.stop();
+ }
+ catch (Exception e)
+ {
+ // ignore me
+ }
+ }
+ }
+ }
+
+ /**
+ * @param con
+ * @param cwdm
+ * @param mode
+ * @param idNode
+ * @throws InterruptedException
+ */
+ private void executeConcurrentReadNWrite(final MyWorkspaceStorageConnection con, final Action readAction,
+ final Action writeAction, final Mode mode, final NodeData parentNode) throws InterruptedException
+ {
+ final CountDownLatch goSignal = con.initCountDownLatch();
+ con.setParentNode(parentNode);
+ final AtomicReference<Exception> ex = new AtomicReference<Exception>();
+ final CountDownLatch startSignal = new CountDownLatch(1);
+ final CountDownLatch doneSignal = new CountDownLatch(2);
+ Thread writer = new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ startSignal.await();
+ con.wait.set(mode != Mode.WRITE_FIRST);
+ writeAction.execute(parentNode);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ ex.set(e);
+ }
+ finally
+ {
+ if (mode == Mode.WRITE_FIRST) goSignal.countDown();
+ doneSignal.countDown();
+ }
+ }
+ };
+ writer.start();
+ Thread reader = new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ startSignal.await();
+ con.wait.set(mode != Mode.READ_FIRST);
+ readAction.execute(parentNode);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ ex.set(e);
+ }
+ finally
+ {
+ if (mode == Mode.READ_FIRST) goSignal.countDown();
+ doneSignal.countDown();
+ }
+ }
+ };
+ reader.start();
+ startSignal.countDown();
+ doneSignal.await();
+ assertNull(ex.get());
+ }
+
+ private abstract class Action
+ {
+
+ protected final CacheableWorkspaceDataManager cwdm;
+ public Action(CacheableWorkspaceDataManager cwdm)
+ {
+ this.cwdm = cwdm;
+ }
+ protected abstract void execute(NodeData parentNode) throws Exception;
+ }
+ private static enum Mode
+ {
+ READ_FIRST, WRITE_FIRST;
+ }
+ private static class MyWorkspaceStorageConnection implements WorkspaceStorageConnection
+ {
+ public ThreadLocal<Boolean> wait = new ThreadLocal<Boolean>();
+ private NodeData parentNode;
+ private CountDownLatch goSignal;
+
+ public CountDownLatch initCountDownLatch()
+ {
+ return this.goSignal = new CountDownLatch(1);
+ }
+
+ public void setParentNode(NodeData parentNode)
+ {
+ this.parentNode = parentNode;
+ }
+
+ public void add(NodeData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public void add(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public void close() throws IllegalStateException, RepositoryException
+ {
+ }
+
+ public void commit() throws IllegalStateException, RepositoryException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public void delete(NodeData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public void delete(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public int getChildNodesCount(NodeData parent) throws RepositoryException
+ {
+ return -1;
+ }
+
+ public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ List<NodeData> children = new ArrayList<NodeData>();
+ children.add(new PersistedNodeData("id-node2" + parentNode.getIdentifier(), QPath.makeChildPath(parent.getQPath(), new InternalQName(null, "node2")), parent.getIdentifier(), 1, 0,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null));
+ return children;
+ }
+
+ public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException,
+ IllegalStateException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ List<PropertyData> children = new ArrayList<PropertyData>();
+ try
+ {
+ children.add(new PersistedPropertyData("id-property2" + parentNode.getIdentifier(), QPath.makeChildPath(
+ parentNode.getQPath(), new InternalQName(null, "property2")), parentNode.getIdentifier(), 0,
+ PropertyType.STRING, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, "some data".getBytes("UTF-8")))));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ return children;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemData getItemData(NodeData parentData, QPathEntry name) throws RepositoryException,
+ IllegalStateException
+ {
+ return getItemData(parentData, name, ItemType.UNKNOWN);
+ }
+
+ public ItemData getItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
+ IllegalStateException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (itemType == ItemType.NODE)
+ {
+ return new PersistedNodeData("my-node" + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), name), Constants.ROOT_UUID, 1, 1,
+ Constants.NT_UNSTRUCTURED, new InternalQName[0], null);
+ }
+ try
+ {
+ return new PersistedPropertyData("property-by-path"
+ + parentNode.getIdentifier(), QPath.makeChildPath(parentNode.getQPath(), name), parentNode
+ .getIdentifier(), 1, PropertyType.STRING, false, Arrays
+ .asList((ValueData)new ByteArrayPersistedValueData(0, "some new data".getBytes("UTF-8"))));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ return parentNode;
+ }
+
+ public List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException,
+ IllegalStateException, UnsupportedOperationException
+ {
+ if (wait.get() != null && wait.get())
+ {
+ try
+ {
+ goSignal.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ List<PropertyData> children = new ArrayList<PropertyData>();
+ try
+ {
+ children.add(new PersistedPropertyData("id-reference2" + parentNode.getIdentifier(), QPath.makeChildPath(
+ parentNode.getQPath(), new InternalQName(null, "reference2")), parentNode.getIdentifier(), 0,
+ PropertyType.REFERENCE, false, Arrays.asList((ValueData)new ByteArrayPersistedValueData(0, parentNode.getIdentifier().getBytes("UTF-8")))));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ return children;
+ }
+
+ public boolean isOpened()
+ {
+ return true;
+ }
+
+ public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException,
+ IllegalStateException
+ {
+ return null;
+ }
+
+ public void rename(NodeData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public void rollback() throws IllegalStateException, RepositoryException
+ {
+ }
+
+ public void update(NodeData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+
+ public void update(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+ InvalidItemStateException, IllegalStateException
+ {
+ }
+ };
+
+ private static class MyWorkspaceDataContainer extends WorkspaceDataContainerBase
+ {
+
+ private WorkspaceStorageConnection con;
+
+ public MyWorkspaceDataContainer(WorkspaceStorageConnection con)
+ {
+ this.con = con;
+ }
+
+ public boolean isCheckSNSNewConnection()
+ {
+ return false;
+ }
+
+ public boolean isSame(WorkspaceDataContainer another)
+ {
+ return false;
+ }
+
+ public WorkspaceStorageConnection openConnection() throws RepositoryException
+ {
+ return con;
+ }
+
+ public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
+ {
+ return con;
+ }
+
+ public WorkspaceStorageConnection reuseConnection(WorkspaceStorageConnection original) throws RepositoryException
+ {
+ return con;
+ }
+
+ public String getInfo()
+ {
+ return "MyWorkspaceDataContainer";
+ }
+
+ public String getName()
+ {
+ return "MyWorkspaceDataContainer";
+ }
+
+ public String getStorageVersion()
+ {
+ return "0";
+ }
+ };
+}
\ No newline at end of file
Added: jcr/branches/1.12.x/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-jbosscache-data-no-mux.xml
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-jbosscache-data-no-mux.xml (rev 0)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-jbosscache-data-no-mux.xml 2011-04-21 11:38:57 UTC (rev 4265)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">
+
+ <locking useLockStriping="false" concurrencyLevel="500" lockParentForChildInsertRemove="false"
+ lockAcquisitionTimeout="20000" />
+ <transaction syncRollbackPhase="true" syncCommitPhase="true" />
+
+ <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
+ <stateRetrieval timeout="20000" fetchInMemoryState="false" />
+ <sync replTimeout="20000"/>
+ <jgroupsConfig configFile="udp.xml">
+ </jgroupsConfig>
+ </clustering>
+
+ <!-- Eviction configuration -->
+ <eviction wakeUpInterval="5000">
+ <default algorithmClass="org.jboss.cache.eviction.ExpirationAlgorithm"
+ actionPolicyClass="org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.ParentNodeEvictionActionPolicy"
+ eventQueueSize="1000000">
+ <property name="maxNodes" value="1000000" />
+ <property name="warnNoExpirationKey" value="false" />
+ </default>
+ </eviction>
+</jbosscache>
\ No newline at end of file
Added: jcr/branches/1.12.x/patch/1.12.9-GA/JCR-1604/readme.txt
===================================================================
--- jcr/branches/1.12.x/patch/1.12.9-GA/JCR-1604/readme.txt (rev 0)
+++ jcr/branches/1.12.x/patch/1.12.9-GA/JCR-1604/readme.txt 2011-04-21 11:38:57 UTC (rev 4265)
@@ -0,0 +1,74 @@
+Summary
+
+ Status: JCR addNode within a transaction causes javax.transaction.HeuristicMixedException in the first access to the node
+ CCP Issue: CCP-870, Product Jira Issue: JCR-1604.
+ Complexity: High
+
+The Proposal
+Problem description
+
+What is the problem to fix?
+
+ JCR addNode within a transaction causes javax.transaction.HeuristicMixedException (internally, org.exoplatform.services.transaction.TransactionException caused by org.jboss.cache.lock.TimeoutException) in the first access to the node
+
+Fix description
+
+How is the problem fixed?
+
+ Perform loading data in cache outside the current transaction
+
+Patch information: JCR-1604.patch
+
+Tests to perform
+
+Reproduction test
+* Steps to reproduce:
+
+ Copy test-jcr.bsh to $JBOSS_HOME/server/$PROFILE/deploy
+ Start EPP (during the startup sequence, test-jcr.bsh will be executed once first=true)
+ Edit the test-jcr.bsh to set first=false and save (Then redeploy will be triggered and test-jcr.bsh will be executed again first=false)
+ Reboot EPP (during the startup sequence, test-jcr.bsh will be executed first=false)
+
+Tests performed at DevLevel
+* Do the same tasks like reproduction test => the scroll bar appears
+
+Tests performed at QA/Support Level
+*
+
+
+Documentation changes
+
+Documentation changes:
+ * No
+
+
+Configuration changes
+
+Configuration changes:
+ * No
+
+Will previous configuration continue to work?
+*Yes
+
+
+Risks and impacts
+
+Can this bug fix have any side effects on current client projects?
+
+ No
+
+Is there a performance risk/cost?
+ * No
+
+Validation (PM/Support/QA)
+
+PM Comment
+
+*Patch approved by the PM
+
+Support Comment
+*Support patch review: patch tested and approved
+
+QA Feedbacks
+*
+
More information about the exo-jcr-commits
mailing list