You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2008/03/03 10:17:38 UTC

svn commit: r632996 - in /jackrabbit/branches/1.3/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/persistence/ main/java/org/apache/jackrabbit/core/persistence/bundle/ main/java/org/apache/jackrabbit/core...

Author: jukka
Date: Mon Mar  3 01:17:36 2008
New Revision: 632996

URL: http://svn.apache.org/viewvc?rev=632996&view=rev
Log:
JCR-1428: Add API for selective bundle consistency check (Jackrabbit-specific)
    - 1.3: Patches by Alexander Klimetschek

Modified:
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
    jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
    jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Mon Mar  3 01:17:36 2008
@@ -1163,6 +1163,78 @@
         return new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true, cacheFactory);
     }
 
+    /**
+     * Checks the consistency of the given nodes in the given workspace or the
+     * versioning subtree. Use <code>jcr:system</code> as workspace name for
+     * checking the versioning tree. The <code>recursive</code> flag indicates
+     * whether the tree below the given nodes shall be traversed and checked as
+     * well.
+     * 
+     * <p>
+     * Note: This must be called right after Jackrabbit startup before the
+     * workspace in question (or the system tree) was accessed, ie. a session
+     * was opened. Otherwise Jackrabbit will be in an inconsistent state and
+     * various errors will happen. Be also sure to have logging enabled before
+     * this method is called to catch all messages of the check.
+     * 
+     * <p>
+     * To use this method, you will have to subclass {@link RepositoryImpl},
+     * make it public and use an instance of the subclass as the repository.
+     * 
+     * <h4>Consistency check details:</h4>
+     * <p>
+     * The check is currently restricted to {@link PersistenceManager}s, more
+     * specifically the {@link BundleDbPersistenceManager}s. They will check
+     * for parent-child relationships of nodes, where either parent or child
+     * nodes no longer exist. In the case of missing child nodes, these will be
+     * repaired by removing them. Any other problems will be logged with ERROR
+     * log level for further analysis. When bundles are fixed, ie.
+     * <code>fix==true</code>, set INFO logging on the specific persistence
+     * manager, to see which bundles get repaired:
+     * 
+     * <pre>
+     * log4j.logger.org.apache.jackrabbit.core.persistence.bundle.BundleDbPersistenceManager=INFO, stdout
+     * </pre>
+     * 
+     * When more details for all checked node bundles shall be displayed, enable
+     * INFO logging for the BundleBinding:
+     * 
+     * <pre>
+     * log4j.logger.org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding=INFO, stdout
+     * </pre>
+     * 
+     * @param workspaceName
+     *            name of the workspace to be checked. Use
+     *            <code>jcr:system</code> to check the versioning subtree.
+     * @param uuids
+     *            list of UUIDs of nodes to be checked. if null, all nodes in
+     *            the workspace will be checked (and the recursive flag will be
+     *            ignored)
+     * @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
+     * 
+     * @throws NoSuchWorkspaceException
+     *             if the workspace does not exist
+     * @throws IllegalStateException
+     *             if this repository has been shut down
+     * @throws RepositoryException
+     *             if the persistence manager could not be initialized
+     */
+    protected void checkConsistency(String workspaceName, String[] uuids, boolean recursive, boolean fix) throws IllegalStateException, NoSuchWorkspaceException, RepositoryException {
+        if ("jcr:system".equals(workspaceName)) {
+            PersistenceManager pm = vMgr.getPersistenceManager();
+            pm.checkConsistency(uuids, recursive, fix);
+        } else {
+            PersistenceManager pm = getWorkspaceInfo(workspaceName).getPersistenceManager();
+            pm.checkConsistency(uuids, recursive, fix);
+        }
+    }
+    
     //-----------------------------------------------------------< Repository >
     /**
      * {@inheritDoc}

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java Mon Mar  3 01:17:36 2008
@@ -96,6 +96,14 @@
             }
         }
     }
+    
+    /**
+     * This implementation does nothing.
+     *  
+     * {@inheritDoc}
+     */
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+    }
 
     /**
      * Store a node state. Subclass responsibility.

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java Mon Mar  3 01:17:36 2008
@@ -180,4 +180,23 @@
      */
     void store(ChangeLog changeLog) throws ItemStateException;
 
