You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by re...@apache.org on 2011/10/11 15:34:11 UTC

svn commit: r1181777 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/persistence/check/ main/java/org/apache/jackrabbit/core/persistence/pool/ test/java/org/apache/jackrabbit/core/ test/java/org/apache/jackrabbit/core/p...

Author: reschke
Date: Tue Oct 11 13:34:10 2011
New Revision: 1181777

URL: http://svn.apache.org/viewvc?rev=1181777&view=rev
Log:
JCR-3069: add new ConsistencyChecker interface, implement it in BundleDbPersistenceManager, use it in ConcurrentImportTest, extend AutoFixCorruptNode to check it (work-in-progress)

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyChecker.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReport.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReportImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItem.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItemImpl.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrentImportTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestHelper.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyChecker.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyChecker.java?rev=1181777&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyChecker.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyChecker.java Tue Oct 11 13:34:10 2011
@@ -0,0 +1,49 @@
+/*
+ * 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.check;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Optional interface for Persistence Managers. Allows running consistency
+ * checks similar to the base one (see @link
+ * {@link PersistenceManager#checkConsistency(String[], boolean, boolean)) but
+ * providing a result that can be acted upon.
+ * <p>
+ * <em>Beware: this interface is designed for unit tests only.</em>
+ */
+public interface ConsistencyChecker {
+
+    /**
+     * Perform a consistency check of the data. An example are non-existent
+     * nodes referenced in a child node entry. The existence of this feature and
+     * the scope of the implementation can vary in different PersistenceManager
+     * implementations.
+     * 
+     * @param uuids
+     *            list of UUIDs of nodes to be checked. if null, all nodes will
+     *            be checked
+     * @param recursive
+     *            if true, the tree(s) below the given node(s) will be traversed
+     *            and checked as well
+     * @param fix
+     *            if true, any problems found that can be repaired will be
+     *            repaired. if false, no data will be modified, instead all
+     *            inconsistencies will only get logged
+     */
+    ConsistencyReport check(String[] uuids, boolean recursive, boolean fix) throws RepositoryException;
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReport.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReport.java?rev=1181777&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReport.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReport.java Tue Oct 11 13:34:10 2011
@@ -0,0 +1,40 @@
+/*
+ * 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.check;
+
+import java.util.Set;
+
+/**
+ * Returned as result of a {@link ConsistencyChecker} run.
+ */
+public interface ConsistencyReport {
+    
+    /**
+     * @return number of nodes that were checked
+     */
+    public int getNodeCount();
+
+    /** 
+     * @return elapsed time in ms
+     */
+    public long getElapsedTimeMs();
+
+    /**
+     * @return generated messages
+     */
+    public Set<ReportItem> getItems();
+}
\ No newline at end of file

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReportImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReportImpl.java?rev=1181777&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReportImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ConsistencyReportImpl.java Tue Oct 11 13:34:10 2011
@@ -0,0 +1,51 @@
+/*
+ * 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.check;
+
+import java.util.Set;
+
+public class ConsistencyReportImpl implements ConsistencyReport {
+
+    private final int nodeCount;
+    private final long elapsedTimeMs;
+    private final Set<ReportItem> reports;
+
+    public ConsistencyReportImpl(int nodeCount, long elapsedTimeMs,
+            Set<ReportItem> reports) {
+        this.nodeCount = nodeCount;
+        this.elapsedTimeMs = elapsedTimeMs;
+        this.reports = reports;
+    }
+
+    public int getNodeCount() {
+        return nodeCount;
+    }
+
+    public long getElapsedTimeMs() {
+        return elapsedTimeMs;
+    }
+
+    public Set<ReportItem> getItems() {
+        return reports;
+    }
+
+    @Override
+    public String toString() {
+        return "elapsedTimeMs " + elapsedTimeMs + ", nodeCount " + nodeCount
+                + ", reports: " + reports;
+    }
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItem.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItem.java?rev=1181777&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItem.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItem.java Tue Oct 11 13:34:10 2011
@@ -0,0 +1,33 @@
+/*
+ * 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.check;
+
+/**
+ * An item reported inside a {@link ConsistencyChecker.Report}.
+ */
+public interface ReportItem {
+
+    /**
+     * @return node id to which the message applies
+     */
+    public String getNodeId();
+
+    /**
+     * @return message
+     */
+    public String getMessage();
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItemImpl.java?rev=1181777&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItemImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/check/ReportItemImpl.java Tue Oct 11 13:34:10 2011
@@ -0,0 +1,41 @@
+/*
+ * 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.check;
+
+public class ReportItemImpl implements ReportItem {
+
+    private final String nodeId;
+    private final String message;
+
+    public ReportItemImpl(String nodeId, String message) {
+        this.nodeId = nodeId;
+        this.message = message;
+    }
+
+    public String getNodeId() {
+        return nodeId;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public String toString() {
+        return nodeId + " -- " + message;
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java?rev=1181777&r1=1181776&r2=1181777&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java Tue Oct 11 13:34:10 2011
@@ -27,9 +27,11 @@ import java.sql.SQLException;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 
 import javax.jcr.RepositoryException;
 import javax.sql.DataSource;
@@ -42,6 +44,11 @@ import org.apache.jackrabbit.core.id.Nod
 import org.apache.jackrabbit.core.id.PropertyId;
 import org.apache.jackrabbit.core.persistence.PMContext;
 import org.apache.jackrabbit.core.persistence.bundle.AbstractBundlePersistenceManager;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyChecker;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReport;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReportImpl;
+import org.apache.jackrabbit.core.persistence.check.ReportItem;
+import org.apache.jackrabbit.core.persistence.check.ReportItemImpl;
 import org.apache.jackrabbit.core.persistence.util.BLOBStore;
 import org.apache.jackrabbit.core.persistence.util.BundleBinding;
 import org.apache.jackrabbit.core.persistence.util.ErrorHandling;
@@ -85,7 +92,7 @@ import org.slf4j.LoggerFactory;
  * </ul>
  */
 public class BundleDbPersistenceManager
-        extends AbstractBundlePersistenceManager implements DatabaseAware {
+        extends AbstractBundlePersistenceManager implements DatabaseAware, ConsistencyChecker {
 
     /** the default logger */
     private static Logger log = LoggerFactory.getLogger(BundleDbPersistenceManager.class);
@@ -700,6 +707,12 @@ public class BundleDbPersistenceManager
         return new DbBlobStore();
     }
 
+    private void addMessage(Set<ReportItem> reports, NodeId id, String message) {
+        if (reports != null) {
+            reports.add(new ReportItemImpl(id.toString(), message));
+        }
+    }
+
     /**
      * Checks a single bundle for inconsistencies, ie. inexistent child nodes
      * and inexistent parents.
@@ -711,7 +724,8 @@ public class BundleDbPersistenceManager
      * {@linkplain NodePropBundle bundles} here
      */
     protected void checkBundleConsistency(NodeId id, NodePropBundle bundle,
-                                          boolean fix, Collection<NodePropBundle> modifications) {
+                                          boolean fix, Collection<NodePropBundle> modifications,
+                                          Set<ReportItem> reports) {
         //log.info(name + ": checking bundle '" + id + "'");
 
         // skip all system nodes except root node
@@ -732,20 +746,25 @@ public class BundleDbPersistenceManager
             try {
                 // analyze child node bundles
                 NodePropBundle child = loadBundle(entry.getId());
+                String message = null;
                 if (child == null) {
-                    log.error(
-                            "NodeState '" + id + "' references inexistent child"
-                            + " '" + entry.getName() + "' with id "
-                            + "'" + entry.getId() + "'");
+                    message = "NodeState '" + id + "' references inexistent child" + " '"
+                            + entry.getName() + "' with id " + "'" + entry.getId() + "'";
+                    log.error(message);
                     missingChildren.add(entry);
                 } else {
                     NodeId cp = child.getParentId();
                     if (cp == null) {
-                        log.error("ChildNode has invalid parent uuid: <null>");
+                        message = "ChildNode has invalid parent uuid: <null>";
+                        log.error(message);
                     } else if (!cp.equals(id)) {
-                        log.error("ChildNode has invalid parent uuid: '" + cp + "' (instead of '" + id + "')");
+                        message = "ChildNode has invalid parent uuid: '" + cp + "' (instead of '" + id + "')";
+                        log.error(message);
                     }
                 }
+                if (message != null) {
+                    addMessage(reports, id, message);
+                }
             } catch (ItemStateException e) {
                 // problem already logged (loadBundle called with logDetailedErrors=true)
             }
@@ -764,7 +783,9 @@ public class BundleDbPersistenceManager
             // skip root nodes (that point to itself)
             if (parentId != null && !id.toString().endsWith("babecafebabe")) {
                 if (loadBundle(parentId) == null) {
-                    log.error("NodeState '" + id + "' references inexistent parent uuid '" + parentId + "'");
+                    String message = "NodeState '" + id + "' references inexistent parent uuid '" + parentId + "'";
+                    log.error(message);
+                    addMessage(reports, id, message);
                 }
             	NodePropBundle parentBundle = loadBundle(parentId);
             	Iterator<NodePropBundle.ChildNodeEntry> childNodeIter = parentBundle.getChildNodeEntries().iterator();
@@ -776,29 +797,54 @@ public class BundleDbPersistenceManager
                     	break;
                     }
             	}
-            	if (!found) {
-            		log.error("NodeState '" + id + "' is not referenced by its parent node '" + parentId + "'");
-            		int l = (int) System.currentTimeMillis();
-            		int r = new Random().nextInt();
-            		int n = l + r; 
-            		String nodeName = Integer.toHexString(n);
-            		parentBundle.addChildNodeEntry(NameFactoryImpl.getInstance().create("{}" + nodeName), id);
-            		log.info("NodeState '" + id + "' adds itself to its parent node '" + parentId + "' with a new name '" + nodeName+ "'");
-            		modifications.add(parentBundle);
-            	}
+                if (!found) {
+                    String message = "NodeState '" + id + "' is not referenced by its parent node '" + parentId + "'";
+                    log.error(message);
+                    addMessage(reports, id, message);
+
+                    int l = (int) System.currentTimeMillis();
+                    int r = new Random().nextInt();
+                    int n = l + r;
+                    String nodeName = Integer.toHexString(n);
+                    parentBundle.addChildNodeEntry(NameFactoryImpl
+                            .getInstance().create("{}" + nodeName), id);
+                    log.info("NodeState '" + id + "' adds itself to its parent node '" + parentId + "' with a new name '" + nodeName + "'");
+                    modifications.add(parentBundle);
+                }
             }
         } catch (ItemStateException e) {
             log.error("Error reading node '" + parentId + "' (parent of '" + id + "'): " + e);
         }
     }
 
