### Eclipse Workspace Patch 1.0 #P jackrabbit-core Index: src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java (revision 0) +++ src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java (revision 0) @@ -0,0 +1,84 @@ +/* + * 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.persistence; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.util.UUID; +import javax.jcr.Node; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import org.apache.commons.io.FileUtils; +import org.apache.jackrabbit.core.TransientRepository; + +/** + * Tests that a corrupt node is automatically fixed. + */ +public class AutoFixCorruptNode { + public static void main(String... args) throws Exception { + test(1); + test(2); + } + + private static void test(int testCase) throws Exception { + + // new repository + FileUtils.deleteDirectory(new File("repository")); + TransientRepository rep = new TransientRepository(); + Session s = rep.login(new SimpleCredentials("admin", "admin".toCharArray())); + Node root = s.getRootNode(); + + // add nodes /test and /test/missing + Node test = root.addNode("test"); + Node missing = test.addNode("missing"); + missing.addMixin("mix:referenceable"); + UUID id = UUID.fromString(missing.getIdentifier()); + s.save(); + s.logout(); + + // remove the bundle for /test/missing directly in the database + Connection conn = DriverManager.getConnection("jdbc:derby:repository/workspaces/default/db"); + PreparedStatement prep = conn.prepareStatement("delete from DEFAULT_BUNDLE where NODE_ID_HI=? and NODE_ID_LO=?"); + prep.setLong(1, id.getMostSignificantBits()); + prep.setLong(2, id.getLeastSignificantBits()); + prep.executeUpdate(); + conn.close(); + + // login and try the operation + s = rep.login(new SimpleCredentials("admin", "admin".toCharArray())); + test = s.getRootNode().getNode("test"); + + switch (testCase) { + case 1: + // case 1: try to add a node with the same name + test.addNode("missing"); + s.save(); + break; + case 2: + // case 2: try to delete the parent node + test.remove(); + s.save(); + break; + } + + s.logout(); + rep.shutdown(); + } + +} Index: src/main/java/org/apache/jackrabbit/core/NodeImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/NodeImpl.java (revision 1032503) +++ src/main/java/org/apache/jackrabbit/core/NodeImpl.java (working copy) @@ -613,8 +613,12 @@ } // 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) { + log.warn("Node " + childId + " not found, ignore", e); + } // remove the child node entry if (!thisState.removeChildNodeEntry(childId)) { @@ -663,9 +667,16 @@ // 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) { + log.warn("Child named " + entry.getName() + " (index " + entry.getIndex() + ", " + + "node id " + childId + ") " + + "not found when trying to remove " + getPath() + " " + + "(node id " + getNodeId() + ") - ignored", e); + } thisState.removeChildNodeEntry(childId); } } @@ -1248,9 +1259,31 @@ } // check for name collisions - NodeState thisState = data.getNodeState(); - ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1); - if (cne != null) { + NodeImpl existing; + while (true) { + NodeState thisState = data.getNodeState(); + // loop until we either find an existing child node, + // or we are sure no such child node exists + ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1); + existing = null; + if (cne != null) { + try { + existing = itemMgr.getNode(cne.getId(), getNodeId()); + } catch (ItemNotFoundException e) { + // the item was removed in the persistence manager, + // which is a repository inconsistency - there is nothing + // we can do, so the child node entry is removed now + log.warn("Node " + getPath() + "/" + cne.getName() + " " + + "(index " + cne.getIndex() + ", id " + cne.getId() + + "parent id " + getId() + ") " + + "not found; removing", e); + removeChildNode(cne.getId()); + continue; + } + } + break; + } + if (existing != null) { // there's already a child node entry with that name; // check same-name sibling setting of new node if (!def.allowsSameNameSiblings()) { @@ -1259,7 +1292,6 @@ + itemMgr.safeGetJCRPath(nodePath)); } // check same-name sibling setting of existing node - NodeImpl existing = itemMgr.getNode(cne.getId(), getNodeId()); if (!existing.getDefinition().allowsSameNameSiblings()) { throw new ItemExistsException( "Same-name siblings not allowed for " + existing);