Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java (revision 1404d55fb1d913a1e08f8b6634082184073213d1) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java (revision ) @@ -243,7 +243,9 @@ allInfos.putAll(batch); } - if (pm.exists(lastId)) { + if (allInfos.isEmpty()){ + log.info("No nodes exists, skipping"); + } else if (pm.exists(lastId)) { for (Map.Entry entry : allInfos.entrySet()) { checkBundleConsistency(entry.getKey(), entry.getValue(), allInfos); } Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java (revision 1404d55fb1d913a1e08f8b6634082184073213d1) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java (revision ) @@ -16,27 +16,22 @@ */ package org.apache.jackrabbit.core.data; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.jcr.RepositoryException; - -import org.apache.jackrabbit.core.cluster.ClusterException; +import junit.framework.TestCase; +import org.apache.jackrabbit.core.NamespaceRegistryImpl; +import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.cluster.ClusterNode; import org.apache.jackrabbit.core.cluster.SimpleClusterContext; import org.apache.jackrabbit.core.cluster.UpdateEventChannel; import org.apache.jackrabbit.core.cluster.UpdateEventListener; import org.apache.jackrabbit.core.config.ClusterConfig; +import org.apache.jackrabbit.core.fs.mem.MemoryFileSystem; import org.apache.jackrabbit.core.id.NodeId; import org.apache.jackrabbit.core.journal.Journal; import org.apache.jackrabbit.core.journal.JournalFactory; import org.apache.jackrabbit.core.journal.MemoryJournal; import org.apache.jackrabbit.core.journal.MemoryJournal.MemoryRecord; import org.apache.jackrabbit.core.observation.EventState; +import org.apache.jackrabbit.core.persistence.PMContext; import org.apache.jackrabbit.core.persistence.bundle.AbstractBundlePersistenceManager; import org.apache.jackrabbit.core.persistence.bundle.ConsistencyCheckerImpl; import org.apache.jackrabbit.core.persistence.check.ReportItem; @@ -50,8 +45,16 @@ import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; +import org.apache.jackrabbit.stats.RepositoryStatisticsImpl; -import junit.framework.TestCase; +import javax.jcr.RepositoryException; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; public class ConsistencyCheckerImplTest extends TestCase { @@ -65,9 +68,16 @@ private ClusterNode master; private ClusterNode slave; + private File directory; + @Override public void setUp() throws Exception { super.setUp(); + + directory = File.createTempFile("jackrabbit-persistence-", "-test"); + directory.delete(); + directory.mkdirs(); + master = createClusterNode("master"); master.start(); @@ -78,7 +88,7 @@ // Abandoned nodes are nodes that have a link to a parent but that // parent does not have a link back to the child - public void testFixAbandonedNode() throws RepositoryException, ClusterException { + public void testFixAbandonedNode() throws Exception { NodePropBundle bundle1 = new NodePropBundle(new NodeId(0, 0)); NodePropBundle bundle2 = new NodePropBundle(new NodeId(0, 1)); @@ -86,7 +96,7 @@ // a corresponding child node entry bundle2.setParentId(bundle1.getId()); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle1, bundle2)); + MockPersistenceManager pm = getMockPersistenceManager(bundle1, bundle2); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, master.createUpdateChannel("default")); // set up cluster event update listener @@ -116,7 +126,7 @@ assertTrue("Expected node1 to be modified", listener.changes.isModified(bundle1.getId())); } - public void testDoubleCheckAbandonedNode() throws RepositoryException { + public void testDoubleCheckAbandonedNode() throws Exception { NodePropBundle bundle1 = new NodePropBundle(new NodeId(0, 0)); NodePropBundle bundle2 = new NodePropBundle(new NodeId(0, 1)); @@ -124,7 +134,7 @@ // a corresponding child node entry bundle2.setParentId(bundle1.getId()); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle1, bundle2)); + MockPersistenceManager pm = getMockPersistenceManager(bundle1, bundle2); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); checker.check(null, false); @@ -151,7 +161,7 @@ * There was a bug where when there were multiple abandoned nodes by the same parent * only one of them was fixed. Hence this separate test case for this scenario. */ - public void testFixMultipleAbandonedNodesBySameParent() throws RepositoryException { + public void testFixMultipleAbandonedNodesBySameParent() throws Exception { NodePropBundle bundle1 = new NodePropBundle(new NodeId(0, 0)); NodePropBundle bundle2 = new NodePropBundle(new NodeId(0, 1)); NodePropBundle bundle3 = new NodePropBundle(new NodeId(1, 0)); @@ -161,7 +171,7 @@ bundle2.setParentId(bundle1.getId()); bundle3.setParentId(bundle1.getId()); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle1, bundle2, bundle3)); + MockPersistenceManager pm = getMockPersistenceManager(bundle1, bundle2, bundle3); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); checker.check(null, false); @@ -175,7 +185,7 @@ } // Orphaned nodes are those nodes who's parent does not exist - public void testAddOrphanedNodeToLostAndFound() throws RepositoryException, ClusterException { + public void testAddOrphanedNodeToLostAndFound() throws Exception { final NodeId lostAndFoundId = new NodeId(0, 0); NodePropBundle lostAndFound = new NodePropBundle(lostAndFoundId); // lost and found must be of type nt:unstructured @@ -186,7 +196,7 @@ // set non-existent parent node id orphaned.setParentId(new NodeId(1, 0)); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(lostAndFound, orphaned)); + MockPersistenceManager pm = getMockPersistenceManager(lostAndFound, orphaned); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, lostAndFoundId.toString(), master.createUpdateChannel("default")); @@ -221,11 +231,11 @@ assertTrue("Expected orphan to be modified", listener.changes.isModified(orphanedId)); } - public void testDoubleCheckOrphanedNode() throws RepositoryException { + public void testDoubleCheckOrphanedNode() throws Exception { NodePropBundle orphaned = new NodePropBundle(new NodeId(0, 1)); orphaned.setParentId(new NodeId(1, 0)); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(orphaned)); + MockPersistenceManager pm = getMockPersistenceManager(orphaned); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); checker.check(null, false); @@ -252,7 +262,7 @@ // Disconnected nodes are those nodes for which there are nodes // that have the node as its child, but the node itself does not // have those nodes as its parent - public void testFixDisconnectedNode() throws RepositoryException, ClusterException { + public void testFixDisconnectedNode() throws Exception { NodePropBundle bundle1 = new NodePropBundle(new NodeId(0, 0)); NodePropBundle bundle2 = new NodePropBundle(new NodeId(0, 1)); NodePropBundle bundle3 = new NodePropBundle(new NodeId(1, 0)); @@ -264,7 +274,7 @@ // node3 has node2 as parent bundle3.setParentId(bundle2.getId()); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle1, bundle2, bundle3)); + MockPersistenceManager pm = getMockPersistenceManager(bundle1, bundle2, bundle3); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, master.createUpdateChannel("default")); // set up cluster event update listener @@ -300,7 +310,7 @@ assertTrue("Expected node1 to be modified", listener.changes.isModified(bundle1.getId())); } - public void testDoubleCheckDisonnectedNode() throws RepositoryException { + public void testDoubleCheckDisonnectedNode() throws Exception { NodePropBundle bundle1 = new NodePropBundle(new NodeId(0, 0)); NodePropBundle bundle2 = new NodePropBundle(new NodeId(0, 1)); NodePropBundle bundle3 = new NodePropBundle(new NodeId(1, 0)); @@ -312,7 +322,7 @@ // node3 has node2 as parent bundle3.setParentId(bundle2.getId()); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle1, bundle2, bundle3)); + MockPersistenceManager pm = getMockPersistenceManager(bundle1, bundle2, bundle3); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); checker.check(null, false); @@ -335,12 +345,11 @@ assertTrue("Double check didn't remove invalid error", checker.getReport().getItems().isEmpty()); } - public void testFixMissingNode() throws RepositoryException, ClusterException { + public void testFixMissingNode() throws Exception { NodePropBundle bundle = new NodePropBundle(new NodeId(0, 0)); bundle.addChildNodeEntry(nameFactory.create("", "test"), new NodeId(0, 1)); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle)); - + MockPersistenceManager pm = getMockPersistenceManager(bundle); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, master.createUpdateChannel("default")); // set up cluster event update listener @@ -368,13 +377,12 @@ assertTrue("Expected node to be modified", listener.changes.isModified(bundle.getId())); } - public void testDoubleCheckMissingNode() throws RepositoryException { + public void testDoubleCheckMissingNode() throws Exception { NodePropBundle bundle = new NodePropBundle(new NodeId(0, 0)); final NodeId childNodeId = new NodeId(0, 1); bundle.addChildNodeEntry(nameFactory.create("", "test"), childNodeId); - MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundle)); - + MockPersistenceManager pm = getMockPersistenceManager(bundle); ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); checker.check(null, false); @@ -396,7 +404,18 @@ checker.doubleCheckErrors(); assertTrue("Double check didn't remove invalid error", checker.getReport().getItems().isEmpty()); + } + // https://issues.apache.org/jira/browse/JCR-3929 + // + public void testCheckEmptyNodeList() throws Exception { + MockPersistenceManager pm = getMockPersistenceManager(); + ConsistencyCheckerImpl checker = new ConsistencyCheckerImpl(pm, null, null, null); + + checker.check(null, false); + checker.repair(); + + assertTrue("No errors expected for empty node list", checker.getReport().getItems().isEmpty()); } private ClusterNode createClusterNode(String id) throws Exception { @@ -423,6 +442,19 @@ return clusterNode; } + private MockPersistenceManager getMockPersistenceManager(NodePropBundle... bundles) throws Exception { + MockPersistenceManager pm = new MockPersistenceManager(Arrays.asList(bundles)); + pm.init(new PMContext( + directory, + new MemoryFileSystem(), + RepositoryImpl.ROOT_NODE_ID, + new NamespaceRegistryImpl(new MemoryFileSystem()), + null, + null, + new RepositoryStatisticsImpl())); + return pm; + } + private static class MockPersistenceManager extends AbstractBundlePersistenceManager { private Map bundles = new LinkedHashMap(); @@ -433,7 +465,13 @@ } } - public List getAllNodeIds(final NodeId after, final int maxCount) throws ItemStateException, RepositoryException { + @Override + protected BLOBStore getBlobStore() { + return null; + } + + @Override + public List getAllNodeIds(NodeId after, int maxCount) throws ItemStateException, RepositoryException { List allNodeIds = new ArrayList(); boolean add = after == null; for (NodeId nodeId : bundles.keySet()) { @@ -448,15 +486,21 @@ } @Override - protected NodePropBundle loadBundle(final NodeId id) { - return bundles.get(id); + public NodeReferences loadReferencesTo(NodeId id) throws NoSuchItemStateException, ItemStateException { + return null; } @Override - protected void evictBundle(final NodeId id) { + public boolean existsReferencesTo(NodeId targetId) throws ItemStateException { + return false; } @Override + protected NodePropBundle loadBundle(final NodeId id) { + return bundles.get(id); + } + + @Override protected void storeBundle(final NodePropBundle bundle) throws ItemStateException { bundles.put(bundle.getId(), bundle); } @@ -472,19 +516,6 @@ @Override protected void store(final NodeReferences refs) throws ItemStateException { - } - - @Override - protected BLOBStore getBlobStore() { - return null; - } - - public NodeReferences loadReferencesTo(final NodeId id) throws NoSuchItemStateException, ItemStateException { - return null; - } - - public boolean existsReferencesTo(final NodeId targetId) throws ItemStateException { - return false; } } \ No newline at end of file Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestAll.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestAll.java (revision 1404d55fb1d913a1e08f8b6634082184073213d1) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestAll.java (revision ) @@ -36,6 +36,7 @@ TestSuite suite = new ConcurrentTestSuite("Data tests"); suite.addTestSuite(ConcurrentGcTest.class); + suite.addTestSuite(ConsistencyCheckerImplTest.class); suite.addTestSuite(CopyValueTest.class); suite.addTestSuite(DataStoreAPITest.class); suite.addTestSuite(DataStoreTest.class); @@ -44,13 +45,13 @@ suite.addTestSuite(GarbageCollectorTest.class); suite.addTestSuite(GCConcurrentTest.class); suite.addTestSuite(GCEventListenerTest.class); + suite.addTestSuite(GCSubtreeMoveTest.class); suite.addTestSuite(LazyFileInputStreamTest.class); suite.addTestSuite(NodeTypeTest.class); suite.addTestSuite(OpenFilesTest.class); suite.addTestSuite(PersistenceManagerIteratorTest.class); suite.addTestSuite(TestTwoGetStreams.class); suite.addTestSuite(WriteWhileReadingTest.class); - suite.addTestSuite(GCSubtreeMoveTest.class); return suite; }