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