You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/07/08 13:50:19 UTC

svn commit: r209739 - in /incubator/jackrabbit/trunk/core/src: java/org/apache/jackrabbit/core/query/lucene/ test/org/apache/jackrabbit/core/query/

Author: mreutegg
Date: Fri Jul  8 04:50:18 2005
New Revision: 209739

URL: http://svn.apache.org/viewcvs?rev=209739&view=rev
Log:
JCR-160: Query index not in sync with workspace
- Improve error handling for uuids that have no node representation anymore, because they have been deleted in the meantime.

Added:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java   (with props)
    incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java   (with props)
Modified:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/DocOrderNodeIteratorImpl.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/NodeIteratorImpl.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RowIteratorImpl.java
    incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/DocOrderNodeIteratorImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/DocOrderNodeIteratorImpl.java?rev=209739&r1=209738&r2=209739&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/DocOrderNodeIteratorImpl.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/DocOrderNodeIteratorImpl.java Fri Jul  8 04:50:18 2005
@@ -27,11 +27,13 @@
 import javax.jcr.RepositoryException;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * Implements a NodeIterator that returns the nodes in document order.
  */
-class DocOrderNodeIteratorImpl extends NodeIteratorImpl {
+class DocOrderNodeIteratorImpl implements ScoreNodeIterator {
 
     /** Logger instance for this class */
     private static final Logger log = Logger.getLogger(DocOrderNodeIteratorImpl.class);
@@ -39,6 +41,15 @@
     /** A node iterator with ordered nodes */
     private NodeIteratorImpl orderedNodes;
 
+    /** The UUIDs of the nodes in the result set */
+    protected String[] uuids;
+
+    /** The score values for the nodes in the result set */
+    protected Float[] scores;
+
+    /** ItemManager to turn UUIDs into Node instances */
+    protected final ItemManager itemMgr;
+
     /**
      * Creates a <code>DocOrderNodeIteratorImpl</code> that orders the nodes
      * with <code>uuids</code> in document order.
@@ -47,38 +58,31 @@
      * @param scores the score values of the nodes.
      */
     DocOrderNodeIteratorImpl(final ItemManager itemMgr, String[] uuids, Float[] scores) {
-        super(itemMgr, uuids, scores);
+        this.itemMgr = itemMgr;
+        this.uuids = uuids;
+        this.scores = scores;
     }
 
     /**
      * {@inheritDoc}
      */
-    public Node nextNode() {
-        initOrderedIterator();
-        return orderedNodes.nextNode();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void skip(long skipNum) {
-        initOrderedIterator();
-        orderedNodes.skip(skipNum);
+    public Object next() {
+        return nextNodeImpl();
     }
 
     /**
      * {@inheritDoc}
      */
-    public long getSize() {
-        return uuids.length;
+    public Node nextNode() {
+        return nextNodeImpl();
     }
 
     /**
      * {@inheritDoc}
      */
-    public long getPosition() {
+    public NodeImpl nextNodeImpl() {
         initOrderedIterator();
-        return orderedNodes.getPosition();
+        return orderedNodes.nextNodeImpl();
     }
 
     /**
@@ -91,116 +95,183 @@
     /**
      * {@inheritDoc}
      */
-    public boolean hasNext() {
+    public void skip(long skipNum) {
         initOrderedIterator();
-        return orderedNodes.hasNext();
+        orderedNodes.skip(skipNum);
+    }
+
+    /**
+     * Returns the number of nodes in this iterator.
+     * </p>
+     * Note: The number returned by this method may differ from the number
+     * of nodes actually returned by calls to hasNext() / getNextNode()! This
+     * is because this iterator works on a lazy instantiation basis and while
+     * iterating over the nodes some of them might have been deleted in the
+     * meantime. Those will not be returned by getNextNode().
+     *
+     * @return the number of node in this iterator.
+     */
+    public long getSize() {
+        return uuids.length;
     }
 
     /**
      * {@inheritDoc}
      */
-    public Object next() {
+    public long getPosition() {
         initOrderedIterator();
-        return orderedNodes.next();
+        return orderedNodes.getPosition();
     }
 
     /**
      * {@inheritDoc}
      */
-    float getScore() {
+    public boolean hasNext() {
         initOrderedIterator();
-        return orderedNodes.getScore();
+        return orderedNodes.hasNext();
     }
 
     /**
      * {@inheritDoc}
      */
-    NodeImpl nextNodeImpl() {
+    public float getScore() {
         initOrderedIterator();
-        return orderedNodes.nextNodeImpl();
+        return orderedNodes.getScore();
     }
 
     //------------------------< internal >--------------------------------------
 
     /**
-     * Initializes the NodeIterator in document order, if needed.
+     * Initializes the NodeIterator in document order
      */
     private void initOrderedIterator() {
-        if (orderedNodes == null) {
-            long time = System.currentTimeMillis();
-            ScoreNode[] nodes = new ScoreNode[uuids.length];
-            for (int i = 0; i < uuids.length; i++) {
-                nodes[i] = new ScoreNode(uuids[i], scores[i]);
+        if (orderedNodes != null) {
+            return;
+        }
+        long time = System.currentTimeMillis();
+        ScoreNode[] nodes = new ScoreNode[uuids.length];
+        for (int i = 0; i < uuids.length; i++) {
+            nodes[i] = new ScoreNode(uuids[i], scores[i]);
+        }
+
+        final List invalidUUIDs = new ArrayList(2);
+
+        do {
+            if (invalidUUIDs.size() > 0) {
+                // previous sort run was not successful -> remove failed uuids
+                ScoreNode[] tmp = new ScoreNode[nodes.length - invalidUUIDs.size()];
+                int newIdx = 0;
+                for (int i = 0; i < nodes.length; i++) {
+                    if (!invalidUUIDs.contains(nodes[i].uuid)) {
+                        tmp[newIdx++] = nodes[i];
+                    }
+                }
+                nodes = tmp;
+                invalidUUIDs.clear();
             }
 
-            Arrays.sort(nodes, new Comparator() {
-                public int compare(Object o1, Object o2) {
-                    ScoreNode n1 = (ScoreNode) o1;
-                    ScoreNode n2 = (ScoreNode) o2;
-                    try {
-                        NodeImpl node1 = (NodeImpl) itemMgr.getItem(new NodeId(n1.uuid));
-                        NodeImpl node2 = (NodeImpl) itemMgr.getItem(new NodeId(n2.uuid));
-                        Path.PathElement[] path1 = node1.getPrimaryPath().getElements();
-                        Path.PathElement[] path2 = node2.getPrimaryPath().getElements();
-
-                        // find nearest common ancestor
-                        int commonDepth = 0; // root
-                        while (path1.length > commonDepth && path2.length > commonDepth) {
-                            if (path1[commonDepth].equals(path2[commonDepth])) {
-                                commonDepth++;
-                            } else {
-                                break;
+            try {
+                // sort the uuids
+                Arrays.sort(nodes, new Comparator() {
+                    public int compare(Object o1, Object o2) {
+                        ScoreNode n1 = (ScoreNode) o1;
+                        ScoreNode n2 = (ScoreNode) o2;
+                        try {
+                            NodeImpl node1 = null;
+                            try {
+                                node1 = (NodeImpl) itemMgr.getItem(new NodeId(n1.uuid));
+                            } catch (RepositoryException e) {
+                                log.warn("Node " + n1.uuid + " does not exist anymore: " + e);
+                                // node does not exist anymore
+                                invalidUUIDs.add(n1.uuid);
+                                throw new SortFailedException();
                             }
-                        }
-                        // path elements at last depth were equal
-                        commonDepth--;
+                            NodeImpl node2 = null;
+                            try {
+                                node2 = (NodeImpl) itemMgr.getItem(new NodeId(n2.uuid));
+                            } catch (RepositoryException e) {
+                                log.warn("Node " + n2.uuid + " does not exist anymore: " + e);
+                                // node does not exist anymore
+                                invalidUUIDs.add(n2.uuid);
+                                throw new SortFailedException();
+                            }
+                            Path.PathElement[] path1 = node1.getPrimaryPath().getElements();
+                            Path.PathElement[] path2 = node2.getPrimaryPath().getElements();
 
-                        // check if either path is an ancestor of the other
-                        if (path1.length - 1 == commonDepth) {
-                            // path1 itself is ancestor of path2
-                            return -1;
-                        }
-                        if (path2.length - 1 == commonDepth) {
-                            // path2 itself is ancestor of path1
-                            return 1;
-                        }
-                        // get common ancestor node
-                        NodeImpl commonNode = (NodeImpl) node1.getAncestor(commonDepth);
-                        // move node1/node2 to the commonDepth + 1
-                        // node1 and node2 then will be child nodes of commonNode
-                        node1 = (NodeImpl) node1.getAncestor(commonDepth + 1);
-                        node2 = (NodeImpl) node2.getAncestor(commonDepth + 1);
-                        for (NodeIterator it = commonNode.getNodes(); it.hasNext();) {
-                            Node child = it.nextNode();
-                            if (child.isSame(node1)) {
+                            // find nearest common ancestor
+                            int commonDepth = 0; // root
+                            while (path1.length > commonDepth && path2.length > commonDepth) {
+                                if (path1[commonDepth].equals(path2[commonDepth])) {
+                                    commonDepth++;
+                                } else {
+                                    break;
+                                }
+                            }
+                            // path elements at last depth were equal
+                            commonDepth--;
+
+                            // check if either path is an ancestor of the other
+                            if (path1.length - 1 == commonDepth) {
+                                // path1 itself is ancestor of path2
                                 return -1;
-                            } else if (child.isSame(node2)) {
+                            }
+                            if (path2.length - 1 == commonDepth) {
+                                // path2 itself is ancestor of path1
                                 return 1;
                             }
+                            // get common ancestor node
+                            NodeImpl commonNode = (NodeImpl) node1.getAncestor(commonDepth);
+                            // move node1/node2 to the commonDepth + 1
+                            // node1 and node2 then will be child nodes of commonNode
+                            node1 = (NodeImpl) node1.getAncestor(commonDepth + 1);
+                            node2 = (NodeImpl) node2.getAncestor(commonDepth + 1);
+                            for (NodeIterator it = commonNode.getNodes(); it.hasNext();) {
+                                Node child = it.nextNode();
+                                if (child.isSame(node1)) {
+                                    return -1;
+                                } else if (child.isSame(node2)) {
+                                    return 1;
+                                }
+                            }
+                            log.error("Internal error: unable to determine document order of nodes:");
+                            log.error("\tNode1: " + node1.getPath());
+                            log.error("\tNode2: " + node2.getPath());
+                        } catch (RepositoryException e) {
+                            log.error("Exception while sorting nodes in document order: " + e.toString(), e);
                         }
-                        log.error("Internal error: unable to determine document order of nodes:");
-                        log.error("\tNode1: " + node1.getPath());
-                        log.error("\tNode2: " + node2.getPath());
-                        return 0;
-                    } catch (RepositoryException e) {
-                        log.error("Exception while sorting nodes in document order: " + e.toString(), e);
-                        // todo ???
-                        return 0;
+                        // if we get here something went wrong
+                        // remove both uuids from array
+                        invalidUUIDs.add(n1.uuid);
+                        invalidUUIDs.add(n2.uuid);
+                        // terminate sorting
+                        throw new SortFailedException();
                     }
-                }
-            });
-
-            for (int i = 0; i < nodes.length; i++) {
-                uuids[i] = nodes[i].uuid;
-                scores[i] = nodes[i].score;
+                });
+            } catch (SortFailedException e) {
+                // retry
             }
-            if (log.isDebugEnabled()) {
-                log.debug("" + uuids.length + " node(s) ordered in " + (System.currentTimeMillis() - time) + " ms");
-            }
-            orderedNodes = new NodeIteratorImpl(itemMgr, uuids, scores);
+
+        } while (invalidUUIDs.size() > 0);
+
+        // resize uuids and scores array if we had to remove some uuids
+        if (uuids.length != nodes.length) {
+            uuids = new String[nodes.length];
+            scores = new Float[nodes.length];
+        }
+
+        for (int i = 0; i < nodes.length; i++) {
+            uuids[i] = nodes[i].uuid;
+            scores[i] = nodes[i].score;
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("" + uuids.length + " node(s) ordered in " + (System.currentTimeMillis() - time) + " ms");
         }
+        orderedNodes = new NodeIteratorImpl(itemMgr, uuids, scores);
     }
 
+    /**
+     * Simple helper class that associates a score with each node uuid.
+     */
     private static final class ScoreNode {
 
         final String uuid;
@@ -211,5 +282,11 @@
             this.uuid = uuid;
             this.score = score;
         }
+    }
+
+    /**
+     * Indicates that sorting failed.
+     */
+    private static final class SortFailedException extends RuntimeException {
     }
 }

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/NodeIteratorImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/NodeIteratorImpl.java?rev=209739&r1=209738&r2=209739&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/NodeIteratorImpl.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/NodeIteratorImpl.java Fri Jul  8 04:50:18 2005
@@ -22,7 +22,6 @@
 import org.apache.log4j.Logger;
 
 import javax.jcr.Node;
-import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 import java.util.NoSuchElementException;
 
@@ -30,7 +29,7 @@
  * Implements a {@link javax.jcr.NodeIterator} returned by
  * {@link javax.jcr.query.QueryResult#getNodes()}.
  */
-class NodeIteratorImpl implements NodeIterator {
+class NodeIteratorImpl implements ScoreNodeIterator {
 
     /** Logger instance for this class */
     private static final Logger log = Logger.getLogger(NodeIteratorImpl.class);
@@ -45,7 +44,10 @@
     protected final ItemManager itemMgr;
 
     /** Current position in the UUID array */
-    private int pos = 0;
+    protected int pos = 0;
+
+    /** Reference to the next node instance */
+    private NodeImpl next;
 
     /**
      * Creates a new <code>NodeIteratorImpl</code> instance.
@@ -60,6 +62,7 @@
         this.itemMgr = itemMgr;
         this.uuids = uuids;
         this.scores = scores;
+        fetchNext();
     }
 
     /**
@@ -83,6 +86,21 @@
     }
 
     /**
+     * Returns the next <code>Node</code> in the result set.
+     *
+     * @return the next <code>Node</code> in the result set.
+     * @throws NoSuchElementException if iteration has no more <code>Node</code>s.
+     */
+    public NodeImpl nextNodeImpl() throws NoSuchElementException {
+        if (next == null) {
+            throw new NoSuchElementException();
+        }
+        NodeImpl n = next;
+        fetchNext();
+        return n;
+    }
+
+    /**
      * Skip a number of <code>Node</code>s in this iterator.
      * @param skipNum the non-negative number of <code>Node</code>s to skip
      * @throws NoSuchElementException
@@ -99,10 +117,15 @@
     }
 
     /**
-     * Returns the number of <code>Node</code>s in this
-     * <code>NodeIterator</code>.
-     * @return the number of <code>Node</code>s in this
-     *   <code>NodeIterator</code>.
+     * Returns the number of nodes in this iterator.
+     * </p>
+     * Note: The number returned by this method may differ from the number
+     * of nodes actually returned by calls to hasNext() / getNextNode()! This
+     * is because this iterator works on a lazy instantiation basis and while
+     * iterating over the nodes some of them might have been deleted in the
+     * meantime. Those will not be returned by getNextNode().
+     *
+     * @return the number of node in this iterator.
      */
     public long getSize() {
         return uuids.length;
@@ -123,7 +146,7 @@
      *  available; <code>false</code> otherwise.
      */
     public boolean hasNext() {
-        return pos < uuids.length;
+        return next != null;
     }
 
     /**
@@ -139,29 +162,30 @@
      * @return the score of the node returned by {@link #nextNode()}.
      * @throws NoSuchElementException if there is no next node.
      */
-    float getScore() throws NoSuchElementException {
+    public float getScore() throws NoSuchElementException {
         if (!hasNext()) {
             throw new NoSuchElementException();
         }
-        return scores[pos].floatValue();
+        return scores[pos - 1].floatValue();
     }
 
     /**
-     * Returns the next <code>Node</code> in the result set.
-     * @return the next <code>Node</code> in the result set.
-     * @throws NoSuchElementException if iteration has no more
-     *   <code>Node</code>s.
-     */
-    NodeImpl nextNodeImpl() throws NoSuchElementException {
-        if (pos >= uuids.length) {
-            throw new NoSuchElementException();
-        }
-        try {
-            return (NodeImpl) itemMgr.getItem(new NodeId(uuids[pos++]));
-        } catch (RepositoryException e) {
-            log.error("Exception retrieving Node with UUID: "
-                    + uuids[pos - 1] + ": " + e.toString());
-            throw new NoSuchElementException();
+     * Clears {@link #next} and tries to fetch the next Node instance.
+     * When this method returns {@link #next} refers to the next available
+     * node instance in this iterator. If {@link #next} is null when this
+     * method returns, then there are no more valid element in this iterator.
+     */
+    protected void fetchNext() {
+        // reset
+        next = null;
+        while (next == null && pos < uuids.length) {
+            try {
+                next = (NodeImpl) itemMgr.getItem(new NodeId(uuids[pos++]));
+            } catch (RepositoryException e) {
+                log.error("Exception retrieving Node with UUID: "
+                        + uuids[pos - 1] + ": " + e.toString());
+                // try next
+            }
         }
     }
 }

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java?rev=209739&r1=209738&r2=209739&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java Fri Jul  8 04:50:18 2005
@@ -128,7 +128,7 @@
      * Creates a node iterator over the result nodes.
      * @return a node iterator over the result nodes.
      */
-    private NodeIteratorImpl getNodeIterator() {
+    private ScoreNodeIterator getNodeIterator() {
         if (docOrder) {
             return new DocOrderNodeIteratorImpl(itemMgr, uuids, scores);
         } else {

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RowIteratorImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RowIteratorImpl.java?rev=209739&r1=209738&r2=209739&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RowIteratorImpl.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RowIteratorImpl.java Fri Jul  8 04:50:18 2005
@@ -48,7 +48,7 @@
     /**
      * Iterator over nodes, that constitute the result set.
      */
-    private final NodeIteratorImpl nodes;
+    private final ScoreNodeIterator nodes;
 
     /**
      * Array of select property names
@@ -64,13 +64,13 @@
      * Creates a new <code>RowIteratorImpl</code> that iterates over the result
      * nodes.
      *
-     * @param nodes      a <code>NodeIteratorImpl</code> that contains the nodes of
+     * @param nodes      a <code>ScoreNodeIterator</code> that contains the nodes of
      *                   the query result.
      * @param properties <code>QName</code> of the select properties.
      * @param resolver   <code>NamespaceResolver</code> of the user
      *                   <code>Session</code>.
      */
-    RowIteratorImpl(NodeIteratorImpl nodes, QName[] properties, NamespaceResolver resolver) {
+    RowIteratorImpl(ScoreNodeIterator nodes, QName[] properties, NamespaceResolver resolver) {
         this.nodes = nodes;
         this.properties = properties;
         this.resolver = resolver;

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java?rev=209739&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java Fri Jul  8 04:50:18 2005
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.query.lucene;
+
+import org.apache.jackrabbit.core.NodeImpl;
+
+import javax.jcr.NodeIterator;
+
+/**
+ * Extends the {@link javax.jcr.NodeIterator} interface by adding a {@link
+ * #getScore()} method that returns the score for the node that is returned by
+ * {@link javax.jcr.NodeIterator#nextNode()}.
+ */
+public interface ScoreNodeIterator extends NodeIterator {
+
+    /**
+     * Returns the score of the node returned by {@link #nextNode()}. In other
+     * words, this method returns the score value of the next
+     * <code>Node</code>.
+     *
+     * @return the score of the node returned by {@link #nextNode()}.
+     * @throws java.util.NoSuchElementException
+     *          if there is no next node.
+     */
+    public float getScore();
+
+    /**
+     * Returns the next <code>Node</code> in the result set.
+     *
+     * @return the next <code>Node</code> in the result set.
+     * @throws java.util.NoSuchElementException
+     *          if iteration has no more <code>Node</code>s.
+     */
+    public NodeImpl nextNodeImpl();
+}

Propchange: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/ScoreNodeIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java?rev=209739&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java (added)
+++ incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java Fri Jul  8 04:50:18 2005
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.query;
+
+import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.QueryManager;
+
+/**
+ * Tests if the NodeIterator returned by {@link javax.jcr.query.QueryResult#getNodes()}
+ * skips Nodes that have been deleted in the meantime.
+ */
+public class SkipDeletedNodesTest extends AbstractQueryTest {
+
+    private Session s2;
+
+    private QueryManager qm;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        s2 = helper.getSuperuserSession();
+        qm = s2.getWorkspace().getQueryManager();
+    }
+
+    protected void tearDown() throws Exception {
+        try {
+            if (s2 != null) {
+                s2.logout();
+            }
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Executes a query with one session and removes a node from that query
+     * result with another session.
+     */
+    public void testRemoveFirstNode() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        testRootNode.addNode("node2");
+        testRootNode.addNode("node3");
+        testRootNode.save();
+
+        // query the workspace for all three nodes
+        String stmt = testPath + "/*";
+        QueryResult res = qm.createQuery(stmt, Query.XPATH).execute();
+
+        // now remove the first node
+        n1.remove();
+        testRootNode.save();
+
+        // iterate over nodes
+        log.println("Result nodes:");
+        for (NodeIterator it = res.getNodes(); it.hasNext(); ) {
+            try {
+                log.println(it.nextNode().getPath());
+            } catch (InvalidItemStateException e) {
+                // this is allowed
+                log.println("Invalid: <deleted>");
+            }
+        }
+    }
+
+    /**
+     * Executes a query with one session and removes a node from that query
+     * result with another session.
+     */
+    public void testRemoveSomeNode() throws RepositoryException {
+        testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        testRootNode.addNode("node3");
+        testRootNode.save();
+
+        // query the workspace for all three nodes
+        String stmt = testPath + "/*";
+        QueryResult res = qm.createQuery(stmt, Query.XPATH).execute();
+
+        // now remove the second node
+        n2.remove();
+        testRootNode.save();
+
+        // iterate over nodes
+        log.println("Result nodes:");
+        for (NodeIterator it = res.getNodes(); it.hasNext(); ) {
+            try {
+                log.println(it.nextNode().getPath());
+            } catch (InvalidItemStateException e) {
+                // this is allowed
+                log.println("Invalid: <deleted>");
+            }
+        }
+    }
+
+    /**
+     * Executes a query with one session and removes a node from that query
+     * result with another session.
+     */
+    public void testRemoveLastNode() throws RepositoryException {
+        testRootNode.addNode("node1");
+        testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+        testRootNode.save();
+
+        // query the workspace for all three nodes
+        String stmt = testPath + "/*";
+        QueryResult res = qm.createQuery(stmt, Query.XPATH).execute();
+
+        // now remove the last node
+        n3.remove();
+        testRootNode.save();
+
+        // iterate over nodes
+        log.println("Result nodes:");
+        for (NodeIterator it = res.getNodes(); it.hasNext(); ) {
+            try {
+                log.println(it.nextNode().getPath());
+            } catch (InvalidItemStateException e) {
+                // this is allowed
+                log.println("Invalid: <deleted>");
+            }
+        }
+    }
+
+    /**
+     * Executes a query with one session and removes a node from that query
+     * result with another session.
+     * </p>This test is different from the other tests that it removes the
+     * node after another session has called hasNext() to retrieve the node
+     * that gets deleted.
+     */
+    public void testRemoveFirstNodeAfterHasNext() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        testRootNode.addNode("node2");
+        testRootNode.addNode("node3");
+        testRootNode.save();
+
+        // query the workspace for all three nodes
+        String stmt = testPath + "/*";
+        QueryResult res = qm.createQuery(stmt, Query.XPATH).execute();
+
+        NodeIterator it = res.getNodes();
+        it.hasNext();
+
+        // now remove the first node
+        n1.remove();
+        testRootNode.save();
+
+        // iterate over nodes
+        log.println("Result nodes:");
+        while (it.hasNext()) {
+            try {
+                log.println(it.nextNode().getPath());
+            } catch (InvalidItemStateException e) {
+                // this is allowed
+                log.println("Invalid: <deleted>");
+            }
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/SkipDeletedNodesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java?rev=209739&r1=209738&r2=209739&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java (original)
+++ incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java Fri Jul  8 04:50:18 2005
@@ -42,6 +42,7 @@
         suite.addTestSuite(SQLTest.class);
         suite.addTestSuite(OrderByTest.class);
         suite.addTestSuite(XPathAxisTest.class);
+        suite.addTestSuite(SkipDeletedNodesTest.class);
 
         return suite;
     }