+    /**
+     * 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
+     */
+    void checkConsistency(String[] uuids, boolean recursive, boolean fix);
+
 }

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java Mon Mar  3 01:17:36 2008
@@ -701,4 +701,12 @@
             bundles.put(bundle);
         }
     }
+
+    /**
+     * This implementation does nothing.
+     *  
+     * {@inheritDoc}
+     */
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+    }
 }

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java Mon Mar  3 01:17:36 2008
@@ -16,32 +16,6 @@
  */
 package org.apache.jackrabbit.core.persistence.bundle;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.util.Text;
-import org.apache.jackrabbit.core.state.ChangeLog;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.NoSuchItemStateException;
-import org.apache.jackrabbit.core.state.NodeReferencesId;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.persistence.PMContext;
-import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager;
-import org.apache.jackrabbit.core.persistence.bundle.util.ConnectionRecoveryManager;
-import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex;
-import org.apache.jackrabbit.core.persistence.bundle.util.NodePropBundle;
-import org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding;
-import org.apache.jackrabbit.core.persistence.bundle.util.ErrorHandling;
-import org.apache.jackrabbit.core.persistence.bundle.util.StringIndex;
-import org.apache.jackrabbit.core.persistence.util.Serializer;
-import org.apache.jackrabbit.core.persistence.util.BLOBStore;
-import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
-import org.apache.jackrabbit.core.fs.FileSystem;
-import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
-import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.uuid.UUID;
-
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -66,6 +40,32 @@
 
 import javax.jcr.RepositoryException;
 
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
+import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding;
+import org.apache.jackrabbit.core.persistence.bundle.util.ConnectionRecoveryManager;
+import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex;
+import org.apache.jackrabbit.core.persistence.bundle.util.ErrorHandling;
+import org.apache.jackrabbit.core.persistence.bundle.util.NodePropBundle;
+import org.apache.jackrabbit.core.persistence.bundle.util.StringIndex;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.uuid.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * This is a generic persistence manager that stores the {@link NodePropBundle}s
  * in a database.
@@ -562,7 +562,8 @@
         initialized = true;
 
         if (consistencyCheck) {
-            checkConsistency();
+            // check all bundles
+            checkConsistency(null, true, consistencyFix);
         }
     }
 
@@ -651,105 +652,209 @@
             throws Exception {
         return new DbBlobStore();
     }
+    
+    /**
+     * Checks a single bundle for inconsistencies, ie. inexistent child nodes
+     * and inexistent parents.
+     * 
+     * @param id
+     *            node id for the bundle to check
+     * @param bundle
+     *            the bundle to check
+     * @param fix
+     *            if true, repair things that can be repaired
+     * @param modifications
+     *            if <code>fix == true</code>, collect the repaired
+     *            {@linkplain NodePropBundle bundles} here
+     */
+    protected void checkBundleConsistency(NodeId id, NodePropBundle bundle, boolean fix, Collection modifications) {
+        //log.info(name + ": checking bundle '" + id + "'");
+        
+        // look at the node's children
+        Collection missingChildren = new ArrayList();
+        Iterator iter = bundle.getChildNodeEntries().iterator();
+        while (iter.hasNext()) {
+            NodePropBundle.ChildNodeEntry entry = (NodePropBundle.ChildNodeEntry) iter.next();
+            
+            // skip check for system nodes (root, system root, version storage, nodetypes)
+            if (entry.getId().toString().endsWith("babecafebabe")) {
+                continue;
+            }
+            if (id.toString().endsWith("babecafebabe")) {
+                continue;
+            }
+            
+            try {
+                // analyze child node bundles
+                NodePropBundle child = loadBundle(entry.getId(), true);
+                if (child == null) {
+                    log.error("NodeState '" + id + "' references inexistent child '" + entry.getName() + "' with id '" + entry.getId() + "'");
+                    missingChildren.add(entry);
+                } else {
+                    NodeId cp = child.getParentId();
+                    if (cp == null) {
+                        log.error("ChildNode has invalid parent uuid: <null>");
+                    } else if (!cp.equals(id)) {
+                        log.error("ChildNode has invalid parent uuid: '" + cp + "' (instead of '" + id + "')");
+                    }
+                }
+            } catch (ItemStateException e) {
+                // problem already logged (loadBundle called with logDetailedErrors=true)
+            }
+        }
+        // remove child node entry (if fixing is enabled)
+        if (fix && !missingChildren.isEmpty()) {
+            Iterator iterator = missingChildren.iterator();
+            while (iterator.hasNext()) {
+                bundle.getChildNodeEntries().remove(iterator.next());
+            }
+            modifications.add(bundle);
+        }
+
+        // check parent reference
+        NodeId parentId = bundle.getParentId();
+        try {
+            // skip root nodes (that point to itself)
+            if (parentId != null && !id.toString().endsWith("babecafebabe")) {
+                if (!existsBundle(parentId)) {
+                    log.error("NodeState '" + id + "' references inexistent parent uuid '" + parentId + "'");
+                }
+            }
+        } catch (ItemStateException e) {
+            log.error("Error reading node '" + parentId + "' (parent of '" + id + "'): " + e);
+        }
+    }
 
     /**
-     * Performs a consistency check.
+     * {@inheritDoc}
      */
