jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From un...@apache.org
Subject svn commit: r1351240 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/persistence/ main/java/org/apache/jackrabbit/core/persistence/bundle/ main/java/org/apache/jackrabbit/core/persistence/mem/ main/java/org/apache/jackr...
Date Mon, 18 Jun 2012 08:46:38 GMT
Author: unico
Date: Mon Jun 18 08:46:37 2012
New Revision: 1351240

URL: http://svn.apache.org/viewvc?rev=1351240&view=rev
Log:
JCR-3263 load in structural node information immediately for better performance and add option
to load the complete node graph first before checking for consistency. performance is boosted
dozens of times on large repositories when enabling this option but requires more memory

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodeInfo.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/IterablePersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemBundlePersistenceManager.java
    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/data/ConsistencyCheckerImplTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/IterablePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/IterablePersistenceManager.java?rev=1351240&r1=1351239&r2=1351240&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/IterablePersistenceManager.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/IterablePersistenceManager.java
Mon Jun 18 08:46:37 2012
@@ -17,33 +17,53 @@
 package org.apache.jackrabbit.core.persistence;
 
 import java.util.List;
+import java.util.Map;
 
 import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.persistence.util.NodeInfo;
 import org.apache.jackrabbit.core.state.ItemStateException;
 
 import javax.jcr.RepositoryException;
 
 /**
- * The iterable persistence manager can return the list of node ids that are stored.
+ * The iterable persistence manager can return the list of {@link NodeId}s and
+ * {@link NodeInfo}s that are stored.
  * Possible applications are backup, migration (copying a workspace or repository),
- * and data store garbage collection.
+ * data store garbage collection, and consistency checking.
  */
 public interface IterablePersistenceManager extends PersistenceManager {
 
     /**
      * Get all node ids.
      * A typical application will call this method multiple times, where 'after'
-     * is the last row read. The maxCount parameter defines the maximum number of
+     * is the last row read previously. The maxCount parameter defines the maximum number
of
      * node ids returned, 0 meaning no limit. The order of the node ids is specific for the
      * given persistent manager. Items that are added concurrently may not be included.
      *
      * @param after the lower limit, or null for no limit.
      * @param maxCount the maximum number of node ids to return, or 0 for no limit.
-     * @return a list of all bundles.
+     * @return a list of all node ids.
      * @throws ItemStateException if an error while loading occurs.
-     * @throws RepositoryException if a repository exception occurs
+     * @throws RepositoryException if a repository exception occurs.
      */
     List<NodeId> getAllNodeIds(NodeId after, int maxCount)
             throws ItemStateException, RepositoryException;
 
+
+    /**
+     * Get all {@link NodeInfo}s.
+     * A typical application will call this method multiple time, where 'after'
+     * is the last row read previously. The maxCount parameter defines the maximum number
of
+     * node ids returned, 0 meaning no limit. The order of the node ids is specific for the
+     * given persistence manager. Items that are added concurrently may not be included.
+     *
+     * @param after the lower limit, or null for no limit.
+     * @param maxCount the maximum number of node infos to return, or 0 for no limit.
+     * @return a list of all node infos.
+     * @throws ItemStateException if an error while loading occurs.
+     * @throws RepositoryException if a repository exception occurs.
+     */
+    Map<NodeId, NodeInfo> getAllNodeInfos(NodeId after, int maxCount)
+            throws ItemStateException, RepositoryException;
+
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java?rev=1351240&r1=1351239&r2=1351240&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
Mon Jun 18 08:46:37 2012
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.core.persistence.bundle;
 
+import org.apache.jackrabbit.core.persistence.util.NodeInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.commons.io.IOUtils;
@@ -42,7 +43,11 @@ import java.io.OutputStream;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
 
 /**
  * This is a generic persistence manager that stores the {@link NodePropBundle}s
@@ -499,6 +504,18 @@ public class BundleFsPersistenceManager 
     /**
      * {@inheritDoc}
      */
+    public Map<NodeId, NodeInfo> getAllNodeInfos(NodeId after, int maxCount) throws
ItemStateException, RepositoryException {
+        List<NodeId> nodeIds = getAllNodeIds(after, maxCount);
+        Map<NodeId, NodeInfo> result = new LinkedHashMap<NodeId, NodeInfo>(nodeIds.size());
+        for (NodeId nodeId : nodeIds) {
+            result.put(nodeId, new NodeInfo(loadBundle(nodeId)));
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     protected NodeId getIdFromFileName(String fileName) {
         StringBuffer buff = new StringBuffer(35);
         if (!fileName.endsWith("." + NODEFILENAME)) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java?rev=1351240&r1=1351239&r2=1351240&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/ConsistencyCheckerImpl.java
Mon Jun 18 08:46:37 2012
@@ -18,8 +18,10 @@ package org.apache.jackrabbit.core.persi
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 
@@ -31,6 +33,7 @@ import org.apache.jackrabbit.core.persis
 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.NodeInfo;
 import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -42,8 +45,7 @@ import org.slf4j.LoggerFactory;
 public class ConsistencyCheckerImpl {
 
     /** the default logger */
-    private static Logger log = LoggerFactory
-            .getLogger(ConsistencyCheckerImpl.class);
+    private static Logger log = LoggerFactory.getLogger(ConsistencyCheckerImpl.class);
 
     private final AbstractBundlePersistenceManager pm;
 
@@ -51,8 +53,16 @@ public class ConsistencyCheckerImpl {
 
     private static final NameFactory NF = NameFactoryImpl.getInstance();
 
-    // process 64K nodes at once
-    private static int NODESATONCE = 1024 * 64;
+    /**
+     * The number of nodes to fetch at once from the persistence manager. Defaults to 8kb
+     */
+    private static final int NODESATONCE = Integer.getInteger("org.apache.jackrabbit.checker.nodesatonce",
1024 * 8);
+
+    /**
+     * Whether to load all node infos before checking or to check nodes as they are loaded.
+     * The former is magnitudes faster but requires more memory.
+     */
+    private static final boolean CHECKAFTERLOADING = Boolean.getBoolean("org.apache.jackrabbit.checker.checkafterloading");
 
     public ConsistencyCheckerImpl(AbstractBundlePersistenceManager pm,
             ConsistencyCheckListener listener) {
@@ -65,8 +75,7 @@ public class ConsistencyCheckerImpl {
         Set<ReportItem> reports = new HashSet<ReportItem>();
 
         long tstart = System.currentTimeMillis();
-        int total = internalCheckConsistency(uuids, recursive, fix, reports,
-                lostNFoundId);
+        int total = internalCheckConsistency(uuids, recursive, fix, reports, lostNFoundId);
         long elapsed = System.currentTimeMillis() - tstart;
 
         return new ConsistencyReportImpl(total, elapsed, reports);
@@ -78,58 +87,62 @@ public class ConsistencyCheckerImpl {
         int count = 0;
 
         NodeId lostNFound = null;
-        if (fix && lostNFoundId != null) {
-            // do we have a "lost+found" node?
-            try {
-                NodeId tmpid = new NodeId(lostNFoundId);
-                NodePropBundle lfBundle = pm.loadBundle(tmpid);
-                if (lfBundle == null) {
-                    error(lostNFoundId,
-                            "specified 'lost+found' node does not exist");
-                } else if (!NameConstants.NT_UNSTRUCTURED.equals(lfBundle
-                        .getNodeTypeName())) {
-                    error(lostNFoundId,
-                            "specified 'lost+found' node is not of type nt:unstructured");
-                } else {
-                    lostNFound = lfBundle.getId();
+        if (fix) {
+            if (lostNFoundId != null) {
+                // do we have a "lost+found" node?
+                try {
+                    NodeId tmpid = new NodeId(lostNFoundId);
+                    NodePropBundle lfBundle = pm.loadBundle(tmpid);
+                    if (lfBundle == null) {
+                        error(lostNFoundId, "Specified 'lost+found' node does not exist");
+                    } else if (!NameConstants.NT_UNSTRUCTURED.equals(lfBundle .getNodeTypeName()))
{
+                        error(lostNFoundId, "Specified 'lost+found' node is not of type nt:unstructured");
+                    } else {
+                        lostNFound = lfBundle.getId();
+                    }
+                } catch (Exception ex) {
+                    error(lostNFoundId, "finding 'lost+found' folder", ex);
                 }
-            } catch (Exception ex) {
-                error(lostNFoundId, "finding 'lost+found' folder", ex);
+            } else {
+                log.info("No 'lost+found' node specified: orphans cannot be fixed");
             }
         }
 
         if (uuids == null) {
             try {
-                List<NodeId> allIds = pm.getAllNodeIds(null, NODESATONCE);
+                Map<NodeId, NodeInfo> batch = pm.getAllNodeInfos(null, NODESATONCE);
+                Map<NodeId, NodeInfo> allInfos = batch;
 
-                while (!allIds.isEmpty()) {
+                while (!batch.isEmpty()) {
                     NodeId lastId = null;
 
-                    for (NodeId id : allIds) {
+                    for (Map.Entry<NodeId, NodeInfo> entry : batch.entrySet()) {
+                        NodeId id = entry.getKey();
                         lastId = id;
-                        try {
-                            // parse and check bundle
-                            NodePropBundle bundle = pm.loadBundle(id);
-                            if (bundle == null) {
-                                error(id.toString(), "No bundle found for id '"
-                                        + id + "'");
-                            } else {
-                                checkBundleConsistency(id, bundle, fix, lostNFound, reports);
 
-                                count++;
-                                if (count % 1000 == 0 && listener == null) {
-                                    log.info(pm + ": checked " + count
-                                            + " bundles...");
-                                }
-                            }
-                        } catch (ItemStateException e) {
-                            // problem already logged (loadBundle called with
-                            // logDetailedErrors=true)
+                        count++;
+                        if (count % 1000 == 0) {
+                            log.info(pm + ": loaded " + count + " infos...");
+                        }
+
+                        if (!CHECKAFTERLOADING) {
+                            // check immediately
+                            NodeInfo nodeInfo = entry.getValue();
+                            checkBundleConsistency(id, nodeInfo, fix, lostNFound, reports,
batch);
                         }
                     }
 
-                    if (!allIds.isEmpty()) {
-                        allIds = pm.getAllNodeIds(lastId, NODESATONCE);
+                    batch = pm.getAllNodeInfos(lastId, NODESATONCE);
+
+                    if (CHECKAFTERLOADING) {
+                        allInfos.putAll(batch);
+                    }
+                }
+
+                if (CHECKAFTERLOADING) {
+                    // check info
+                    for (Map.Entry<NodeId, NodeInfo> entry : allInfos.entrySet()) {
+                        checkBundleConsistency(entry.getKey(), entry.getValue(), fix, lostNFound,
reports, allInfos);
                     }
                 }
             } catch (ItemStateException ex) {
@@ -170,7 +183,8 @@ public class ConsistencyCheckerImpl {
                                     + id + "'");
                         }
                     } else {
-                        checkBundleConsistency(id, bundle, fix, lostNFound, reports);
+                        checkBundleConsistency(id, new NodeInfo(bundle), fix, lostNFound,
+                                reports, Collections.<NodeId, NodeInfo>emptyMap());
 
                         if (recursive) {
                             for (NodePropBundle.ChildNodeEntry entry : bundle
@@ -194,21 +208,25 @@ public class ConsistencyCheckerImpl {
 
         log.info(pm + ": checked " + count + " bundles.");
 
+        // clear the NodeId pool
+        NodeInfo.clearPool();
+
         return count;
     }
 
     /**
-     * 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 <code>true</code>, repair things that can be repaired
+     * Checks a single bundle for inconsistencies, ie. inexistent child nodes, inexistent
parents, and other
+     * structural inconsistencies.
+     *
+     * @param id node id for the bundle to check
+     * @param nodeInfo the node info for the node to check
+     * @param fix if <code>true</code>, repair things that can be repaired
+     * {@linkplain org.apache.jackrabbit.core.persistence.util.NodePropBundle bundles} here
+     * @param infos all the {@link NodeInfo}s loaded in the current batch
      */
-    private void checkBundleConsistency(NodeId id, NodePropBundle bundle, boolean fix, NodeId
lostNFoundId, Set<ReportItem> reports) {
+    private void checkBundleConsistency(NodeId id, NodeInfo nodeInfo,
+                                        boolean fix, NodeId lostNFoundId,
+                                        Set<ReportItem> reports, Map<NodeId, NodeInfo>
infos) {
         // log.info(name + ": checking bundle '" + id + "'");
 
         // skip all virtual nodes
@@ -223,9 +241,10 @@ public class ConsistencyCheckerImpl {
         // look at the node's children
         Collection<NodePropBundle.ChildNodeEntry> missingChildren = new ArrayList<NodePropBundle.ChildNodeEntry>();
         Collection<NodePropBundle.ChildNodeEntry> disconnectedChildren = new ArrayList<NodePropBundle.ChildNodeEntry>();
-        for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) {
 
-            final NodeId childNodeId = entry.getId();
+        NodePropBundle bundle = null;
+
+        for (final NodeId childNodeId : nodeInfo.getChildren()) {
 
             // skip check for system nodes (root, system root, version storage,
             // node types)
@@ -235,53 +254,74 @@ public class ConsistencyCheckerImpl {
 
             try {
                 // analyze child node bundles
-                final NodePropBundle childBundle = pm.loadBundle(childNodeId);
+                NodePropBundle childBundle = null;
+                NodeInfo childNodeInfo = infos.get(childNodeId);
+
                 String message = null;
-                if (childBundle == null) {
-                    // double check whether we still exist and the child entry is still there
-                    bundle = pm.loadBundle(id);
-
-                    if (bundle != null) {
-                        boolean stillThere = false;
-                        for (NodePropBundle.ChildNodeEntry entryRetry : bundle.getChildNodeEntries())
{
-                            if (entryRetry.getId().equals(childNodeId)) {
-                                stillThere = true;
-                                break;
-                            }
-                        }
-                        if (stillThere) {
-                            message = "NodeState '" + id
-                                    + "' references inexistent child" + " '"
-                                    + entry.getName() + "' with id " + "'"
-                                    + childNodeId + "'";
-                            log.error(message);
-                            missingChildren.add(entry);
+                // does the child exist?
+                if (childNodeInfo == null) {
+                    // try to load the bundle
+                    childBundle = pm.loadBundle(childNodeId);
+                    if (childBundle == null) {
+                        // the child indeed does not exist
+                        // double check whether we still exist and the child entry is still
there
+                        if (bundle == null) {
+                            bundle = pm.loadBundle(id);
                         }
-                    } else {
-                        return;
-                    }
-                } else {
-                    NodeId cp = childBundle.getParentId();
-                    if (!id.equals(cp)) {
-                        // double check whether the child entry is still there
-                        bundle = pm.loadBundle(id);
                         if (bundle != null) {
-                            boolean stillThere = false;
-                            for (NodePropBundle.ChildNodeEntry entryRetry : bundle.getChildNodeEntries())
{
-                                if (entryRetry.getId().equals(childNodeId)) {
-                                    stillThere = true;
+                            NodePropBundle.ChildNodeEntry childNodeEntry = null;
+                            for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries())
{
+                                if (entry.getId().equals(childNodeId)) {
+                                    childNodeEntry = entry;
                                     break;
                                 }
                             }
-                            if (stillThere) {
-                                // indeed we have a disconnected child
-                                message = "ChildNode has invalid parent id: '" + cp + "'
(instead of '" + id + "')";
+                            if (childNodeEntry != null) {
+                                message = "NodeState '" + id + "' references inexistent child
'" + childNodeId + "'";
                                 log.error(message);
-                                disconnectedChildren.add(entry);
+                                missingChildren.add(childNodeEntry);
                             }
                         } else {
                             return;
                         }
+                    } else {
+                        // exists after all
+                        childNodeInfo = new NodeInfo(childBundle);
+                    }
+                }
+                if (childNodeInfo != null) {
+                    // if the child exists does it reference the current node as its parent?
+                    NodeId cp = childNodeInfo.getParentId();
+                    if (!id.equals(cp)) {
+                        // double check whether the child still has a different parent
+                        if (childBundle == null) {
+                            childBundle = pm.loadBundle(childNodeId);
+                        }
+                        if (childBundle != null && !childBundle.getParentId().equals(id))
{
+                            // double check if we still exist
+                            if (bundle == null) {
+                                bundle = pm.loadBundle(id);
+                            }
+                            if (bundle != null) {
+                                // double check if the child node entry is still there
+                                NodePropBundle.ChildNodeEntry childNodeEntry = null;
+                                for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries())
{
+                                    if (entry.getId().equals(childNodeId)) {
+                                        childNodeEntry = entry;
+                                        break;
+                                    }
+                                }
+                                if (childNodeEntry != null) {
+                                    // indeed we have a disconnected child
+                                    message = "ChildNode has invalid parent id: '" + cp +
"' (instead of '" + id + "')";
+                                    log.error(message);
+                                    disconnectedChildren.add(childNodeEntry);
+                                }
+                            } else {
+                                return;
+                            }
+
+                        }
                     }
                 }
                 if (message != null) {
@@ -305,54 +345,82 @@ public class ConsistencyCheckerImpl {
         }
 
         // check parent reference
-        NodeId parentId = bundle.getParentId();
+        NodeId parentId = nodeInfo.getParentId();
         try {
             // skip root nodes (that point to itself)
             if (parentId != null && !id.toString().endsWith("babecafebabe")) {
-                NodePropBundle parentBundle = pm.loadBundle(parentId);
+                NodePropBundle parentBundle = null;
+                NodeInfo parentInfo = infos.get(parentId);
 
-                if (parentBundle == null) {
-                    // double check whether we still exist and the parent is still the same
-                    bundle = pm.loadBundle(id);
-                    if (bundle != null) {
-                        if (parentId.equals(bundle.getParentId())) {
-                            String message = "NodeState '" + id
-                                    + "' references inexistent parent id '" + parentId
-                                    + "'";
-                            log.error(message);
-                            addMessage(reports, id, message);
-                            if (fix && lostNFoundId != null) {
-                                // add a child to lost+found
-                                NodePropBundle lfBundle = pm.loadBundle(lostNFoundId);
-                                lfBundle.markOld();
-                                String nodeName = id + "-" + System.currentTimeMillis();
-                                lfBundle.addChildNodeEntry(NF.create("", nodeName), id);
-                                pm.storeBundle(lfBundle);
-                                pm.evictBundle(lostNFoundId);
-
-                                // set lost+found parent
-                                bundle.setParentId(lostNFoundId);
-                                fixBundle(bundle);
+                // does the parent exist?
+                if (parentInfo == null) {
+                    // try to load the bundle
+                    parentBundle = pm.loadBundle(parentId);
+                    if (parentBundle == null) {
+                        // indeed the parent doesn't exist
+                        // double check whether we still exist and the parent is still the
same\
+                        if (bundle == null) {
+                            bundle = pm.loadBundle(id);
+                        }
+                        if (bundle != null) {
+                            if (parentId.equals(bundle.getParentId())) {
+                                // indeed we have an orphaned node
+                                String message = "NodeState '" + id + "' references inexistent
parent id '" + parentId + "'";
+                                log.error(message);
+                                addMessage(reports, id, message);
+                                if (fix && lostNFoundId != null) {
+                                    // add a child to lost+found
+                                    NodePropBundle lfBundle = pm.loadBundle(lostNFoundId);
+                                    lfBundle.markOld();
+                                    String nodeName = id + "-" + System.currentTimeMillis();
+                                    lfBundle.addChildNodeEntry(NF.create("", nodeName), id);
+                                    pm.storeBundle(lfBundle);
+                                    pm.evictBundle(lostNFoundId);
+
+                                    // set lost+found parent
+                                    bundle.setParentId(lostNFoundId);
+                                    fixBundle(bundle);
+                                }
                             }
+                        } else {
+                            return;
                         }
                     } else {
-                        return;
+                        // parent exists after all
+                        parentInfo = new NodeInfo(parentBundle);
                     }
-                } else {
+                }
+                if (parentInfo != null) {
+                    // if the parent exists, does it have a child node entry for us?
                     boolean found = false;
 
-                    for (NodePropBundle.ChildNodeEntry entry : parentBundle.getChildNodeEntries())
{
-                        if (entry.getId().equals(id)) {
+                    for (NodeId childNodeId : parentInfo.getChildren()) {
+                        if (childNodeId.equals(id)){
                             found = true;
                             break;
                         }
                     }
 
+                    if (!found && parentBundle == null) {
+                        // double check the parent
+                        parentBundle = pm.loadBundle(parentId);
+                        if (parentBundle != null) {
+                            for (NodePropBundle.ChildNodeEntry entry : parentBundle.getChildNodeEntries())
{
+                                if (entry.getId().equals(id)) {
+                                    found = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
                     if (!found) {
-                        // double check whether we still exist and the parent is still the
same
-                        bundle = pm.loadBundle(id);
+                        // double check whether we still exist and the parent id is still
the same
+                        if (bundle == null) {
+                            bundle = pm.loadBundle(id);
+                        }
                         if (bundle != null) {
                             if (parentId.equals(bundle.getParentId())) {
+                                // indeed we have an abandoned node
                                 String message = "NodeState '" + id
                                         + "' is not referenced by its parent node '"
                                         + parentId + "'";

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemBundlePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemBundlePersistenceManager.java?rev=1351240&r1=1351239&r2=1351240&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemBundlePersistenceManager.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemBundlePersistenceManager.java
Mon Jun 18 08:46:37 2012
@@ -27,6 +27,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -44,6 +45,7 @@ import org.apache.jackrabbit.core.persis
 import org.apache.jackrabbit.core.persistence.util.BundleBinding;
 import org.apache.jackrabbit.core.persistence.util.ErrorHandling;
 import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.NodeInfo;
 import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
 import org.apache.jackrabbit.core.persistence.util.Serializer;
 import org.apache.jackrabbit.core.state.ItemStateException;
@@ -501,6 +503,18 @@ public class InMemBundlePersistenceManag
     /**
      * {@inheritDoc}
      */
+    public Map<NodeId, NodeInfo> getAllNodeInfos(NodeId after, int maxCount) throws
ItemStateException, RepositoryException {
+        List<NodeId> nodeIds = getAllNodeIds(after, maxCount);
+        Map<NodeId, NodeInfo> result = new LinkedHashMap<NodeId, NodeInfo>(nodeIds.size());
+        for (NodeId nodeId : nodeIds) {
+            result.put(nodeId, new NodeInfo(loadBundle(nodeId)));
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     @Override
     protected NodePropBundle loadBundle(NodeId id) throws ItemStateException {
         if (!bundleStore.containsKey(id)) {

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=1351240&r1=1351239&r2=1351240&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
Mon Jun 18 08:46:37 2012
@@ -26,7 +26,9 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.jcr.RepositoryException;
 import javax.sql.DataSource;
@@ -43,6 +45,7 @@ import org.apache.jackrabbit.core.persis
 import org.apache.jackrabbit.core.persistence.util.BundleBinding;
 import org.apache.jackrabbit.core.persistence.util.ErrorHandling;
 import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.NodeInfo;
 import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
 import org.apache.jackrabbit.core.persistence.util.Serializer;
 import org.apache.jackrabbit.core.state.ChangeLog;
@@ -141,6 +144,8 @@ public class BundleDbPersistenceManager
     protected String bundleDeleteSQL;
     protected String bundleSelectAllIdsFromSQL;
     protected String bundleSelectAllIdsSQL;
+    protected String bundleSelectAllBundlesFromSQL;
+    protected String bundleSelectAllBundlesSQL;
 
     // SQL statements for NodeReference management
     protected String nodeReferenceInsertSQL;
@@ -821,6 +826,58 @@ public class BundleDbPersistenceManager
     /**
      * {@inheritDoc}
      */
+    public synchronized Map<NodeId, NodeInfo> getAllNodeInfos(NodeId bigger, int maxCount)
throws ItemStateException {
+        ResultSet rs = null;
+        try {
+            String sql = bundleSelectAllBundlesSQL;
+            NodeId lowId = null;
+            Object[] keys = new Object[0];
+            if (bigger != null) {
+                sql = bundleSelectAllBundlesFromSQL;
+                lowId = bigger;
+                keys = getKey(bigger);
+            }
+            if (getStorageModel() == SM_LONGLONG_KEYS && maxCount > 0) {
+                // get some more rows, in case the first row is smaller
+                // only required for SM_LONGLONG_KEYS
+                // probability is very low to get get the wrong first key, < 1 : 2^64
+                // see also bundleSelectAllIdsFrom SQL statement
+                maxCount += 10;
+            }
+            rs = conHelper.exec(sql, keys, false, maxCount);
+            Map<NodeId, NodeInfo> result = new LinkedHashMap<NodeId, NodeInfo>(maxCount);
+            while ((maxCount == 0 || result.size() < maxCount) && rs.next()) {
+                NodeId current;
+                if (getStorageModel() == SM_BINARY_KEYS) {
+                    current = new NodeId(rs.getBytes(1));
+                } else {
+                    long high = rs.getLong(1);
+                    long low = rs.getLong(2);
+                    current = new NodeId(high, low);
+                }
+                if (getStorageModel() == SM_LONGLONG_KEYS && lowId != null) {
+                    // skip the keys that are smaller or equal (see above, maxCount += 10)
+                    if (current.compareTo(lowId) <= 0) {
+                        continue;
+                    }
+                }
+                NodePropBundle bundle = readBundle(current, rs, getStorageModel() == SM_LONGLONG_KEYS
? 3 : 2);
+                NodeInfo nodeInfo = new NodeInfo(bundle);
+                result.put(nodeInfo.getId(), nodeInfo);
+            }
+            return result;
+        } catch (SQLException e) {
+            String msg = "getAllNodeIds failed.";
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            DbUtility.close(rs);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     @Override
     protected NodePropBundle loadBundle(NodeId id) throws ItemStateException {
         try {
@@ -1060,6 +1117,8 @@ public class BundleDbPersistenceManager
 
             bundleSelectAllIdsSQL = "select NODE_ID from " + schemaObjectPrefix + "BUNDLE
ORDER BY NODE_ID";
             bundleSelectAllIdsFromSQL = "select NODE_ID from " + schemaObjectPrefix + "BUNDLE
WHERE NODE_ID > ? ORDER BY NODE_ID";
+            bundleSelectAllBundlesSQL = "select NODE_ID, BUNDLE_DATA from " + schemaObjectPrefix
+ "BUNDLE ORDER BY NODE_ID";
+            bundleSelectAllBundlesFromSQL = "select NODE_ID, BUNDLE_DATA from " + schemaObjectPrefix
+ "BUNDLE WHERE NODE_ID > ? ORDER BY NODE_ID";
         } else {
             bundleInsertSQL = "insert into " + schemaObjectPrefix + "BUNDLE (BUNDLE_DATA,
NODE_ID_HI, NODE_ID_LO) values (?, ?, ?)";
             bundleUpdateSQL = "update " + schemaObjectPrefix + "BUNDLE set BUNDLE_DATA =
? where NODE_ID_HI = ? and NODE_ID_LO = ?";
@@ -1083,6 +1142,16 @@ public class BundleDbPersistenceManager
                 "select NODE_ID_HI, NODE_ID_LO from " + schemaObjectPrefix + "BUNDLE"
                 + " WHERE (NODE_ID_HI >= ?) AND (? IS NOT NULL)"
                 + " ORDER BY NODE_ID_HI, NODE_ID_LO";
+
+            bundleSelectAllBundlesSQL = "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from
" + schemaObjectPrefix
+                    + "BUNDLE ORDER BY NODE_ID_HI, NODE_ID_LO";
+            // need to use HI and LO parameters
+            // this is not the exact statement, but not all databases support WHERE (NODE_ID_HI,
NODE_ID_LOW) >= (?, ?)
+            bundleSelectAllBundlesFromSQL =
+                    "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from " + schemaObjectPrefix
+ "BUNDLE"
+                            + " WHERE (NODE_ID_HI >= ?) AND (? IS NOT NULL)"
+                            + " ORDER BY NODE_ID_HI, NODE_ID_LO";
+
         }
 
     }

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodeInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodeInfo.java?rev=1351240&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodeInfo.java
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodeInfo.java
Mon Jun 18 08:46:37 2012
@@ -0,0 +1,168 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.spi.Name;
+
+/**
+ * Holds purely structural information about a node. Used by the consistency checker.
+ */
+public final class NodeInfo {
+
+    /**
+     * The same node id in a NodeInfo graph typically occurs three times: as a the id of
the current
+     * NodeInfo, as the parent to another NodeInfo, and as a child of another NodeInfo. In
order to
+     * minimize the memory footprint use an NodeId object pool.
+     */
+    private static final ConcurrentMap<NodeId,NodeId> nodeIdPool = new ConcurrentHashMap<NodeId,
NodeId>(1000);
+
+    /**
+     * The node id
+     */
+    private final NodeId nodeId;
+
+    /**
+     * The parent node id
+     */
+    private final NodeId parentId;
+
+    /**
+     * The child ids
+     */
+    private List<NodeId> children;
+
+    /**
+     * Map of reference property names of this node with their node id values
+     */
+    private Map<Name, List<NodeId>> references;
+
+    /**
+     * Whether this node is referenceable or not
+     */
+    private boolean isReferenceable;
+
+    /**
+     * Create a new NodeInfo object from a bundle
+     *
+     * @param bundle the node bundle
+     */
+    public NodeInfo(final NodePropBundle bundle) {
+        nodeId = getNodeId(bundle.getId());
+        parentId = getNodeId(bundle.getParentId());
+
+        List<NodePropBundle.ChildNodeEntry> childNodeEntries = bundle.getChildNodeEntries();
+        if (!childNodeEntries.isEmpty()) {
+            children = new ArrayList<NodeId>(childNodeEntries.size());
+            for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) {
+                children.add(getNodeId(entry.getId()));
+            }
+        } else {
+            children = Collections.emptyList();
+        }
+
+        for (NodePropBundle.PropertyEntry entry : bundle.getPropertyEntries()) {
+            if (entry.getType() == PropertyType.REFERENCE) {
+                if (references == null) {
+                    references = new HashMap<Name, List<NodeId>>(4);
+                }
+                List<NodeId> values = new ArrayList<NodeId>(entry.getValues().length);
+                for (InternalValue value : entry.getValues()) {
+                    values.add(getNodeId(value.getNodeId()));
+                }
+                references.put(entry.getName(), values);
+            }
+        }
+
+        if (references == null) {
+            references = Collections.emptyMap();
+        }
+        isReferenceable = bundle.isReferenceable();
+    }
+
+    /**
+     * @return the node id of this node
+     */
+    public NodeId getId() {
+        return nodeId;
+    }
+
+    /**
+     * @return the parent id of this node
+     */
+    public NodeId getParentId() {
+        return parentId;
+    }
+
+    /**
+     * @return the child ids of this node
+     */
+    public List<NodeId> getChildren() {
+        return children;
+    }
+
+    /**
+     * @return the reference properties along with their node id values of this node
+     */
+    public Map<Name, List<NodeId>> getReferences() {
+        return references;
+    }
+
+    /**
+     * @return whether the node represented by this node info is referenceable
+     */
+    public boolean isReferenceable() {
+        return isReferenceable;
+    }
+
+    /**
+     * Simple pool implementation to minimize memory overhead from node id objects
+     * @param nodeId  node id to cache
+     * @return  the cached node id
+     */
+    private static NodeId getNodeId(NodeId nodeId) {
+        if (nodeId == null) {
+            return null;
+        }
+        NodeId cached = nodeIdPool.get(nodeId);
+        if (cached == null) {
+            cached = nodeIdPool.putIfAbsent(nodeId, nodeId);
+            if (cached == null) {
+                cached = nodeId;
+            }
+        }
+        return cached;
+    }
+
+    /**
+     * Clear the NodeId pool.
+     */
+    public static void clearPool() {
+        nodeIdPool.clear();
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java?rev=1351240&r1=1351239&r2=1351240&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/ConsistencyCheckerImplTest.java
Mon Jun 18 08:46:37 2012
@@ -29,6 +29,7 @@ import org.apache.jackrabbit.core.persis
 import org.apache.jackrabbit.core.persistence.bundle.ConsistencyCheckerImpl;
 import org.apache.jackrabbit.core.persistence.check.ConsistencyChecker;
 import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.NodeInfo;
 import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
@@ -221,6 +222,20 @@ public class ConsistencyCheckerImplTest 
             return allNodeIds;
         }
 
+        public Map<NodeId, NodeInfo> getAllNodeInfos(final NodeId after, final int
maxCount) throws ItemStateException, RepositoryException {
+            Map<NodeId, NodeInfo> allNodeInfos = new LinkedHashMap<NodeId, NodeInfo>();
+            boolean add = after == null;
+            for (Map.Entry<NodeId, NodePropBundle> entry : bundles.entrySet()) {
+                if (add) {
+                    allNodeInfos.put(entry.getKey(), new NodeInfo(entry.getValue()));
+                }
+                if (!add) {
+                    add = entry.getKey().equals(after);
+                }
+            }
+            return allNodeInfos;
+        }
+
         @Override
         protected NodePropBundle loadBundle(final NodeId id) {
             return bundles.get(id);



Mime
View raw message