+    public ConsistencyReport check(String[] uuids, boolean recursive,
+            boolean fix) throws RepositoryException {
+
+        Set<ReportItem> reports = new HashSet<ReportItem>();
+
+        long tstart = System.currentTimeMillis();
+        int total = internalCheckConsistency(uuids, recursive, fix, reports);
+        long elapsed = System.currentTimeMillis() - tstart;
+
+        return new ConsistencyReportImpl(total, elapsed, reports);
+    }
+
     public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+        try {
+            internalCheckConsistency(uuids, recursive, fix, null);
+        }
+        catch (RepositoryException ex) {
+            log.error("While running consistency check.", ex);
+        }
+    }
+    
+    private int internalCheckConsistency(String[] uuids, boolean recursive, boolean fix, Set<ReportItem> reports) throws RepositoryException {
         int count = 0;
         int total = 0;
         Collection<NodePropBundle> modifications = new ArrayList<NodePropBundle>();        
         
         if (uuids == null) {
-            // get all node bundles in the database with a single sql statement,
+            // get all node bundles in the database with a single SQL statement,
             // which is (probably) faster than loading each bundle and traversing the tree              
             ResultSet rs = null;
             try {               
@@ -806,8 +852,9 @@ public class BundleDbPersistenceManager
                 rs = conHelper.exec(sql, new Object[0], false, 0);
                 try {
                     if (!rs.next()) {
-                        log.error("Could not retrieve total number of bundles. empty result set.");
-                        return;
+                        String message = "Could not retrieve total number of bundles. empty result set.";
+                        log.error(message);
+                        throw new RepositoryException(message);
                     }
                     total = rs.getInt(1);
                 } finally {
@@ -838,7 +885,7 @@ public class BundleDbPersistenceManager
                         }
                         // parse and check bundle
                         NodePropBundle bundle = readBundle(id, bRs, 1);
-                        checkBundleConsistency(id, bundle, fix, modifications);
+                        checkBundleConsistency(id, bundle, fix, modifications, reports);
                     } catch (SQLException e) {
                         log.error("Unable to parse bundle " + id, e);
                     } finally {
@@ -887,7 +934,7 @@ public class BundleDbPersistenceManager
                         continue;
                     }
 
-                    checkBundleConsistency(id, bundle, fix, modifications);
+                    checkBundleConsistency(id, bundle, fix, modifications, reports);
 
                     if (recursive) {
                         for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) {
@@ -923,6 +970,8 @@ public class BundleDbPersistenceManager
         }
 
         log.info(name + ": checked " + count + "/" + total + " bundles.");
+
+        return total;
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrentImportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrentImportTest.java?rev=1181777&r1=1181776&r2=1181777&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrentImportTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ConcurrentImportTest.java Tue Oct 11 13:34:10 2011
@@ -27,7 +27,9 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
 import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReport;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.test.NotExecutableException;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -51,9 +53,14 @@ public class ConcurrentImportTest extend
      * performed by the threads.
      */
     private static final int NUM_NODES = 10;
-
+    
     public void testConcurrentImport() throws RepositoryException {
-        concurrentImport(new String[]{JcrConstants.MIX_REFERENCEABLE}, false);
+        try {
+            concurrentImport(new String[]{JcrConstants.MIX_REFERENCEABLE}, false);
+        }
+        finally {
+            checkConsistency();
+        }
     }
 
     public void testConcurrentImportSynced() throws RepositoryException {
@@ -206,4 +213,12 @@ public class ConcurrentImportTest extend
 
     }
 
-}
\ No newline at end of file
+    private void checkConsistency() throws RepositoryException {
+        try {
+            ConsistencyReport rep = TestHelper.checkConsistency(testRootNode.getSession());
+            assertEquals("Found broken nodes in repository: " + rep, 0, rep.getItems().size());
+        } catch (NotExecutableException ex) {
+            // ignore
+        }
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestHelper.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestHelper.java?rev=1181777&r1=1181776&r2=1181777&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestHelper.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/TestHelper.java Tue Oct 11 13:34:10 2011
@@ -16,7 +16,14 @@
  */
 package org.apache.jackrabbit.core;
 
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyChecker;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReport;
+import org.apache.jackrabbit.test.NotExecutableException;
 
 /**
  * <code>TestHelper</code> provides test utility methods.
@@ -35,4 +42,31 @@ public class TestHelper {
             throws RepositoryException {
         repo.getWorkspaceInfo(name).dispose();
     }
+
+    /**
+     * Runs a consistency check on the workspace used by the specified session.
+     *
+     * @param session the Session accessing the workspace to be checked
+     * @throws RepositoryException if an error occurs while getting the
+     * workspace with the given name.
+     * @throws NotExecutableException if the {@link PersistenceManager} does
+     * not implement {@link ConsistencyChecker}, or if the associated
+     * {@link Repository} is not a {@link RepositoryImpl}.
+     */
+    public static ConsistencyReport checkConsistency(Session session)
+            throws NotExecutableException, RepositoryException {
+        Repository r = session.getRepository();
+        if (!(r instanceof RepositoryImpl)) {
+            throw new NotExecutableException();
+        } else {
+            RepositoryImpl ri = (RepositoryImpl) r;
+            PersistenceManager pm = ri.getWorkspaceInfo(
+                    session.getWorkspace().getName()).getPersistenceManager();
+            if (!(pm instanceof ConsistencyChecker)) {
+                throw new NotExecutableException();
+            } else {
+                return ((ConsistencyChecker) pm).check(null, true, false);
+            }
+        }
+    }
 }

Modified: 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=1181777&r1=1181776&r2=1181777&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/AutoFixCorruptNode.java Tue Oct 11 13:34:10 2011
@@ -21,15 +21,20 @@ 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.TestHelper;
 import org.apache.jackrabbit.core.TransientRepository;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReport;
 
 /**
  * Tests that a corrupt node is automatically fixed.
@@ -46,6 +51,51 @@ public class AutoFixCorruptNode extends 
         setUp();
     }
 
+    /**
+     * Unit test for <a
+     * href="https://issues.apache.org/jira/browse/JCR-3069">JCR-3069</a>
+     */
+    public void testAutoFixWithConsistencyCheck() 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();
+
+        s = openSession(rep, false);
+        try {
+            ConsistencyReport r = TestHelper.checkConsistency(s);
+            assertNotNull(r);
+            assertNotNull(r.getItems());
+            assertEquals(1, r.getItems().size());
+            assertEquals(test.getIdentifier(), r.getItems().iterator().next()
+                    .getNodeId());
+        } finally {
+            s.logout();
+            rep.shutdown();
+            FileUtils.deleteDirectory(new File("repository"));
+        }
+
+    }
+
     public void testAutoFix() throws Exception {
 
         // new repository
@@ -62,10 +112,10 @@ public class AutoFixCorruptNode extends 
         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=?");
+        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();
@@ -108,10 +158,13 @@ public class AutoFixCorruptNode extends 
 
     }
 
-    private Session openSession(Repository rep, boolean autoFix) throws RepositoryException {
-        SimpleCredentials cred = new SimpleCredentials("admin", "admin".toCharArray());
+    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");
+            cred.setAttribute("org.apache.jackrabbit.autoFixCorruptions",
+                    "true");
         }
         return rep.login(cred);
     }