[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