Index: src/main/java/org/apache/jackrabbit/core/ItemImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/ItemImpl.java (revision 898289) +++ src/main/java/org/apache/jackrabbit/core/ItemImpl.java (working copy) @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -481,7 +482,7 @@ String msg = itemMgr.safeGetJCRPath(id) + ": mandatory child node " + cnd.getName() + " does not exist"; - if (!nodeState.hasChildNodeEntry(cnd.getName())) { + if (!nodeState.hasChildNodeEntry(cnd.getName())) { log.debug(msg); throw new ConstraintViolationException(msg); } else { @@ -975,7 +976,15 @@ * 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; Index: src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (revision 898289) +++ src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (working copy) @@ -404,6 +404,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); @@ -420,6 +425,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); @@ -625,7 +635,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/state/SessionItemStateManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java (revision 898289) +++ src/main/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java (working copy) @@ -41,9 +41,6 @@ import org.apache.jackrabbit.core.util.Dumpable; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.QNodeDefinition; -import org.apache.jackrabbit.spi.Path; -import org.apache.jackrabbit.spi.PathFactory; -import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -582,7 +579,7 @@ // the nearest common ancestor of all transient states // must be either // a) a node state with STATUS_EXISTING_MODIFIED, or - // b) the parent node of a property state with STATUS_EXISTING_MODIFIED + // b) the parent node of a property state with STATUS_EXISTING_MODIFIED // collect all candidates based on above criteria Collection candidateIds = new LinkedList(); Index: src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (revision 898289) +++ src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (working copy) @@ -574,6 +574,8 @@ checkReferentialIntegrity(); } + checkAddedChildNodes(); + /** * prepare the events. this needs to be after the referential * integrity check, since another transaction could have modified @@ -731,8 +733,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 { @@ -968,6 +970,36 @@ } /** + * 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 (ItemState state : local.addedStates()) { + checkAddedChildNode(state); + } + for (ItemState state : local.modifiedStates()) { + checkAddedChildNode(state); + } + } + + private void checkAddedChildNode(ItemState state) throws ItemStateException { + if (state.isNode()) { + NodeState node = (NodeState) state; + for (ChildNodeEntry child : node.getAddedChildNodeEntries()) { + NodeId id = child.getId(); + if (local.get(id) == null) { + if (!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