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/04/22 03:54:25 UTC

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

Author: jukka
Date: Mon Apr 21 18:54:22 2008
New Revision: 650354

URL: http://svn.apache.org/viewvc?rev=650354&view=rev
Log:
1.4: Merged revisions 632993 and 637847 (JCR-1428)

Modified:
    jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
    jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
    jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
    jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java

Modified: jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java?rev=650354&r1=650353&r2=650354&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java Mon Apr 21 18:54:22 2008
@@ -98,6 +98,14 @@
     }
 
     /**
+     * This implementation does nothing.
+     *
+     * {@inheritDoc}
+     */
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+    }
+
+    /**
      * Store a node state. Subclass responsibility.
      *
      * @param state node state to store

Modified: jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java?rev=650354&r1=650353&r2=650354&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java Mon Apr 21 18:54:22 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.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java?rev=650354&r1=650353&r2=650354&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java Mon Apr 21 18:54:22 2008
@@ -712,4 +712,12 @@
         }
     }
 
+    /**
+     * This implementation does nothing.
+     *
+     * {@inheritDoc}
+     */
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
+    }
+
 }

Modified: jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java?rev=650354&r1=650353&r2=650354&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java Mon Apr 21 18:54:22 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.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.NodeIdIterator;
-import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.uuid.UUID;
-
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -67,6 +41,34 @@
 
 import javax.jcr.RepositoryException;
 
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.NodeIdIterator;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.RepositoryImpl;
+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.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.bundle.util.NodePropBundle.ChildNodeEntry;
+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.
@@ -576,7 +578,8 @@
         initialized = true;
 
         if (consistencyCheck) {
-            checkConsistency();
+            // check all bundles
+            checkConsistency(null, true, consistencyFix);
         }
     }
 
@@ -677,103 +680,271 @@
     }
 
     /**
-     * Performs a consistency check.
+     * Helper method that returns the jcr path for a node bundle.
+     * 
+     * <p>
+     * Resolves the special root nodes (eg. <code>/jcr:system</code>). If a
+     * node cannot be found in the list of child nodes of its parent (ie. has no
+     * name), <code>{{missing}}</code> will be used instead.
+     * 
+     * @param id
+     *            NodeId for which the path should be displayed
+     * @param bundle
+     *            bundle for id, optional (if <code>null</code>, will be
+     *            loaded via given id)
+     * @return the path of the node identified by id, with namespaces not
+     *         resolved (eg. <code>{http://www.jcp.org/jcr/1.0}</code> instead
+     *         of <code>jcr:</code>)
+     * @throws ItemStateException
+     *             if loading one of the parent nodes failed
+     */
+    protected String getBundlePath(NodeId id, NodePropBundle bundle) throws ItemStateException {
+        // load bundle on demand
+        if (bundle == null) {
+            bundle = loadBundle(id);
+            if (bundle == null) {
+                return "{{missing}}";
+            }
+        }
+        
+        // check for the various special root nodes
+        if (id.equals(RepositoryImpl.VERSION_STORAGE_NODE_ID)) {
+            return "/jcr:system/jcr:versionStorage";
+        } else if (id.equals(RepositoryImpl.NODETYPES_NODE_ID)) {
+            return "/jcr:system/jcr:nodeTypes";
+        } else if (id.equals(RepositoryImpl.SYSTEM_ROOT_NODE_ID)) {
+            return "/jcr:system";
+        } else if (id.equals(RepositoryImpl.ROOT_NODE_ID)) {
+            return "";
+        } else if (bundle.getParentId() == null) {
+            return "";
+        } else if (bundle.getParentId().equals(id)) {
+            return "";
+        }
+        
+        // get the name of this bundle by looking at the parent
+        NodePropBundle parentBundle = loadBundle(bundle.getParentId());
+        if (parentBundle == null) {
+            return "{{missing}}";
+        }
+        
+        String name = "{{missing}}";
+        Iterator iter = parentBundle.getChildNodeEntries().iterator();
+        while (iter.hasNext()) {
+            ChildNodeEntry entry = (ChildNodeEntry) iter.next();
+            if (entry.getId().equals(id)) {
+                String uri = entry.getName().getNamespaceURI();
+                // hide the empty {}ĂŠnamespace if none is present
+                if (uri == null || uri.equals("")) {
+                    name = entry.getName().getLocalName();
+                } else {
+                    // pattern: {uri}localName
+                    name = entry.getName().toString();
+                }
+                break;
+            }
+        }
+        
+        // recursive call (building the path from right-to-left)
+        return getBundlePath(parentBundle.getId(), parentBundle) + "/" + name;
+    }
+
+    /**
+     * 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 '" + getBundlePath(id, bundle) + "' ('" + id + "') references inexistent child '" + entry.getName() + "' with id '" + entry.getId() + "'");
+                    missingChildren.add(entry);
+                } else {
+                    NodeId cp = child.getParentId();
+                    if (cp == null) {
+                        log.error("ChildNode '" + entry.getName() + "' has invalid parent uuid: <null>");
+                    } else if (!cp.equals(id)) {
+                        log.error("ChildNode '" + entry.getName() + "' 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);
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
      */
-    private void checkConsistency() {
-        int count = 0;
-        int total = 0;
+    public void checkConsistency(String[] uuids, boolean recursive, boolean fix) {
         log.info("{}: checking workspace consistency...", name);
 
+        int count = 0;
+        int total = 0;
         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 inexistent 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);
                     }
+                    count++;
+                    if (count % 1000 == 0) {
+                        log.info(name + ": checked " + count + " 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(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);
+                }
+            }
 
-                    NodeId parentId = bundle.getParentId();
-                    if (parentId != null) {
-                        if (!exists(parentId)) {
-                            log.error("NodeState " + id + " references inexistent parent id " + parentId);
+            // 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());
                         }
                     }
-                } 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...");
+
+                    count++;
+                    if (count % 1000 == 0) {
+                        log.info(name + ": checked " + count + "/" + uuidList.size() + " bundles...");
+                    }
+                } catch (ItemStateException e) {
+                    // problem already logged (loadBundle called with logDetailedErrors=true)
                 }
             }
-        } catch (Exception e) {
-            log.error("Error in bundle", e);
-        } finally {
-            closeStream(din);
-            closeResultSet(rs);
+            total = uuidList.size();
         }
 
+        // 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) {
@@ -785,7 +956,6 @@
         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
@@ -968,8 +1138,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();
@@ -979,13 +1166,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;