You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2010/11/30 17:04:19 UTC

svn commit: r1040601 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ test/java/org/apache/jackrabbit/core/persistence/

Author: thomasm
Date: Tue Nov 30 16:04:18 2010
New Revision: 1040601

URL: http://svn.apache.org/viewvc?rev=1040601&view=rev
Log:
JCR-2740 On missing child node, automatically remove the entry

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/VersionManagerImpl.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java?rev=1040601&r1=1040600&r2=1040601&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java Tue Nov 30 16:04:18 2010
@@ -713,7 +713,7 @@ public class ItemManager implements Item
             childIds.add(entry.getId());
         }
 
-        return new LazyItemIterator(this, childIds, parentId);
+        return new LazyItemIterator(sessionContext, childIds, parentId);
     }
 
     /**
@@ -774,7 +774,7 @@ public class ItemManager implements Item
             childIds.add(id);
         }
 
-        return new LazyItemIterator(this, childIds);
+        return new LazyItemIterator(sessionContext, childIds);
     }
 
     //-------------------------------------------------< item factory methods >

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java?rev=1040601&r1=1040600&r2=1040601&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/LazyItemIterator.java Tue Nov 30 16:04:18 2010
@@ -31,6 +31,8 @@ import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.id.ItemId;
 import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.session.SessionContext;
+import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,6 +55,11 @@ public class LazyItemIterator implements
     /** Logger instance for this class */
     private static Logger log = LoggerFactory.getLogger(LazyItemIterator.class);
 
+    /**
+     * The session context used to access the repository.
+     */
+    private final SessionContext sessionContext;
+
     /** the item manager that is used to lazily fetch the items */
     private final ItemManager itemMgr;
 
@@ -71,11 +78,11 @@ public class LazyItemIterator implements
     /**
      * Creates a new <code>LazyItemIterator</code> instance.
      *
-     * @param itemMgr item manager
+     * @param sessionContext session context
      * @param idList  list of item id's
      */
-    public LazyItemIterator(ItemManager itemMgr, List< ? extends ItemId> idList) {
-        this(itemMgr, idList, null);
+    public LazyItemIterator(SessionContext sessionContext, List< ? extends ItemId> idList) {
+        this(sessionContext, idList, null);
     }
 
     /**
@@ -83,12 +90,13 @@ public class LazyItemIterator implements
      * a parent id as parameter. This version should be invoked to strictly return
      * children nodes of a node.
      *
-     * @param itemMgr item manager
+     * @param sessionContext session context
      * @param idList  list of item id's
      * @param parentId parent id.
      */
-    public LazyItemIterator(ItemManager itemMgr, List< ? extends ItemId> idList, NodeId parentId) {
-        this.itemMgr = itemMgr;
+    public LazyItemIterator(SessionContext sessionContext, List< ? extends ItemId> idList, NodeId parentId) {
+        this.sessionContext = sessionContext;
+        this.itemMgr = sessionContext.getSessionImpl().getItemManager();
         this.idList = new ArrayList<ItemId>(idList);
         this.parentId = parentId;
         // prefetch first item
@@ -117,6 +125,24 @@ public class LazyItemIterator implements
                 log.debug("ignoring nonexistent item " + id);
                 // remove invalid id
                 idList.remove(pos);
+
+                // maybe fix the root cause
+                if (parentId != null && sessionContext.getSessionImpl().autoFixCorruptions()) {
+                    try {
+                        // it might be an access right problem
+                        // we need to check if the item doesn't exist in the ism
+                        ItemStateManager ism = sessionContext.getItemStateManager();
+                        if (!ism.hasItemState(id)) {
+                            NodeImpl p = (NodeImpl) itemMgr.getItem(parentId);
+                            p.removeChildNode((NodeId) id);
+                            p.save();
+                        }
+                    } catch (RepositoryException e2) {
+                        log.error("could not fix repository inconsistency", e);
+                        // ignore
+                    }
+                }
+
                 // try next
             } catch (AccessDeniedException e) {
                 log.debug("ignoring nonexistent item " + id);

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=1040601&r1=1040600&r2=1040601&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Tue Nov 30 16:04:18 2010
@@ -98,6 +98,7 @@ import org.apache.jackrabbit.core.sessio
 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;
@@ -555,7 +556,7 @@ public class NodeImpl extends ItemImpl i
      * @param id
      * @param newName
      * @throws RepositoryException
-     * @deprecated use #renameChildNode(NodeId, Name, boolean) 
+     * @deprecated use #renameChildNode(NodeId, Name, boolean)
      */
     protected void renameChildNode(Name oldName, int index, NodeId id,
                                    Name newName)
@@ -613,8 +614,24 @@ public class NodeImpl extends ItemImpl i
         }
 
         // 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 (sessionContext.getSessionImpl().autoFixCorruptions()) {
+                // it might be an access right problem
+                // we need to check if the item doesn't exist in the ism
+                ItemStateManager ism = sessionContext.getItemStateManager();
+                if (!ism.hasItemState(childId)) {
+                    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 @@ public class NodeImpl extends ItemImpl i
                 // 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 && sessionContext.getSessionImpl().autoFixCorruptions()) {
+                        // it might be an access right problem
+                        // we need to check if the item doesn't exist in the ism
+                        ItemStateManager ism = 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);
             }
         }