-    private void checkConsistency() {
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+        
+        log.info("{}: checking workspace consistency...", name);
+        
         int count = 0;
         int total = 0;
-        log.info("{}: checking workspace consistency...", name);
-
         Collection modifications = new ArrayList();
-        ResultSet rs = null;
-        DataInputStream din = null;
-        try {
-            String sql;
-            if (getStorageModel() == SM_BINARY_KEYS) {
-                sql = "select NODE_ID, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
-            } else {
-                sql = "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
-            }
-            Statement stmt = connectionManager.executeStmt(sql, new Object[0]);
-            rs = stmt.getResultSet();
-            while (rs.next()) {
-                NodeId id;
-                Blob blob;
+        
+        if (uuids == null) {
+            // 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;
+            DataInputStream din = null;
+            try {
+                String sql;
                 if (getStorageModel() == SM_BINARY_KEYS) {
-                    id = new NodeId(new UUID(rs.getBytes(1)));
-                    blob = rs.getBlob(2);
+                    sql = "select NODE_ID, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
                 } else {
-                    id = new NodeId(new UUID(rs.getLong(1), rs.getLong(2)));
-                    blob = rs.getBlob(3);
+                    sql = "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
                 }
-                din = new DataInputStream(blob.getBinaryStream());
-                try {
-                    NodePropBundle bundle = binding.readBundle(din, id);
-                    Collection missingChildren = new ArrayList();
-                    Iterator iter = bundle.getChildNodeEntries().iterator();
-                    while (iter.hasNext()) {
-                        NodePropBundle.ChildNodeEntry entry = (NodePropBundle.ChildNodeEntry) iter.next();
-                        if (entry.getId().toString().endsWith("babecafebabe")) {
-                            continue;
-                        }
-                        if (id.toString().endsWith("babecafebabe")) {
-                            continue;
-                        }
-                        try {
-                            NodePropBundle child = loadBundle(entry.getId());
-                            if (child == null) {
-                                log.error("NodeState " + id.getUUID() + " references unexistent child " + entry.getName() + " with id " + entry.getId().getUUID());
-                                missingChildren.add(entry);
-                            } else {
-                                NodeId cp = child.getParentId();
-                                if (cp == null) {
-                                    log.error("ChildNode has invalid parent uuid: null");
-                                } else if (!cp.equals(id)) {
-                                    log.error("ChildNode has invalid parent uuid: " + cp + " (instead of " + id.getUUID() + ")");
-                                }
-                            }
-                        } catch (ItemStateException e) {
-                            log.error("Error while loading child node: " + e);
-                        }
+                Statement stmt = connectionManager.executeStmt(sql, new Object[0]);
+                rs = stmt.getResultSet();
+                
+                // iterate over all nodebundles in the db
+                while (rs.next()) {
+                    NodeId id;
+                    Blob blob;
+                    if (getStorageModel() == SM_BINARY_KEYS) {
+                        id = new NodeId(new UUID(rs.getBytes(1)));
+                        blob = rs.getBlob(2);
+                    } else {
+                        id = new NodeId(new UUID(rs.getLong(1), rs.getLong(2)));
+                        blob = rs.getBlob(3);
                     }
-                    if (consistencyFix && !missingChildren.isEmpty()) {
-                        Iterator iterator = missingChildren.iterator();
-                        while (iterator.hasNext()) {
-                            bundle.getChildNodeEntries().remove(iterator.next());
+                    din = new DataInputStream(blob.getBinaryStream());
+                    try {
+                        // parse and check bundle
+                        
+                        // check bundle will log any problems itself
+                        if (binding.checkBundle(din)) {
+                            // reset stream for readBundle()
+                            din = new DataInputStream(blob.getBinaryStream());
+                            
+                            NodePropBundle bundle = binding.readBundle(din, id);
+                            
+                            checkBundleConsistency(id, bundle, fix, modifications);
+                        } else {
+                            log.error("invalid bundle '" + id + "', see previous BundleBinding error log entry");
                         }
-                        modifications.add(bundle);
+                        
+                    } catch (Exception e) {
+                        log.error("Error in bundle " + id + ": " + e);
                     }
-
-                    NodeId parentId = bundle.getParentId();
-                    if (parentId != null) {
-                        if (!exists(parentId)) {
-                            log.error("NodeState " + id + " references unexistent parent id " + parentId);
-                        }
+                    count++;
+                    if (count % 1000 == 0) {
+                        log.info(name + ": checked " + count + " bundles...");
                     }
-                } catch (IOException e) {
-                    log.error("Error in bundle " + id + ": " + e);
-                    din = new DataInputStream(blob.getBinaryStream());
-                    binding.checkBundle(din);
                 }
-                count++;
-                if (count % 1000 == 0) {
-                    log.info(name + ": checked " + count + "/" + total + " bundles...");
+            } catch (Exception e) {
+                log.error("Error loading bundle", e);
+            } finally {
+                closeStream(din);
+                closeResultSet(rs);
+                total = count;
+            }
+        } else {
+            // check only given uuids, handle recursive flag
+    
+            // 1) convert uuid array to modifiable list
+            // 2) for each uuid do
+            //     a) load node bundle
+            //     b) check bundle, store any bundle-to-be-modified in collection
+            //     c) if recursive, add child uuids to list of uuids
+            
+            List uuidList = new ArrayList(recursive ? uuids.length*3 : uuids.length);
+            // convert uuid string array to list of UUID objects
+            for (int i=0; i < uuids.length; i++) {
+                try {
+                    uuidList.add(new UUID(uuids[i]));
+                } catch (IllegalArgumentException e) {
+                    log.error("Invalid uuid for consistency check, skipping: '" + uuids[i] + "': " + e);
                 }
             }
-        } catch (Exception e) {
-            log.error("Error in bundle", e);
-        } finally {
-            closeStream(din);
-            closeResultSet(rs);
+            
+            // iterate over UUIDs (including ones that are newly added inside the loop!)
+            for (int i=0; i < uuidList.size(); i++) {
+                final UUID uuid = (UUID) uuidList.get(i);
+                try {
+                    // load the node from the database
+                    NodeId id = new NodeId(uuid);
+                    NodePropBundle bundle = loadBundle(id, true);
+                    
+                    if (bundle == null) {
+                        log.error("No bundle found for uuid '" + uuid + "'");
+                        continue;
+                    }
+                    
+                    checkBundleConsistency(id, bundle, fix, modifications);
+                    
+                    if (recursive) {
+                        Iterator iter = bundle.getChildNodeEntries().iterator();
+                        while (iter.hasNext()) {
+                            NodePropBundle.ChildNodeEntry entry = (NodePropBundle.ChildNodeEntry) iter.next();
+                            uuidList.add(entry.getId().getUUID());
+                        }
+                    }
+                    
+                    count++;
+                    if (count % 1000 == 0) {
+                        log.info(name + ": checked " + count + "/" + uuidList.size() + " bundles...");
+                    }
+                } catch (ItemStateException e) {
+                    // problem already logged (loadBundle called with logDetailedErrors=true)
+                }
+            }
+            
+            total = uuidList.size();
         }
-
-        if (consistencyFix && !modifications.isEmpty()) {
+        
+        // repair collected broken bundles
+        if (fix && !modifications.isEmpty()) {
             log.info(name + ": Fixing " + modifications.size() + " inconsistent bundle(s)...");
             Iterator iterator = modifications.iterator();
             while (iterator.hasNext()) {
                 NodePropBundle bundle = (NodePropBundle) iterator.next();
                 try {
-                    log.info(name + ": Fixing bundle " + bundle.getId());
+                    log.info(name + ": Fixing bundle '" + bundle.getId() + "'");
                     bundle.markOld(); // use UPDATE instead of INSERT
                     storeBundle(bundle);
                 } catch (ItemStateException e) {
@@ -760,8 +865,7 @@
 
         log.info(name + ": checked " + count + "/" + total + " bundles.");
     }
-
-
+    
     /**
      * Makes sure that <code>schemaObjectPrefix</code> does only consist of
      * characters that are allowed in names on the target database. Illegal
@@ -887,8 +991,25 @@
      */
     protected synchronized NodePropBundle loadBundle(NodeId id)
             throws ItemStateException {
+        return loadBundle(id, false);
+    }
+    
+    /**
+     * Loads a bundle from the underlying system and optionally performs
+     * a check on the bundle first.
+     *
+     * @param id the node id of the bundle
+     * @param checkBeforeLoading check the bundle before loading it and log
+     *                           detailed informations about it (slower)
+     * @return the loaded bundle or <code>null</code> if the bundle does not
+     *         exist.
+     * @throws ItemStateException if an error while loading occurs.
+     */
+    protected synchronized NodePropBundle loadBundle(NodeId id, boolean checkBeforeLoading)
+            throws ItemStateException {
         ResultSet rs = null;
         InputStream in = null;
+        byte[] bytes = null;
         try {
             Statement stmt = connectionManager.executeStmt(bundleSelectSQL, getKey(id.getUUID()));
             rs = stmt.getResultSet();
@@ -898,13 +1019,24 @@
             Blob b = rs.getBlob(1);
             // JCR-1039: pre-fetch/buffer blob data
             long length = b.length();
-            byte[] bytes = new byte[(int) length];
+            bytes = new byte[(int) length];
             in = b.getBinaryStream();
             int read, pos = 0;
             while ((read = in.read(bytes, pos, bytes.length - pos)) > 0) {
                 pos += read;
             }
             DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes));
+            
+            if (checkBeforeLoading) {
+                if (binding.checkBundle(din)) {
+                    // reset stream for readBundle()
+                    din = new DataInputStream(new ByteArrayInputStream(bytes));
+                } else {
+                    // gets wrapped as proper ItemStateException below
+                    throw new Exception("invalid bundle, see previous BundleBinding error log entry");
+                }
+            }
+            
             NodePropBundle bundle = binding.readBundle(din, id);
             bundle.setSize(length);
             return bundle;

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java Mon Mar  3 01:17:36 2008
@@ -196,6 +196,14 @@
     public DynamicESCFactory getEscFactory() {
         return escFactory;
     }
+    
+    /**
+     * Returns the persistence manager used by the version manager.
+     * @return the persistence manager used by the version manager
+     */
+    public PersistenceManager getPersistenceManager() {
+        return pMgr;
+    }
 
     /**
      * {@inheritDoc}

Modified: jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java?rev=632996&r1=632995&r2=632996&view=diff
==============================================================================
--- jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java (original)
+++ jackrabbit/branches/1.3/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/TestAll.java Mon Mar  3 01:17:36 2008
@@ -36,6 +36,7 @@
     public static Test suite() {
         TestSuite suite = new TestSuite("Persistence tests");
 
+        suite.addTestSuite(ConsistencyCheckTest.class);
         suite.addTestSuite(DatabaseConnectionFailureTest.class);
 
         return suite;