Index: src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (revision 938884)
+++ src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (working copy)
@@ -577,6 +577,8 @@
checkReferentialIntegrity();
}
+ checkAddedChildNodes();
+
/**
* prepare the events. this needs to be after the referential
* integrity check, since another transaction could have modified
@@ -728,8 +730,8 @@
long t0 = System.currentTimeMillis();
persistMgr.store(shared);
succeeded = true;
- long t1 = System.currentTimeMillis();
if (log.isDebugEnabled()) {
+ long t1 = System.currentTimeMillis();
log.debug("persisting change log " + shared + " took " + (t1 - t0) + "ms");
}
} finally {
@@ -972,6 +974,39 @@
}
/**
+ * Verify the added child nodes of the added or modified states exist.
+ * If they don't exist, most likely the problem is that the same session
+ * is used concurrently.
+ */
+ private void checkAddedChildNodes() throws ItemStateException {
+ for (Iterator iter = local.addedStates(); iter.hasNext();) {
+ ItemState state = (ItemState) iter.next();
+ checkAddedChildNode(state);
+ }
+ for (Iterator iter = local.modifiedStates(); iter.hasNext();) {
+ ItemState state = (ItemState) iter.next();
+ checkAddedChildNode(state);
+ }
+ }
+
+ private void checkAddedChildNode(ItemState state) throws ItemStateException {
+ if (state.isNode()) {
+ NodeState nodeState = (NodeState) state;
+ for (Iterator cneIt = nodeState.getAddedChildNodeEntries().iterator(); cneIt.hasNext();) {
+ ChildNodeEntry cne = (ChildNodeEntry) cneIt.next();
+ NodeId id = cne.getId();
+ if (local.get(id) == null && !id.equals(RepositoryImpl.VERSION_STORAGE_NODE_ID)
+ && !id.equals(RepositoryImpl.NODETYPES_NODE_ID) && !cache.isCached(id)
+ && !persistMgr.exists(id)) {
+ String msg = "Trying to add a non-existing child node: " + id;
+ log.debug(msg);
+ throw new ItemStateException(msg);
+ }
+ }
+ }
+ }
+
+ /**
* Verifies that
*
* - no referenceable nodes are deleted if they are still being referenced
Index: src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (revision 938884)
+++ src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (working copy)
@@ -403,6 +403,11 @@
NodeId parentId = n.getParentId();
// the parent of an added item is always modified or new
NodeState parent = (NodeState) changes.get(parentId);
+ if (parent == null) {
+ String msg = "Parent " + parentId + " must be changed as well.";
+ log.error(msg);
+ throw new ItemStateException(msg);
+ }
NodeTypeImpl nodeType = getNodeType(parent, session);
Set mixins = parent.getMixinTypeNames();
Path path = getPath(n.getNodeId(), hmgr);
@@ -419,6 +424,11 @@
} else {
// property created / set
NodeState n = (NodeState) changes.get(state.getParentId());
+ if (n == null) {
+ String msg = "Node " + state.getParentId() + " must be changed as well.";
+ log.error(msg);
+ throw new ItemStateException(msg);
+ }
NodeTypeImpl nodeType = getNodeType(n, session);
Set mixins = n.getMixinTypeNames();
Path path = getPath(state.getId(), hmgr);
@@ -611,7 +621,12 @@
} catch (Exception e) {
// also catch eventual runtime exceptions here
// should never happen actually
- String msg = "Item " + node.getNodeId() + " has unknown node type: " + node.getNodeTypeName();
+ String msg;
+ if (node == null) {
+ msg = "Node state is null";
+ } else {
+ msg = "Item " + node.getNodeId() + " has unknown node type: " + node.getNodeTypeName();
+ }
log.error(msg);
throw new ItemStateException(msg, e);
}
Index: src/main/java/org/apache/jackrabbit/core/ItemImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/ItemImpl.java (revision 938884)
+++ src/main/java/org/apache/jackrabbit/core/ItemImpl.java (working copy)
@@ -67,6 +67,7 @@
import javax.jcr.version.VersionException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@@ -936,12 +937,21 @@
* build list of transient (i.e. new & modified) states that
* should be persisted
*/
- Collection dirty = getTransientStates();
+
+ Collection dirty;
+ try {
+ dirty = getTransientStates();
+ } catch (ConcurrentModificationException e) {
+ String msg = "Concurrent modification; session is closed";
+ log.error(msg, e);
+ session.logout();
+ throw e;
+ }
+
if (dirty.size() == 0) {
// no transient items, nothing to do here
return;
}
-
/**
* build list of transient descendants in the attic
* (i.e. those marked as 'removed')