Index: src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java (revision 1035641) +++ src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java (working copy) @@ -31,6 +31,7 @@ import org.apache.jackrabbit.core.id.ItemId; import org.apache.jackrabbit.core.id.NodeId; +import org.apache.jackrabbit.core.state.ItemStateManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,6 +118,24 @@ log.debug("ignoring nonexistent item " + id); // remove invalid id idList.remove(pos); + + // maybe fix the root cause + if (parentId != null && itemMgr.session.autoFixCorruptions()) { + try { + // it might be an access right problem + // we need to check if the item doesn't exist in the ism + ItemStateManager ism = itemMgr.sessionContext.getItemStateManager(); + if (!ism.hasItemState(id)) { + NodeImpl p = (NodeImpl) itemMgr.getItem(parentId); + p.removeChildNode((NodeId) id); + p.save(); + } + } catch (RepositoryException e2) { + e2.printStackTrace(); + // ignore + } + } + // try next } catch (AccessDeniedException e) { log.debug("ignoring nonexistent item " + id); Index: src/main/java/org/apache/jackrabbit/core/NodeImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/NodeImpl.java (revision 1035641) +++ src/main/java/org/apache/jackrabbit/core/NodeImpl.java (working copy) @@ -98,6 +98,7 @@ import org.apache.jackrabbit.core.state.ChildNodeEntry; import org.apache.jackrabbit.core.state.ItemState; import org.apache.jackrabbit.core.state.ItemStateException; +import org.apache.jackrabbit.core.state.ItemStateManager; import org.apache.jackrabbit.core.state.NodeReferences; import org.apache.jackrabbit.core.state.NodeState; import org.apache.jackrabbit.core.state.PropertyState; @@ -613,8 +614,24 @@ } // notify target of removal - NodeImpl childNode = itemMgr.getNode(childId, getNodeId()); - childNode.onRemove(getNodeId()); + try { + NodeImpl childNode = itemMgr.getNode(childId, getNodeId()); + childNode.onRemove(getNodeId()); + } catch (ItemNotFoundException e) { + boolean ignoreError = false; + if (itemMgr.session.autoFixCorruptions()) { + // it might be an access right problem + // we need to check if the item doesn't exist in the ism + ItemStateManager ism = itemMgr.sessionContext.getItemStateManager(); + if (!ism.hasItemState(id)) { + log.warn("Node " + childId + " not found, ignore", e); + ignoreError = true; + } + } + if (!ignoreError) { + throw e; + } + } // remove the child node entry if (!thisState.removeChildNodeEntry(childId)) { @@ -663,9 +680,28 @@ // recursively remove child node NodeId childId = entry.getId(); //NodeImpl childNode = (NodeImpl) itemMgr.getItem(childId); - NodeImpl childNode = itemMgr.getNode(childId, getNodeId()); - childNode.onRemove(thisState.getNodeId()); - // remove the child node entry + try { + NodeImpl childNode = itemMgr.getNode(childId, getNodeId()); + childNode.onRemove(thisState.getNodeId()); + // remove the child node entry + } catch (ItemNotFoundException e) { + boolean ignoreError = false; + if (parentId != null && itemMgr.session.autoFixCorruptions()) { + // it might be an access right problem + // we need to check if the item doesn't exist in the ism + ItemStateManager ism = itemMgr.sessionContext.getItemStateManager(); + if (!ism.hasItemState(childId)) { + log.warn("Child named " + entry.getName() + " (index " + entry.getIndex() + ", " + + "node id " + childId + ") " + + "not found when trying to remove " + getPath() + " " + + "(node id " + getNodeId() + ") - ignored", e); + ignoreError = true; + } + } + if (!ignoreError) { + throw e; + } + } thisState.removeChildNodeEntry(childId); } } Index: src/main/java/org/apache/jackrabbit/core/SessionImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/SessionImpl.java (revision 1035641) +++ src/main/java/org/apache/jackrabbit/core/SessionImpl.java (working copy) @@ -122,6 +122,17 @@ public static final String DISABLE_CLUSTER_SYNC_ON_REFRESH = "org.apache.jackrabbit.disableClusterSyncOnRefresh"; + /** + * Name of the session attribute that controls whether repository + * inconsistencies should be automatically fixed when traversing over child + * nodes, when trying to add a child node, or remove a child node. + * + * @since Apache Jackrabbit 2.2 + * @see JCR-2740 + */ + public static final String AUTO_FIX_CORRUPTIONS = + "org.apache.jackrabbit.autoFixCorruptions"; + private static Logger log = LoggerFactory.getLogger(SessionImpl.class); /** @@ -772,6 +783,22 @@ } /** + * Automatically fixes repository inconsistencies when traversing over child nodes, + * when trying to add a child node, or remove a child node. + *

+ * Subclasses can override this method to implement alternative + * rules on when cluster synchronization should be done. + * + * @return true if the {@link #AUTO_FIX_CORRUPTIONS} + * attribute is not set, false otherwise + * @since Apache Jackrabbit 2.2 + * @see JCR-2740 + */ + protected boolean autoFixCorruptions() { + return getAttribute(AUTO_FIX_CORRUPTIONS) == null; + } + + /** * {@inheritDoc} */ public boolean hasPendingChanges() throws RepositoryException {