You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ck...@apache.org on 2010/10/01 10:19:42 UTC
svn commit: r1003428 - in /jackrabbit/branches/1.6/jackrabbit-core/src:
main/java/org/apache/jackrabbit/core/
main/java/org/apache/jackrabbit/core/state/
test/java/org/apache/jackrabbit/core/
Author: ckoell
Date: Fri Oct 1 08:19:42 2010
New Revision: 1003428
URL: http://svn.apache.org/viewvc?rev=1003428&view=rev
Log:
1.6: Modified Patch for JCR-2598
Added:
jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java (with props)
Modified:
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java
jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java Fri Oct 1 08:19:42 2010
@@ -569,14 +569,17 @@ public class BatchedItemOperations exten
throw new UnsupportedRepositoryOperationException(msg);
}
- // remove child node entry from old parent
- srcParent.removeChildNodeEntry(srcName.getName(), srcNameIndex);
+ // do move:
+ // 1. remove child node entry from old parent
+ boolean success = srcParent.removeChildNodeEntry(target.getNodeId());
+ if (success) {
- // re-parent target node
- target.setParentId(destParent.getNodeId());
+ // 2. re-parent target node
+ target.setParentId(destParent.getNodeId());
- // add child node entry to new parent
- destParent.addChildNodeEntry(destName.getName(), target.getNodeId());
+ // 3. add child node entry to new parent
+ destParent.addChildNodeEntry(destName.getName(), target.getNodeId());
+ }
}
// store states
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java Fri Oct 1 08:19:42 2010
@@ -850,10 +850,10 @@ public abstract class ItemImpl implement
}
// delegate the removal of the child item to the parent node
- Path.Element thisName = getPrimaryPath().getNameElement();
if (isNode()) {
- parentNode.removeChildNode(thisName.getName(), thisName.getIndex());
+ parentNode.removeChildNode((NodeId) getId());
} else {
+ Path.Element thisName = getPrimaryPath().getNameElement();
parentNode.removeChildProperty(thisName.getName());
}
}
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Fri Oct 1 08:19:42 2010
@@ -600,6 +600,8 @@ public class NodeImpl extends ItemImpl i
itemMgr.getItem(propId).setRemoved();
}
+ // The index may have changed because of changes by another session. Use removeChildNode(NodeId childId)
+ // instead
protected void removeChildNode(Name nodeName, int index)
throws RepositoryException {
// modify the state of 'this', i.e. the parent node
@@ -627,7 +629,30 @@ public class NodeImpl extends ItemImpl i
throw new RepositoryException(msg);
}
}
+
+ protected void removeChildNode(NodeId childId) throws RepositoryException {
+ // modify the state of 'this', i.e. the parent node
+ NodeState thisState = (NodeState) getOrCreateTransientItemState();
+ ChildNodeEntry entry =
+ thisState.getChildNodeEntry(childId);
+ if (entry == null) {
+ String msg = "failed to remove child " + childId + " of " + this;
+ log.debug(msg);
+ throw new RepositoryException(msg);
+ }
+
+ // notify target of removal
+ NodeImpl childNode = itemMgr.getNode(childId, getNodeId());
+ childNode.onRemove(getNodeId());
+ // remove the child node entry
+ if (!thisState.removeChildNodeEntry(childId)) {
+ String msg = "failed to remove child " + childId + " of " + this;
+ log.debug(msg);
+ throw new RepositoryException(msg);
+ }
+ }
+
protected void onRedefine(NodeDef def) throws RepositoryException {
NodeDefinitionImpl newDef =
session.getNodeTypeManager().getNodeDefinition(def);
@@ -670,7 +695,7 @@ public class NodeImpl extends ItemImpl i
NodeImpl childNode = itemMgr.getNode(childId, getNodeId());
childNode.onRemove(thisState.getNodeId());
// remove the child node entry
- thisState.removeChildNodeEntry(entry.getName(), entry.getIndex());
+ thisState.removeChildNodeEntry(childId);
}
}
@@ -1307,7 +1332,7 @@ public class NodeImpl extends ItemImpl i
if (oldDef.isProtected()) {
// remove 'orphaned' protected child node immediately
- removeChildNode(entry.getName(), entry.getIndex());
+ removeChildNode(entry.getId());
continue;
}
@@ -1322,7 +1347,7 @@ public class NodeImpl extends ItemImpl i
} catch (ConstraintViolationException cve) {
// no suitable definition found for this child node,
// remove it
- removeChildNode(entry.getName(), entry.getIndex());
+ removeChildNode(entry.getId());
}
}
success = true;
@@ -4932,7 +4957,7 @@ public class NodeImpl extends ItemImpl i
NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
if (node.getDefinition().isProtected()) {
// remove 'orphaned' protected child node immediately
- removeChildNode(entry.getName(), entry.getIndex());
+ removeChildNode(entry.getId());
continue;
}
NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(
@@ -4945,7 +4970,7 @@ public class NodeImpl extends ItemImpl i
} catch (ConstraintViolationException cve) {
// no suitable definition found for this child node,
// remove it
- removeChildNode(entry.getName(), entry.getIndex());
+ removeChildNode(entry.getId());
}
}
} catch (ItemStateException ise) {
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Fri Oct 1 08:19:42 2010
@@ -1111,19 +1111,25 @@ public class SessionImpl extends Abstrac
throw new UnsupportedRepositoryOperationException(msg);
}
- // do move:
- // 1. remove child node entry from old parent
+ // Get the transient states
NodeState srcParentState =
(NodeState) srcParentNode.getOrCreateTransientItemState();
- srcParentState.removeChildNodeEntry(srcName.getName(), index);
- // 2. re-parent target node
NodeState targetState =
(NodeState) targetNode.getOrCreateTransientItemState();
- targetState.setParentId(destParentNode.getNodeId());
- // 3. add child node entry to new parent
NodeState destParentState =
(NodeState) destParentNode.getOrCreateTransientItemState();
- destParentState.addChildNodeEntry(destName.getName(), targetId);
+
+ // do move:
+ // 1. remove child node entry from old parent
+ boolean success = srcParentState.removeChildNodeEntry(targetId);
+ if (success) {
+
+ // 2. re-parent target node
+ targetState.setParentId(destParentNode.getNodeId());
+
+ // 3. add child node entry to new parent
+ destParentState.addChildNodeEntry(destName.getName(), targetId);
+ }
}
// change definition of target
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java Fri Oct 1 08:19:42 2010
@@ -145,6 +145,8 @@ class ChildNodeEntries implements Clonea
}
}
+ // The index may have changed because of changes by another session. Use remove(NodeId id)
+ // instead
public ChildNodeEntry remove(Name nodeName, int index) {
if (index < 1) {
throw new IllegalArgumentException("index is 1-based");
@@ -224,7 +226,7 @@ class ChildNodeEntries implements Clonea
* @return the removed entry or <code>null</code> if there is no such entry.
*/
public ChildNodeEntry remove(ChildNodeEntry entry) {
- return remove(entry.getName(), entry.getIndex());
+ return remove(entry.getId());
}
/**
Modified: jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?rev=1003428&r1=1003427&r2=1003428&view=diff
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (original)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java Fri Oct 1 08:19:42 2010
@@ -712,6 +712,14 @@ public class SharedItemStateManager
if (eventChannel != null) {
eventChannel.updatePrepared(this);
}
+
+ try {
+ validateHierarchy(local);
+ } catch (ItemStateException e) {
+ throw e;
+ } catch (RepositoryException e) {
+ throw new ItemStateException("Invalid hierarchy", e);
+ }
/* Push all changes from the local items to the shared items */
local.push();
@@ -1118,7 +1126,340 @@ public class SharedItemStateManager
}
}
+
+ /**
+ * Validates the hierarchy consistency of the changes in the changelog.
+ *
+ * @param changeLog
+ * The local changelog the should be validated
+ * @throws ItemStateException
+ * If the hierarchy changes are inconsistent.
+ * @throws RepositoryException
+ * If the consistency could not be validated
+ *
+ */
+ private void validateHierarchy(ChangeLog changeLog) throws ItemStateException, RepositoryException {
+
+ // Check the deleted node states
+ validateDeleted(changeLog);
+
+ // Check the added node states
+ validateAdded(changeLog);
+
+ // Check the modified node states
+ validateModified(changeLog);
+ }
+
+ /**
+ * Checks the parents and children of all deleted node states in the changelog.
+ *
+ * @param changeLog
+ * The local changelog the should be validated
+ * @throws ItemStateException
+ * If the hierarchy changes are inconsistent.
+ */
+ private void validateDeleted(ChangeLog changeLog) throws ItemStateException {
+
+ // Check each deleted nodestate
+ for (Iterator it = changeLog.deletedStates(); it.hasNext(); ) {
+ ItemState removedState = (ItemState) it.next();
+ if (removedState instanceof NodeState) {
+
+ // Get the next state
+ NodeState removedNodeState = (NodeState) removedState;
+ NodeId id = removedNodeState.getNodeId();
+
+ // Get and check the corresponding overlayed state
+ NodeState overlayedState = (NodeState) removedState.getOverlayedState();
+ if (overlayedState == null) {
+ String message = "Unable to load persistent state for removed node " + id;
+ overlayedState = (NodeState) SharedItemStateManager.this.getItemState(id);
+ if (overlayedState == null) {
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+
+ // Check whether an version of this node has been restored
+ boolean addedAndRemoved = changeLog.has(removedNodeState.getId());
+ if (!addedAndRemoved) {
+
+ // Check the old parent
+ NodeId oldParentId = overlayedState.getParentId();
+ if (changeLog.deleted(oldParentId)) {
+ // parent has been deleted aswell
+ } else if (changeLog.isModified(oldParentId)) {
+ // the modified state will be check later on
+ } else {
+ String message = "Node with id " + id
+ + " has been removed, but the parent node isn't part of the changelog " + oldParentId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ // Get the original list of child ids
+ for (Iterator it2 = overlayedState.getChildNodeEntries().iterator(); it2.hasNext(); ) {
+ ChildNodeEntry entry = (ChildNodeEntry) it2.next();
+ // Check the next child
+ NodeId childId = entry.getId();
+
+ if (changeLog.deleted(childId)) {
+ // child has been deleted aswell
+ } else if (changeLog.isModified(childId)) {
+
+ // the modified state will be check later on
+ } else {
+ String message = "Node with id " + id
+ + " has been removed, but the old child node isn't part of the changelog "
+ + childId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks the parents and children of all added node states in the changelog.
+ *
+ * @param changeLog
+ * The local changelog the should be validated
+ * @throws ItemStateException
+ * If the hierarchy changes are inconsistent.
+ */
+ private void validateAdded(ChangeLog changeLog) throws ItemStateException {
+
+ // Check each added node
+ for (Iterator it = changeLog.addedStates(); it.hasNext(); ) {
+ ItemState state = (ItemState) it.next();
+ if (state instanceof NodeState) {
+
+ // Get the next added node
+ NodeState addedNodeState = (NodeState) state;
+ NodeId id = addedNodeState.getNodeId();
+
+ // Check the parent
+ NodeId parentId = addedNodeState.getParentId();
+ if (changeLog.has(parentId)) { // Added or modified
+ // the modified state will be check later on
+ checkParent(changeLog, addedNodeState, parentId);
+ } else {
+ String message = "Node with id " + id
+ + " has been added, but the parent node isn't part of the changelog " + parentId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ // Check the children
+ for (Iterator it2 = addedNodeState.getChildNodeEntries().iterator(); it2.hasNext(); ) {
+ ChildNodeEntry entry = (ChildNodeEntry) it2.next();
+ // Get the next child
+ NodeId childId = entry.getId();
+
+ if (changeLog.has(childId)) {
+ NodeState childState = (NodeState) changeLog.get(childId);
+ checkParent(changeLog, childState, id);
+ // the child state will be check later on
+
+ } else {
+ String message = "Node with id " + id
+ + " has been added, but the child node isn't part of the changelog " + childId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks the parents and children of all modified node states in the changelog.
+ *
+ * @param changeLog
+ * The local changelog the should be validated
+ * @throws ItemStateException
+ * If the hierarchy changes are inconsistent.
+ */
+ private void validateModified(ChangeLog changeLog) throws ItemStateException, RepositoryException {
+
+ // Check all modified nodes
+ for (Iterator it = changeLog.modifiedStates(); it.hasNext(); ) {
+ ItemState state = (ItemState) it.next();
+ if (state instanceof NodeState) {
+ // Check the next node
+ NodeState modifiedNodeState = (NodeState) state;
+ NodeId id = modifiedNodeState.getNodeId();
+
+ // Check whether to overlayed state is present for determining diffs
+ NodeState overlayedState = (NodeState) modifiedNodeState.getOverlayedState();
+ if (overlayedState == null) {
+ String message = "Unable to load persistent state for modified node " + id;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ // Check the parent
+ NodeId parentId = modifiedNodeState.getParentId();
+ NodeId oldParentId = overlayedState.getParentId();
+
+ // The parent should not be deleted
+ if (parentId != null && changeLog.deleted(parentId)) {
+ String message = "Parent of node with id " + id + " has been deleted";
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ if (parentId != null && changeLog.has(parentId)) {
+ checkParent(changeLog, modifiedNodeState, parentId);
+ }
+
+ // Check whether this node is the root node
+ if (parentId == null && oldParentId == null) {
+ // The root node can be ignored
+
+ } else if (!parentId.equals(oldParentId)) {
+
+ // This node has been moved, check whether the parent has been modified aswell
+ if (changeLog.has(parentId)) {
+ checkParent(changeLog, modifiedNodeState, parentId);
+ } else if (!isShareable(modifiedNodeState)) {
+ String message = "New parent of node " + id + " is not present in the changelog " + id;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ // The old parent must be modified or deleted
+ if (!changeLog.isModified(oldParentId) && !changeLog.deleted(oldParentId)) {
+ String message = "Node with id " + id
+ + " has been move, but the original parent is not part of the changelog: "
+ + oldParentId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+
+ // Check all assigned children
+ for (Iterator it2 = modifiedNodeState.getChildNodeEntries().iterator(); it2.hasNext(); ) {
+ ChildNodeEntry entry = (ChildNodeEntry) it2.next();
+
+ NodeId childId = entry.getId();
+
+ // Check whether this node has a deleted childid
+ if (changeLog.deleted(childId) && !changeLog.has(childId)) { // Versionable
+ String message = "Node with id " + id + " has a deleted childid: " + childId;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ if (changeLog.has(childId)) {
+ NodeState childState = (NodeState) changeLog.get(childId);
+ checkParent(changeLog, childState, id);
+ }
+ }
+
+ // Check all children the have been added
+ for (Iterator it2 = modifiedNodeState.getAddedChildNodeEntries().iterator(); it2.hasNext(); ) {
+ ChildNodeEntry entry = (ChildNodeEntry) it2.next();
+ NodeId childId = entry.getId();
+ if (!changeLog.has(childId)) {
+ String message = "ChildId " + childId + " has been added to parent " + id
+ + ", but is not present in the changelog";
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+
+ // Check all children the have been moved or removed
+ for (Iterator it2 = modifiedNodeState.getRemovedChildNodeEntries().iterator(); it2.hasNext(); ) {
+ ChildNodeEntry entry = (ChildNodeEntry) it2.next();
+ NodeId childId = entry.getId();
+ if (!changeLog.isModified(childId) && !changeLog.deleted(childId)) {
+ String message = "Child node entry with id " + childId
+ + " has been removed, but is not present in the changelog";
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check the consistency of a parent/child relationship.
+ *
+ * @param changeLog
+ * The changelog to check
+ * @param childState
+ * The id of the node for which the parent/child relationship should be validated.
+ * @param expectedParent
+ * The expected parent id of the child node.
+ * @throws ItemStateException
+ * If a inconsistency has been detected.
+ */
+ void checkParent(ChangeLog changeLog, NodeState childState, NodeId expectedParent) throws ItemStateException {
+
+ // Check whether the the changelog contains an entry for the parent aswell.
+ NodeId parentId = childState.getParentId();
+ if (!parentId.equals(expectedParent)) {
+ Set sharedSet = childState.getSharedSet();
+ if (sharedSet.contains(expectedParent)) {
+ return;
+ }
+ String message = "Child node has another parent id " + parentId + ", expected " + expectedParent;
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ if (!changeLog.has(parentId)) {
+ String message = "Parent not part of changelog";
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+
+ // Get the parent from the changelog
+ NodeState parent = (NodeState) changeLog.get(parentId);
+
+ // Get and check the child node entry from the parent
+ NodeId childId = childState.getNodeId();
+ ChildNodeEntry childNodeEntry = parent.getChildNodeEntry(childId);
+ if (childNodeEntry == null) {
+ String message = "Child not present in parent";
+ log.error(message);
+ throw new ItemStateException(message);
+ }
+ }
+
+ /**
+ * Determines whether the specified node is <i>shareable</i>, i.e. whether the mixin type <code>mix:shareable</code>
+ * is either directly assigned or indirectly inherited.
+ *
+ * @param state
+ * node state to check
+ * @return true if the specified node is <i>shareable</i>, false otherwise.
+ * @throws RepositoryException
+ * if an error occurs
+ */
+ private boolean isShareable(NodeState state) throws RepositoryException {
+ // shortcut: check some wellknown built-in types first
+ Name primary = state.getNodeTypeName();
+ Set mixins = state.getMixinTypeNames();
+ if (mixins.contains(NameConstants.MIX_SHAREABLE)) {
+ return true;
+ }
+
+ try {
+ EffectiveNodeType type = ntReg.getEffectiveNodeType(primary, mixins);
+ return type.includesNodeType(NameConstants.MIX_SHAREABLE);
+ } catch (NodeTypeConflictException ntce) {
+ String msg = "internal error: failed to build effective node type for node " + state.getNodeId();
+ log.debug(msg);
+ throw new RepositoryException(msg, ntce);
+ }
+ }
+
/**
* Begin update operation. This will return an object that can itself be
* ended/canceled.
Added: jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java?rev=1003428&view=auto
==============================================================================
--- jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java (added)
+++ jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java Fri Oct 1 08:19:42 2010
@@ -0,0 +1,204 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+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.config.RepositoryConfig;
+
+public class ConcurrencyTest3 extends TestCase {
+
+ private static final int NUM_ITERATIONS = 1;
+
+ private static final int NUM_THREADS = 5;
+
+ 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/corruption-test3");
+ 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());
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testConcurrentWritingSessions() throws Exception {
+
+ startRepository();
+
+ // Create test root node
+ Session session = login();
+ Node testRoot = session.getRootNode().addNode("test");
+ Node testRoot2 = session.getRootNode().addNode("test2");
+ testRoot.addMixin("mix:referenceable");
+ session.save();
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ JcrTestThread[] threads = new JcrTestThread[NUM_THREADS];
+ Session[] sessions = new Session[NUM_THREADS];
+ for (int j = 0; j < threads.length; j++) {
+ // create new session and a new thread
+ Session threadSession = login();
+ JcrTestThread thread = new JcrTestThread(testRoot.getUUID(), threadSession);
+ thread.setName("Iteration " + i + " - Thread " + j);
+ thread.start();
+ threads[j] = thread;
+ sessions[j] = threadSession;
+ }
+ for (int j = 0; j < threads.length; j++) {
+ if (threads[j] != null) {
+ threads[j].join();
+ }
+ }
+ for (int j = 0; j < sessions.length; j++) {
+ if (sessions[j] != null) {
+ sessions[j].logout();
+ }
+ }
+ }
+
+ session.logout();
+ stopRepository();
+
+ // Restart with an empty index, scan and delete test root node
+ deleteIndex();
+ startRepository();
+ session = login();
+ testRoot = session.getRootNode().getNode("test");
+ testRoot.addNode("new node");
+ session.save();
+ scan(testRoot);
+ testRoot.remove();
+ session.save();
+ session.logout();
+ stopRepository();
+ }
+
+ 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();
+ }
+
+ private void deleteIndex() throws IOException {
+ FileUtils.deleteDirectory(new File(repoHome, "workspaces/default/index"));
+ }
+
+ private void scan(Node node) throws RepositoryException {
+ // System.err.println(node.getName() + " - " + node.getPath());
+ for (NodeIterator it = node.getNodes(); it.hasNext();) {
+ Node child = it.nextNode();
+ scan(child);
+ }
+ }
+
+ class JcrTestThread extends Thread {
+
+ private String testRootUuid;
+
+ private Session jcrSession;
+
+ private ArrayList nodes = new ArrayList();
+
+ private int RUN_SIZE = 100;
+
+ private int ACTION_SIZE = 1000;
+
+ JcrTestThread(String uuid, Session session) throws RepositoryException {
+ testRootUuid = uuid;
+ jcrSession = session;
+ }
+
+ public void run() {
+ outer: for (int i = 0; i < RUN_SIZE; i++) {
+ for (int j = 0; j < ACTION_SIZE; j++) {
+ int random = (int) Math.floor(2 * Math.random());
+ if (random == 0) {
+ try {
+ // Add a node called "P"
+ Node addedNode = jcrSession.getNodeByUUID(testRootUuid).addNode("P");
+ addedNode.addMixin("mix:referenceable");
+ nodes.add(addedNode.getUUID());
+
+ } catch (RepositoryException ignore) {
+ }
+ } else {
+ if (nodes.size() > 0) {
+ int randomIndex = (int) Math.floor(nodes.size() * Math.random());
+ try {
+ // Remove a random node we created within this
+ // thread and within this session
+ Node removeNode = jcrSession.getNodeByUUID((String) nodes.get(randomIndex));
+ String path = removeNode.getPath();
+ if (path.indexOf("test2") == -1) {
+ jcrSession.move(removeNode.getPath(), removeNode.getParent().getPath()
+ + "2/P");
+ } else {
+ removeNode.remove();
+ nodes.remove(randomIndex);
+ }
+ } catch (RepositoryException ignore) {
+ System.err.println(" 1 " + ignore.toString());
+ }
+ }
+ }
+ }
+ try {
+ jcrSession.save();
+ } catch (RepositoryException e) {
+ System.err.println(" 2 " + e.toString());
+ break outer;
+ }
+ }
+ }
+ }
+}
Propchange: jackrabbit/branches/1.6/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrencyTest3.java
------------------------------------------------------------------------------
svn:eol-style = native