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;
}