@@ -1418,7 +1454,7 @@ public class NodeImpl extends ItemImpl i
             log.debug(msg);
             throw new AccessDeniedException(msg);
         }
-        
+
         ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(data.getNodeState().getChildNodeEntries());
         int srcInd = -1, destInd = -1;
         for (int i = 0; i < list.size(); i++) {
@@ -3022,7 +3058,7 @@ public class NodeImpl extends ItemImpl i
                     }
                     idList = filteredList;
                 }
-                return new LazyItemIterator(itemMgr, idList);
+                return new LazyItemIterator(sessionContext, idList);
             } else {
                 // there are no references, return empty iterator
                 return PropertyIteratorAdapter.EMPTY;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=1040601&r1=1040600&r2=1040601&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Tue Nov 30 16:04:18 2010
@@ -122,6 +122,17 @@ public class SessionImpl extends Abstrac
     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 removing a child node.
+     *
+     * @since Apache Jackrabbit 2.2
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-2740">JCR-2740</a>
+     */
+    public static final String AUTO_FIX_CORRUPTIONS =
+        "org.apache.jackrabbit.autoFixCorruptions";
+
     private static Logger log = LoggerFactory.getLogger(SessionImpl.class);
 
     /**
@@ -772,6 +783,20 @@ public class SessionImpl extends Abstrac
     }
 
     /**
+     * Checks whether repository inconsistencies should be automatically fixed
+     * when traversing over child nodes, when trying to add a child node, or
+     * when removing a child node.
+     *
+     * @return <code>true</code> if the {@link #AUTO_FIX_CORRUPTIONS}
+     *         attribute is set, <code>false</code> otherwise
+     * @since Apache Jackrabbit 2.2
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-2740">JCR-2740</a>
+     */
+    protected boolean autoFixCorruptions() {
+        return getAttribute(AUTO_FIX_CORRUPTIONS) != null;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public boolean hasPendingChanges() throws RepositoryException {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/VersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/VersionManagerImpl.java?rev=1040601&r1=1040600&r2=1040601&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/VersionManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/VersionManagerImpl.java Tue Nov 30 16:04:18 2010
@@ -413,7 +413,7 @@ public class VersionManagerImpl extends 
                         Permission.VERSION_MNGMT);
                 List<ItemId> failedIds = new LinkedList<ItemId>();
                 mergeOrUpdate(state, srcWorkspaceName, failedIds, bestEffort, isShallow);
-                return new LazyItemIterator(session.getItemManager(), failedIds);
+                return new LazyItemIterator(context, failedIds);
             }
             public String toString() {
                 return "versionManager.merge("
@@ -592,7 +592,7 @@ public class VersionManagerImpl extends 
         }
         List<ItemId> failedIds = new ArrayList<ItemId>();
         merge(activity, failedIds);
-        return new LazyItemIterator(session.getItemManager(), failedIds);
+        return new LazyItemIterator(context, failedIds);
     }
 
     /**
@@ -642,4 +642,4 @@ public class VersionManagerImpl extends 
         }
     }
 
-}
\ No newline at end of file
+}

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java?rev=1040601&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java Tue Nov 30 16:04:18 2010
@@ -0,0 +1,119 @@
+/*
+ * 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.NodeIterator;
+import javax.jcr.Repository;
+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.TransientRepository;
+
+/**
+ * Tests that a corrupt node is automatically fixed.
+ */
+public class AutoFixCorruptNode extends TestCase {
+
+    private final String TEST_DIR = "target/temp/" + getClass().getSimpleName();
+
+    public void setUp() throws Exception {
+        FileUtils.deleteDirectory(new File(TEST_DIR));
+    }
+
+    public void tearDown() throws Exception {
+        setUp();
+    }
+
+    public void testAutoFix() throws Exception {
+
+        // new repository
+        TransientRepository rep = new TransientRepository(new File(TEST_DIR));
+        Session s = openSession(rep, false);
+        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:"+TEST_DIR+"/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 = openSession(rep, false);
+        test = s.getRootNode().getNode("test");
+
+        // try to add a node with the same name
+        try {
+            test.addNode("missing");
+            s.save();
+        } catch (RepositoryException e) {
+            // expected
+        }
+
+        s.logout();
+
+        s = openSession(rep, true);
+        test = s.getRootNode().getNode("test");
+        // iterate over all child nodes fixes the corruption
+        NodeIterator it = test.getNodes();
+        while (it.hasNext()) {
+            it.nextNode();
+        }
+
+        // try to add a node with the same name
+        test.addNode("missing");
+        s.save();
+
+        // try to delete the parent node
+        test.remove();
+        s.save();
+
+        s.logout();
+        rep.shutdown();
+
+        FileUtils.deleteDirectory(new File("repository"));
+
+    }
+
+    private Session openSession(Repository rep, boolean autoFix) throws RepositoryException {
+        SimpleCredentials cred = new SimpleCredentials("admin", "admin".toCharArray());
+        if (autoFix) {
+            cred.setAttribute("org.apache.jackrabbit.autoFixCorruptions", "true");
+        }
+        return rep.login(cred);
+    }
+
+}

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java?rev=1040601&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java Tue Nov 30 16:04:18 2010
@@ -0,0 +1,39 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all test cases for this package.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("org.apache.jackrabbit.core.persistence tests");
+
+        suite.addTestSuite(AutoFixCorruptNode.class);
+
+        return suite;
+    }
+}