You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/04/13 16:43:11 UTC

svn commit: r933646 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/state/NodeState.java test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java

Author: jukka
Date: Tue Apr 13 14:43:11 2010
New Revision: 933646

URL: http://svn.apache.org/viewvc?rev=933646&view=rev
Log:
JCR-2503: inconsistent session and persistent state after ReferentialIntegrityException

Patch and test case by Stephan Huttenhuis

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java?rev=933646&r1=933645&r2=933646&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java Tue Apr 13 14:43:11 2010
@@ -320,9 +320,12 @@ public class NodeState extends ItemState
      * @param id the id the new entry is refering to.
      * @return the newly added <code>ChildNodeEntry</code>
      */
-    public synchronized ChildNodeEntry addChildNodeEntry(Name nodeName,
+    public ChildNodeEntry addChildNodeEntry(Name nodeName,
                                                          NodeId id) {
-        ChildNodeEntry entry = childNodeEntries.add(nodeName, id);
+        ChildNodeEntry entry = null;
+        synchronized (this) {
+            entry = childNodeEntries.add(nodeName, id);
+        }
         notifyNodeAdded(entry);
         return entry;
     }
@@ -336,12 +339,18 @@ public class NodeState extends ItemState
      * @return <code>true</code> if the entry was sucessfully renamed;
      *         otherwise <code>false</code>
      */
-    public synchronized boolean renameChildNodeEntry(Name oldName, int index,
+    public boolean renameChildNodeEntry(Name oldName, int index,
                                                      Name newName) {
-        ChildNodeEntry oldEntry = childNodeEntries.remove(oldName, index);
-        if (oldEntry != null) {
-            ChildNodeEntry newEntry =
+        ChildNodeEntry oldEntry = null;
+        ChildNodeEntry newEntry = null;
+        synchronized (this) {
+            oldEntry = childNodeEntries.remove(oldName, index);
+            if (oldEntry != null) {
+                newEntry =
                     childNodeEntries.add(newName, oldEntry.getId());
+            }
+        }
+        if (oldEntry != null) {
             notifyNodeAdded(newEntry);
             notifyNodeRemoved(oldEntry);
             return true;
@@ -357,8 +366,11 @@ public class NodeState extends ItemState
      * @return <code>true</code> if the specified child node entry was found
      *         in the list of child node entries and could be removed.
      */
-    public synchronized boolean removeChildNodeEntry(Name nodeName, int index) {
-        ChildNodeEntry entry = childNodeEntries.remove(nodeName, index);
+    public boolean removeChildNodeEntry(Name nodeName, int index) {
+        ChildNodeEntry entry = null;
+        synchronized (this) {
+            entry = childNodeEntries.remove(nodeName, index);
+        }
         if (entry != null) {
             notifyNodeRemoved(entry);
         }
@@ -372,8 +384,11 @@ public class NodeState extends ItemState
      * @return <code>true</code> if the specified child node entry was found
      *         in the list of child node entries and could be removed.
      */
-    public synchronized boolean removeChildNodeEntry(NodeId id) {
-        ChildNodeEntry entry = childNodeEntries.remove(id);
+    public boolean removeChildNodeEntry(NodeId id) {
+        ChildNodeEntry entry = null;
+        synchronized (this) {
+            entry = childNodeEntries.remove(id);    
+        }
         if (entry != null) {
             notifyNodeRemoved(entry);
         }
@@ -383,8 +398,10 @@ public class NodeState extends ItemState
     /**
      * Removes all <code>ChildNodeEntry</code>s.
      */
-    public synchronized void removeAllChildNodeEntries() {
-        childNodeEntries.removeAll();
+    public void removeAllChildNodeEntries() {
+        synchronized (this) {
+            childNodeEntries.removeAll();
+        }
         notifyNodesReplaced();
     }
 
@@ -394,15 +411,16 @@ public class NodeState extends ItemState
      * @param nodeEntries list of {@link ChildNodeEntry} or
      * a {@link ChildNodeEntries} list.
      */
-    public synchronized void setChildNodeEntries(List<ChildNodeEntry> nodeEntries) {
-        if (nodeEntries instanceof ChildNodeEntries) {
-            // optimization
-            ChildNodeEntries entries = (ChildNodeEntries) nodeEntries;
-            childNodeEntries = (ChildNodeEntries) entries.clone();
-        } else {
-            childNodeEntries.removeAll();
-            childNodeEntries.addAll(nodeEntries);
-
+    public void setChildNodeEntries(List<ChildNodeEntry> nodeEntries) {
+        synchronized (this) {
+            if (nodeEntries instanceof ChildNodeEntries) {
+                // optimization
+                ChildNodeEntries entries = (ChildNodeEntries) nodeEntries;
+                childNodeEntries = (ChildNodeEntries) entries.clone();
+            } else {
+                childNodeEntries.removeAll();
+                childNodeEntries.addAll(nodeEntries);
+            }
         }
         notifyNodesReplaced();
     }

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java?rev=933646&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java Tue Apr 13 14:43:11 2010
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.integration.daily;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+
+public class ItemStateHierarchyManagerDeadlockTest extends TestCase {
+
+    public int g_numThreads = 30;
+
+    private int g_numRuns = 30;
+
+    private File repoDescriptor;
+
+    private File repoHome;
+
+    private RepositoryImpl repository;
+
+    public void setUp() throws IOException, RepositoryException {
+        File baseDir = new File(System.getProperty("basedir", "."));
+        File repoBaseDir = new File(baseDir, "target/ItemStateHierarchyManagerDeadlockTest");
+        FileUtils.deleteQuietly(repoBaseDir);
+
+        repoDescriptor = new File(repoBaseDir, "repository.xml");
+        repoHome = new File(repoBaseDir, "repository");
+        repoHome.mkdirs();
+
+        File repositoryDescriptor = new File(baseDir, "src/test/repository/repository.xml");
+        FileUtils.copyFile(repositoryDescriptor, repoDescriptor);
+    }
+
+    public void tearDown() throws IOException, InterruptedException {
+        FileUtils.deleteQuietly(repoHome.getParentFile());
+    }
+
+    private void startRepository() throws RepositoryException {
+        repository =
+            RepositoryImpl.create(RepositoryConfig.create(repoDescriptor.getAbsolutePath(), repoHome
+                .getAbsolutePath()));
+    }
+
+    private Session login() throws RepositoryException {
+        return repository.login(new SimpleCredentials("admin", "admin".toCharArray()), null);
+    }
+
+    private void stopRepository() throws RepositoryException {
+        repository.shutdown();
+    }
+
+    public void testConcurrentWritingSessions() throws Exception {
+
+        startRepository();
+
+        for (int i = 0; i < g_numRuns; i++) {
+            clearInvRootNode();
+            createInvRootNode();
+            runDeadlockTest();
+            try {
+                System.out.println("*** DONE FOR RUN " + (i + 1) + "/" + g_numRuns
+                        + ". SLEEP 1s before running next one");
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        stopRepository();
+    }
+
+    public void clearInvRootNode() {
+        System.out.println("Clear test repository InventoryTest.");
+        Session session = null;
+        try {
+            session = login();
+            Node root = session.getRootNode();
+            try {
+                Node inv = root.getNode("InventoryTest");
+                inv.remove();
+                session.save();
+            } catch (PathNotFoundException pnfx) {
+                System.err.println(" The root node <InventoryTest> is not available.");
+            }
+        } catch (Exception e) {
+            System.err.println("Exception in clear test repository:" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (session != null)
+                session.logout();
+        }
+    }
+
+    public void createInvRootNode() throws RepositoryException {
+        Session session = null;
+        try {
+            session = login();
+            getInvRootNode(session);
+        } catch (Exception e) {
+            System.err.println("Exception in clear test repository:" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (session != null)
+                session.logout();
+        }
+    }
+
+    private Node getInvRootNode(Session session) throws RepositoryException {
+        Node root = session.getRootNode();
+        Node inventoryRoot;
+        try {
+            inventoryRoot = root.getNode("InventoryTest");
+        } catch (PathNotFoundException pnfx) {
+            System.err.println(" The root node <InventoryTest> is not available. So creating new root Node.");
+            inventoryRoot = root.addNode("InventoryTest");
+            session.save();
+        }
+
+        return inventoryRoot;
+    }
+
+    public void createNodesUnderInvRootNode() {
+        System.out.println("Start createNodesUnderInvRootNode ");
+        Session session = null;
+        try {
+            session = login();
+            Node inventoryRoot = getInvRootNode(session);
+            for (int num = 0; num < 3; num++) {
+                Node current = inventoryRoot.addNode("Test" + num + "_" + System.currentTimeMillis());
+                current.setProperty("profondeur", 123);
+                current.setProperty("tree", "1");
+                current.setProperty("clientid", 1);
+                current.setProperty("propId", System.currentTimeMillis());
+                current.setProperty("name", "Node " + System.currentTimeMillis());
+                current.setProperty("address", "1.22.3.3");
+            }
+            session.save();
+            System.out.println("End createNodesUnderInvRootNode ");
+        } catch (Exception e) {
+            System.err.println("Exception in createNodesUnderInvRootNode:" + e.getMessage());
+        } finally {
+            if (session != null)
+                session.logout();
+        }
+    }
+
+    public void retrieveNodesUnderInvRootNode() {
+        System.out.println("Start retrieveNodesUnderInvRootNode ");
+        Session session = null;
+        try {
+            session = login();
+            // start from the bottom of the tree and move up
+            Node inventoryRoot = getInvRootNode(session);
+            NodeIterator nodes = inventoryRoot.getNodes();
+            while (nodes.hasNext()) {
+                Node node = nodes.nextNode();
+                // System.out.println("   Node: " + node.getName());
+                PropertyIterator properties = node.getProperties();
+                while (properties.hasNext()) {
+                    Property prop = properties.nextProperty();
+                    // System.out.println("      Prop: " + prop.getName() + " - " + prop.getString());
+                }
+            }
+            session.save();
+            System.out.println("End retrieveNodesUnderInvRootNode");
+        } catch (Exception e) {
+            System.err.println("Exception in retrieveNodesUnderInvRootNode:" + e.getMessage());
+        } finally {
+            if (session != null)
+                session.logout();
+        }
+    }
+
+    public void removeNodesUnderInvRootNode() {
+        System.out.println("Start removeNodesUnderInvRootNode ");
+        Session session = null;
+        try {
+            session = login();
+            // start from the bottom of the tree and move up
+            Node inventoryRoot = getInvRootNode(session);
+            NodeIterator nodes = inventoryRoot.getNodes();
+            int num = 0;
+            while (nodes.hasNext() && num < 3) {
+                Node node = nodes.nextNode();
+                node.remove();
+                num++;
+            }
+            session.save();
+            System.out.println("End removeNodesUnderInvRootNode");
+        } catch (Exception e) {
+            System.err.println("Exception in removeNodesUnderInvRootNode:" + e.getMessage());
+        } finally {
+            if (session != null)
+                session.logout();
+        }
+    }
+
+    public void runDeadlockTest() {
+        long start = System.currentTimeMillis();
+        List threads = new ArrayList();
+        for (int instanceIndex = 0; instanceIndex < g_numThreads; instanceIndex++) {
+            final int index = instanceIndex;
+            Thread t = new Thread(new Runnable() {
+
+                public void run() {
+                    for (int rounds = 0; rounds < 2; rounds++) {
+                        if (index % 2 == 0) {
+                            removeNodesUnderInvRootNode();
+                        }
+                        createNodesUnderInvRootNode();
+                        retrieveNodesUnderInvRootNode();
+                        if (index % 2 != 0) {
+                            removeNodesUnderInvRootNode();
+                        }
+                    }
+                }
+
+            });
+            threads.add(t);
+            t.start();
+        }
+
+        try {
+            Iterator it = threads.listIterator();
+            while (it.hasNext()) {
+                Thread t = (Thread) it.next();
+                t.join();
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        System.out.println("Duration for run: " + (System.currentTimeMillis() - start) / 1000 + "s");
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/integration/daily/ItemStateHierarchyManagerDeadlockTest.java
------------------------------------------------------------------------------
    svn:eol-style = native