Author: rhauch
Date: 2008-12-12 18:00:10 -0500 (Fri, 12 Dec 2008)
New Revision: 685
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadNextBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
Log:
DNA-40 and DNA-265
Changed the Graph API to improve the methods of getting blocks of children, including
adding a new technique for getting the 'n' children that follow a
previously-returned sibling. This did change the API, but these were methods that were
not used except in test cases.
Also implemented in the JPA Store connector the process methods for
ReadBlockOfChildrenRequest and ReadNextBlockOfChildrenRequest, utilizing efficient
database operations for these methods to efficiently find and return the block of
children.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-12-12 00:05:32 UTC
(rev 684)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-12-12 23:00:10 UTC
(rev 685)
@@ -56,6 +56,7 @@
import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
import org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest;
import org.jboss.dna.graph.requests.ReadBranchRequest;
+import org.jboss.dna.graph.requests.ReadNextBlockOfChildrenRequest;
import org.jboss.dna.graph.requests.ReadNodeRequest;
import org.jboss.dna.graph.requests.ReadPropertyRequest;
import org.jboss.dna.graph.requests.RemovePropertiesRequest;
@@ -877,12 +878,13 @@
/**
* Request that the children be read on the node defined via the
<code>of(...)</code> method on the returned {@link Of}
- * object. Once the location is specified, the {@link List list of children} are read
and then returned.
+ * object. The returned object is used to supply the remaining information, including
either the {@link Children#of(Location)
+ * location of the parent}, or that a subset of the children should be retrieved
{@link Children#inBlockOf(int) in a block}.
*
- * @return the object that is used to specified the node whose children are to be
read, and which will return the children
+ * @return the object that is used to specify the remaining inputs for the request,
and which will return the children
*/
- public Of<List<Location>> getChildren() {
- return new Of<List<Location>>() {
+ public Children<List<Location>> getChildren() {
+ return new Children<List<Location>>() {
public List<Location> of( String path ) {
return of(new Location(createPath(path)));
}
@@ -909,64 +911,68 @@
queue().submit(request);
return request.getChildren();
}
- };
- }
- /**
- * Request that the children in the specified index range be read on the node defined
via the <code>of(...)</code> method on
- * the returned {@link Of} object. Once the location is specified, the {@link List
list of children} are read and then
- * returned.
- *
- * @param startingIndex the index of the first child to be read
- * @param endingIndex the index past the last the first child to be read
- * @return the object that is used to specified the node whose children are to be
read, and which will return the children
- */
- public Of<List<Location>> getChildrenInRange( final int startingIndex,
- final int endingIndex ) {
- CheckArg.isNonNegative(startingIndex, "startingIndex");
- CheckArg.isPositive(endingIndex, "endingIndex");
- int count = endingIndex - startingIndex;
- return getChildrenInBlock(startingIndex, count);
- }
+ public BlockOfChildren<List<Location>> inBlockOf( final int
blockSize ) {
+ return new BlockOfChildren<List<Location>>() {
+ public Under<List<Location>> startingAt( final int
startingIndex ) {
+ return new Under<List<Location>>() {
+ public List<Location> under( String path ) {
+ return under(new Location(createPath(path)));
+ }
- /**
- * Request that the children in the specified block be read on the node defined via
the <code>of(...)</code> method on the
- * returned {@link Of} object. Once the location is specified, the {@link List list
of children} are read and then returned.
- *
- * @param startingIndex the index of the first child to be read
- * @param blockSize the maximum number of children that should be read
- * @return the object that is used to specified the node whose children are to be
read, and which will return the children
- */
- public Of<List<Location>> getChildrenInBlock( final int startingIndex,
- final int blockSize ) {
- CheckArg.isNonNegative(startingIndex, "startingIndex");
- CheckArg.isPositive(blockSize, "blockSize");
- return new Of<List<Location>>() {
- public List<Location> of( String path ) {
- return of(new Location(createPath(path)));
- }
+ public List<Location> under( Path path ) {
+ return under(new Location(path));
+ }
- public List<Location> of( Path path ) {
- return of(new Location(path));
- }
+ public List<Location> under( Property idProperty ) {
+ return under(new Location(idProperty));
+ }
- public List<Location> of( Property idProperty ) {
- return of(new Location(idProperty));
- }
+ public List<Location> under( Property firstIdProperty,
+ Property...
additionalIdProperties ) {
+ return under(new Location(firstIdProperty,
additionalIdProperties));
+ }
- public List<Location> of( Property firstIdProperty,
- Property... additionalIdProperties ) {
- return of(new Location(firstIdProperty, additionalIdProperties));
- }
+ public List<Location> under( UUID uuid ) {
+ return under(new Location(uuid));
+ }
- public List<Location> of( UUID uuid ) {
- return of(new Location(uuid));
- }
+ public List<Location> under( Location at ) {
+ ReadBlockOfChildrenRequest request = new
ReadBlockOfChildrenRequest(at, startingIndex, blockSize);
+ queue().submit(request);
+ return request.getChildren();
+ }
+ };
+ }
- public List<Location> of( Location at ) {
- ReadBlockOfChildrenRequest request = new ReadBlockOfChildrenRequest(at,
startingIndex, blockSize);
- queue().submit(request);
- return request.getChildren();
+ public List<Location> startingAfter( final Location
previousSibling ) {
+ ReadNextBlockOfChildrenRequest request = new
ReadNextBlockOfChildrenRequest(previousSibling, blockSize);
+ queue().submit(request);
+ return request.getChildren();
+ }
+
+ public List<Location> startingAfter( String
pathOfPreviousSibling ) {
+ return startingAfter(new
Location(createPath(pathOfPreviousSibling)));
+ }
+
+ public List<Location> startingAfter( Path pathOfPreviousSibling
) {
+ return startingAfter(new Location(pathOfPreviousSibling));
+ }
+
+ public List<Location> startingAfter( UUID uuidOfPreviousSibling
) {
+ return startingAfter(new Location(uuidOfPreviousSibling));
+ }
+
+ public List<Location> startingAfter( Property
idPropertyOfPreviousSibling ) {
+ return startingAfter(new Location(idPropertyOfPreviousSibling));
+ }
+
+ public List<Location> startingAfter( Property
firstIdProperyOfPreviousSibling,
+ Property...
additionalIdPropertiesOfPreviousSibling ) {
+ return startingAfter(new
Location(firstIdProperyOfPreviousSibling,
+
additionalIdPropertiesOfPreviousSibling));
+ }
+ };
}
};
}
@@ -2696,6 +2702,166 @@
}
/**
+ * A component used to supply the details for getting children of another node. If
all of the children are to be obtained,
+ * then the parent can be specified using one of the <code>of(...)</code>
methods on this component. If, however, only some of
+ * the nodes are to be returned (e.g., a "block" of children), then specify
the {@link #inBlockOf(int) block size} followed by
+ * the {@link BlockOfChildren block size and parent}.
+ *
+ * @param <Next>
+ * @author Randall Hauch
+ */
+ public interface Children<Next> extends Of<Next> {
+ /**
+ * Specify that a block of children are to be retreived, and in particular the
number of children that are to be returned.
+ *
+ * @param blockSize the number of children that are to be retrieved in the block;
must be positive
+ * @return the interface used to specify the starting point for the block and the
parent
+ */
+ BlockOfChildren<Next> inBlockOf( int blockSize );
+ }
+
+ /**
+ * A component used to specify a block of children starting either {@link
#startingAt(int) at a particular index} or
+ * {@link #startingAfter(Location) after a previous sibling}.
+ *
+ * @param <Next>
+ * @author Randall Hauch
+ */
+ public interface BlockOfChildren<Next> {
+ /**
+ * Specify the block of children is to start at the supplied index.
+ *
+ * @param startingIndex the zero-based index of the first child to be returned in
the block
+ * @return interface used to specify the parent of the children; never null
+ */
+ Under<Next> startingAt( int startingIndex );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param previousSibling the location of the sibling node that is before the
first node in the block
+ * @return the children; never null
+ */
+ Next startingAfter( Location previousSibling );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param pathToPreviousSiblingName the path of the sibling node that is before
the first node in the block
+ * @return the children; never null
+ */
+ Next startingAfter( String pathToPreviousSiblingName );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param previousSibling the path of the sibling node that is before the first
node in the block
+ * @return the children; never null
+ */
+ Next startingAfter( Path previousSibling );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param previousSiblingUuid the UUID of the sibling node that is before the
first node in the block
+ * @return the children; never null
+ */
+ Next startingAfter( UUID previousSiblingUuid );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param idPropertyOfPreviousSibling the property that uniquely identifies the
previous sibling
+ * @return the children; never null
+ */
+ Next startingAfter( Property idPropertyOfPreviousSibling );
+
+ /**
+ * Specify the block of children is to start with the child immediately following
the supplied node. This method is
+ * typically used when a previous block of children has already been retrieved
and this request is retrieving the next
+ * block.
+ *
+ * @param firstIdPropertyOfPreviousSibling the first property that, with the
<code>additionalIdProperties</code>, uniquely
+ * identifies the previous sibling
+ * @param additionalIdPropertiesOfPreviousSibling the additional properties that,
with the
+ * <code>additionalIdProperties</code>, uniquely identifies
the previous sibling
+ * @return the children; never null
+ */
+ Next startingAfter( Property firstIdPropertyOfPreviousSibling,
+ Property... additionalIdPropertiesOfPreviousSibling );
+ }
+
+ /**
+ * The interface for defining the node under which which a request operates.
+ *
+ * @param <Next> The interface that is to be returned when the request is
completed
+ * @author Randall Hauch
+ */
+ public interface Under<Next> {
+ /**
+ * Specify the location of the node under which the request is to operate.
+ *
+ * @param to the location of the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( Location to );
+
+ /**
+ * Specify the path of the node under which the request is to operate.
+ *
+ * @param toPath the path of the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( String toPath );
+
+ /**
+ * Specify the path of the node under which the request is to operate.
+ *
+ * @param to the path of the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( Path to );
+
+ /**
+ * Specify the UUID of the node under which the request is to operate.
+ *
+ * @param to the UUID of the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( UUID to );
+
+ /**
+ * Specify the unique identification property that identifies the node under
which the request is to operate.
+ *
+ * @param idProperty the property that uniquely identifies the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( Property idProperty );
+
+ /**
+ * Specify the unique identification properties that identify the node under
which the request is to operate.
+ *
+ * @param firstIdProperty the first property that, with the
<code>additionalIdProperties</code>, uniquely identifies the
+ * new parent
+ * @param additionalIdProperties the additional properties that, with the
<code>additionalIdProperties</code>, uniquely
+ * identifies the new parent
+ * @return the interface for additional requests or actions
+ */
+ Next under( Property firstIdProperty,
+ Property... additionalIdProperties );
+ }
+
+ /**
* A component used to set the values on a property.
*
* @param <Next>
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -58,7 +58,7 @@
* an array.
*
* @param of the location of the node whose children are to be read
- * @param startingIndex the index of the first child to be included in the block
+ * @param startingIndex the zero-based index of the first child to be included in the
block
* @param count the maximum number of children that should be included in the block
* @throws IllegalArgumentException if the location is null, if
<code>startingIndex</code> is negative, or if
* <code>count</count> is less than 1.
@@ -108,7 +108,8 @@
* Get the starting index of the block, which is the index of the first child to
include. This index corresponds to the index
* of all children in the list, not the {@link Path.Segment#getIndex()
same-name-sibiling index}.
*
- * @return the child index at which this block starts; never negative and always less
than {@link #endingBefore()}
+ * @return the (zero-based) child index at which this block starts; never negative
and always less than
+ * {@link #endingBefore()}
* @see #endingBefore()
* @see #count()
*/
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadNextBlockOfChildrenRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadNextBlockOfChildrenRequest.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/ReadNextBlockOfChildrenRequest.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -33,7 +33,8 @@
/**
* Instruction to read a block of the children of a node, where the block is dictated by
the {@link #startingAfter location of the
* child preceding the block} and the {@link #count() maximum number of children} to
include in the block. This command is useful
- * when paging through a large number of children.
+ * when paging through a large number of children, when the previous block of children
was already retrieved and the next block is
+ * to be read.
*
* @see ReadBlockOfChildrenRequest
* @author Randall Hauch
@@ -44,31 +45,23 @@
private static final long serialVersionUID = 1L;
- private final Location of;
private final List<Location> children = new LinkedList<Location>();
private final Location startingAfter;
private final int count;
- private Location actualLocation;
+ private Location actualStartingAfter;
/**
- * Create a request to read a block of the children of a node at the supplied
location. The block is defined by the starting
- * index of the first child and the number of children to include. Note that this
index is <i>not</i> the
- * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index
of the child as if the children were in
- * an array.
+ * Create a request to read those children of a node that are immediately after a
supplied sibling node.
*
- * @param of the location of the node whose children are to be read
- * @param startingAfter the child that was the last child of the previous block of
children read
+ * @param startingAfter the location of the previous sibling that was the last child
of the previous block of children read
* @param count the maximum number of children that should be included in the block
* @throws IllegalArgumentException if the location is null, if
<code>startingAfter</code> is null, or if
* <code>count</count> is less than 1.
*/
- public ReadNextBlockOfChildrenRequest( Location of,
- Location startingAfter,
+ public ReadNextBlockOfChildrenRequest( Location startingAfter,
int count ) {
- CheckArg.isNotNull(of, "of");
CheckArg.isNotNull(startingAfter, "startingAfter");
CheckArg.isPositive(count, "count");
- this.of = of;
this.startingAfter = startingAfter;
this.count = count;
}
@@ -84,15 +77,6 @@
}
/**
- * Get the location defining the node whose children are to be read.
- *
- * @return the location of the parent node; never null
- */
- public Location of() {
- return of;
- }
-
- /**
* Get the maximum number of children that may be returned in the block.
*
* @return the block's maximum count
@@ -176,28 +160,29 @@
* Sets the actual and complete location of the node whose children have been read.
This method must be called when processing
* the request, and the actual location must have a {@link Location#getPath() path}.
*
- * @param actual the actual location of the node being read, or null if the {@link
#of() current location} should be used
+ * @param actual the actual location of the node being read, or null if the {@link
#startingAfter() starting after location}
+ * should be used
* @throws IllegalArgumentException if the actual location does not represent the
{@link Location#isSame(Location) same
- * location} as the {@link #of() current location}, or if the actual location
does not have a path.
+ * location} as the {@link #startingAfter() starting after location}, or if
the actual location does not have a path.
*/
- public void setActualLocationOfNode( Location actual ) {
- if (!of.isSame(actual)) { // not same if actual is null
- throw new
IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual,
of));
+ public void setActualLocationOfStartingAfterNode( Location actual ) {
+ if (!startingAfter.isSame(actual)) { // not same if actual is null
+ throw new
IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual,
startingAfter));
}
assert actual != null;
if (!actual.hasPath()) {
throw new
IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
}
- this.actualLocation = actual;
+ this.actualStartingAfter = actual;
}
/**
- * Get the actual location of the node whose children were read.
+ * Get the actual location of the {@link #startingAfter() starting after} sibling.
*
* @return the actual location, or null if the actual location was not set
*/
- public Location getActualLocationOfNode() {
- return actualLocation;
+ public Location getActualLocationOfStartingAfterNode() {
+ return actualStartingAfter;
}
/**
@@ -209,7 +194,6 @@
public boolean equals( Object obj ) {
if (this.getClass().isInstance(obj)) {
ReadNextBlockOfChildrenRequest that = (ReadNextBlockOfChildrenRequest)obj;
- if (!this.of().equals(that.of())) return false;
if (!this.startingAfter().equals(that.startingAfter())) return false;
if (this.count() != that.count()) return false;
return true;
@@ -225,9 +209,9 @@
@Override
public String toString() {
if (count() == 1) {
- return "read one child of " + of() + " starting after " +
startingAfter();
+ return "read the next child after " + startingAfter();
}
- return "read " + count() + " children of " + of();
+ return "read the next " + count() + " children after " +
startingAfter();
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -283,8 +283,22 @@
*/
public void process( ReadNextBlockOfChildrenRequest request ) {
if (request == null) return;
+
+ // Get the parent path ...
+ Path path = request.startingAfter().getPath();
+ Location actualSiblingLocation = request.startingAfter();
+ Path parentPath = null;
+ if (path != null) parentPath = path.getParent();
+ if (parentPath == null) {
+ ReadAllPropertiesRequest readPropertiesOfSibling = new
ReadAllPropertiesRequest(request.startingAfter());
+ process(readPropertiesOfSibling);
+ actualSiblingLocation = readPropertiesOfSibling.getActualLocationOfNode();
+ parentPath = actualSiblingLocation.getPath().getParent();
+ }
+ assert parentPath != null;
+
// Convert the request to a ReadAllChildrenRequest and execute it ...
- ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(request.of());
+ ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(new
Location(parentPath));
process(readAll);
if (readAll.hasError()) {
request.setError(readAll.getError());
@@ -308,7 +322,7 @@
}
// Set the actual location ...
- request.setActualLocationOfNode(readAll.getActualLocationOfNode());
+ request.setActualLocationOfStartingAfterNode(actualSiblingLocation);
}
/**
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-12-12 00:05:32
UTC (rev 684)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-12-12 23:00:10
UTC (rev 685)
@@ -56,6 +56,7 @@
import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
import org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.requests.ReadNextBlockOfChildrenRequest;
import org.jboss.dna.graph.requests.ReadNodeRequest;
import org.jboss.dna.graph.requests.ReadPropertyRequest;
import org.jboss.dna.graph.requests.Request;
@@ -214,11 +215,15 @@
assertThat(read.getChildren(), hasItems(children));
}
- protected void assertNextRequestReadRangeOfChildren( Location at,
- int startIndex,
- int endIndex,
- Location... children ) {
- assertNextRequestReadBlockOfChildren(at, startIndex, endIndex - startIndex,
children);
+ protected void assertNextRequestReadNextBlockOfChildren( Location previousSibling,
+ int maxCount,
+ Location... children ) {
+ Request request = executedRequests.poll();
+ assertThat(request, is(instanceOf(ReadNextBlockOfChildrenRequest.class)));
+ ReadNextBlockOfChildrenRequest read = (ReadNextBlockOfChildrenRequest)request;
+ assertThat(read.startingAfter(), is(previousSibling));
+ assertThat(read.count(), is(maxCount));
+ assertThat(read.getChildren(), hasItems(children));
}
protected void assertNextRequestReadNode( Location at ) {
@@ -418,36 +423,36 @@
}
@Test
- public void shouldGetChildrenInBlockOnNode() {
+ public void shouldGetChildrenInBlockAtStartingIndex() {
Location child1 = new Location(createPath(validPath, "x"));
Location child2 = new Location(createPath(validPath, "y"));
Location child3 = new Location(createPath(validPath, "z"));
setChildrenToReadOn(new Location(validPath), child1, child2, child3);
- List<Location> children = graph.getChildrenInBlock(0, 2).of(validPath);
+ List<Location> children =
graph.getChildren().inBlockOf(2).startingAt(0).under(validPath);
assertThat(numberOfExecutions, is(1));
assertNextRequestReadBlockOfChildren(new Location(validPath), 0, 2, child1,
child2);
assertNoMoreRequests();
assertThat(children, hasItems(child1, child2));
- children = graph.getChildrenInBlock(1, 2).of(validPath);
+ children = graph.getChildren().inBlockOf(2).startingAt(1).under(validPath);
assertThat(numberOfExecutions, is(1));
assertNextRequestReadBlockOfChildren(new Location(validPath), 1, 2, child2,
child3);
assertNoMoreRequests();
assertThat(children, hasItems(child2, child3));
- children = graph.getChildrenInBlock(2, 2).of(validPath);
+ children = graph.getChildren().inBlockOf(2).startingAt(2).under(validPath);
assertThat(numberOfExecutions, is(1));
assertNextRequestReadBlockOfChildren(new Location(validPath), 2, 2, child3);
assertNoMoreRequests();
assertThat(children, hasItems(child3));
- children = graph.getChildrenInBlock(20, 2).of(validPath);
+ children = graph.getChildren().inBlockOf(2).startingAt(20).under(validPath);
assertThat(numberOfExecutions, is(1));
assertNextRequestReadBlockOfChildren(new Location(validPath), 20, 2);
assertNoMoreRequests();
assertThat(children.isEmpty(), is(true));
- children = graph.getChildrenInBlock(0, 20).of(validPath);
+ children = graph.getChildren().inBlockOf(20).startingAt(0).under(validPath);
assertThat(numberOfExecutions, is(1));
assertNextRequestReadBlockOfChildren(new Location(validPath), 0, 20, child1,
child2, child3);
assertNoMoreRequests();
@@ -455,40 +460,32 @@
}
@Test
- public void shouldGetChildrenInRangeOnNode() {
- Location child1 = new Location(createPath(validPath, "x"));
- Location child2 = new Location(createPath(validPath, "y"));
- Location child3 = new Location(createPath(validPath, "z"));
+ public void shouldGetChildrenInBlockAfterPreviousSibling() {
+ Path pathX = createPath(validPath, "x");
+ Path pathY = createPath(validPath, "y");
+ Path pathZ = createPath(validPath, "z");
+ Location child1 = new Location(pathX);
+ Location child2 = new Location(pathY);
+ Location child3 = new Location(pathZ);
setChildrenToReadOn(new Location(validPath), child1, child2, child3);
- List<Location> children = graph.getChildrenInRange(0, 2).of(validPath);
- assertThat(numberOfExecutions, is(1));
- assertNextRequestReadRangeOfChildren(new Location(validPath), 0, 2, child1,
child2);
- assertNoMoreRequests();
- assertThat(children, hasItems(child1, child2));
- children = graph.getChildrenInRange(1, 3).of(validPath);
+ List<Location> children =
graph.getChildren().inBlockOf(2).startingAfter(pathX);
assertThat(numberOfExecutions, is(1));
- assertNextRequestReadRangeOfChildren(new Location(validPath), 1, 3, child2,
child3);
+ assertNextRequestReadNextBlockOfChildren(new Location(pathX), 2, child2,
child3);
assertNoMoreRequests();
assertThat(children, hasItems(child2, child3));
- children = graph.getChildrenInRange(2, 4).of(validPath);
+ children = graph.getChildren().inBlockOf(3).startingAfter(pathX);
assertThat(numberOfExecutions, is(1));
- assertNextRequestReadRangeOfChildren(new Location(validPath), 2, 4, child3);
+ assertNextRequestReadNextBlockOfChildren(new Location(pathX), 3, child2,
child3);
assertNoMoreRequests();
- assertThat(children, hasItems(child3));
+ assertThat(children, hasItems(child2, child3));
- children = graph.getChildrenInRange(20, 21).of(validPath);
+ children = graph.getChildren().inBlockOf(2).startingAfter(pathY);
assertThat(numberOfExecutions, is(1));
- assertNextRequestReadRangeOfChildren(new Location(validPath), 20, 21);
+ assertNextRequestReadNextBlockOfChildren(new Location(pathY), 2, child3);
assertNoMoreRequests();
- assertThat(children.isEmpty(), is(true));
-
- children = graph.getChildrenInRange(0, 20).of(validPath);
- assertThat(numberOfExecutions, is(1));
- assertNextRequestReadRangeOfChildren(new Location(validPath), 0, 20, child1,
child2, child3);
- assertNoMoreRequests();
- assertThat(children, hasItems(child1, child2, child3));
+ assertThat(children, hasItems(child3));
}
@Test
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -77,7 +77,9 @@
import org.jboss.dna.graph.requests.MoveBranchRequest;
import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest;
import org.jboss.dna.graph.requests.ReadBranchRequest;
+import org.jboss.dna.graph.requests.ReadNextBlockOfChildrenRequest;
import org.jboss.dna.graph.requests.ReadNodeRequest;
import org.jboss.dna.graph.requests.ReadPropertyRequest;
import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
@@ -172,8 +174,8 @@
assert ns != null;
// Figure out the next SNS index and index-in-parent for this new child ...
- int nextSnsIndex = 1;
- int nextIndexInParent = 1;
+ int nextSnsIndex = 1; // SNS index is 1-based
+ int nextIndexInParent = 0; // index-in-parent is 0-based
final Path parentPath = actual.location.getPath();
assert parentPath != null;
// Look in the cache for the children of the parent node.
@@ -181,7 +183,7 @@
if (childrenOfParent != null) {
// The cache had the complete list of children for the parent node, which
means
// we know about all of the children and can walk the children to figure
out the next indexes.
- nextIndexInParent = childrenOfParent.size() + 1;
+ nextIndexInParent = childrenOfParent.size();
if (nextIndexInParent > 1) {
// Since we want the last indexes, process the list backwards ...
ListIterator<Location> iter =
childrenOfParent.listIterator(childrenOfParent.size());
@@ -205,7 +207,7 @@
query.setParameter("childName", childLocalName);
try {
Integer result = (Integer)query.getSingleResult();
- nextSnsIndex = result != null ? result + 1 : 1;
+ nextSnsIndex = result != null ? result + 1 : 1; // SNS index is
1-based
} catch (NoResultException e) {
}
@@ -214,7 +216,7 @@
query.setParameter("parentUuid", parentUuidString);
try {
Integer result = (Integer)query.getSingleResult();
- nextIndexInParent = result != null ? result + 1 : 1;
+ nextIndexInParent = result != null ? result + 1 : 0; //
index-in-parent is 0-based
} catch (NoResultException e) {
}
}
@@ -374,6 +376,158 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadBlockOfChildrenRequest request ) {
+ logger.trace(request.toString());
+ Location actualLocation = null;
+ final int startingIndex = request.startingAtIndex();
+ try {
+ Location parentLocation = request.of();
+ ActualLocation actualParent = getActualLocation(parentLocation);
+ actualLocation = actualParent.location;
+
+ Path parentPath = actualParent.location.getPath();
+ assert parentPath != null;
+ LinkedList<Location> cachedChildren =
cache.getAllChildren(parentPath);
+ if (cachedChildren != null) {
+ // The cache has all of the children for the node ...
+ if (startingIndex < cachedChildren.size()) {
+ ListIterator<Location> iter =
cachedChildren.listIterator(startingIndex);
+ for (int i = 0; i != request.count() && iter.hasNext(); ++i)
{
+ Location child = iter.next();
+ request.addChild(child);
+ }
+ }
+ } else {
+ // Nothing was cached, so we need to search the database for the children
...
+ Query query =
entities.createNamedQuery("ChildEntity.findRangeUnderParent");
+ query.setParameter("parentUuidString", actualParent.uuid);
+ query.setParameter("firstIndex", startingIndex);
+ query.setParameter("afterIndex", startingIndex +
request.count());
+ @SuppressWarnings( "unchecked" )
+ List<ChildEntity> children = query.getResultList();
+ for (ChildEntity child : children) {
+ String namespaceUri = child.getChildNamespace().getUri();
+ String localName = child.getChildName();
+ Name childName = nameFactory.create(namespaceUri, localName);
+ int sns = child.getSameNameSiblingIndex();
+ Path childPath = pathFactory.create(parentPath, childName, sns);
+ String childUuidString = child.getId().getChildUuidString();
+ Location childLocation = new Location(childPath,
UUID.fromString(childUuidString));
+ request.addChild(childLocation);
+ }
+ // Do not update the cache, since we don't know all of the children.
+ }
+
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadNextBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadNextBlockOfChildrenRequest request ) {
+ logger.trace(request.toString());
+ Location actualLocation = null;
+ final Location previousSibling = request.startingAfter();
+ final int count = request.count();
+ try {
+ ActualLocation actualSibling = getActualLocation(previousSibling);
+ actualLocation = actualSibling.location;
+ if (!actualLocation.getPath().isRoot()) {
+ // First look in the cache for the children of the parent ...
+ Path parentPath = actualSibling.location.getPath().getParent();
+ assert parentPath != null;
+ LinkedList<Location> cachedChildren =
cache.getAllChildren(parentPath);
+ if (cachedChildren != null) {
+ // The cache has all of the children for the node.
+ // First find the location of the previous sibling ...
+ boolean accumulate = false;
+ int counter = 0;
+ for (Location child : cachedChildren) {
+ if (accumulate) {
+ // We're accumulating children ...
+ request.addChild(child);
+ ++counter;
+ if (counter <= count) continue;
+ break;
+ }
+ // Haven't found the previous sibling yet ...
+ if (child.isSame(previousSibling)) {
+ accumulate = true;
+ }
+ }
+ } else {
+ // The children were not found in the cache, so we have to search the
database.
+ // We don't know the UUID of the parent, so find the previous
sibling and
+ // then get the starting index and the parent UUID ...
+ ChildEntity previousChild = actualSibling.childEntity;
+ if (previousChild == null) {
+ Query query =
entities.createNamedQuery("ChildEntity.findByChildUuid");
+ query.setParameter("childUuidString",
actualSibling.uuid);
+ previousChild = (ChildEntity)query.getSingleResult();
+ }
+ int startingIndex = previousChild.getIndexInParent() + 1;
+ String parentUuid = previousChild.getId().getParentUuidString();
+
+ // Now search the database for the children ...
+ Query query =
entities.createNamedQuery("ChildEntity.findRangeUnderParent");
+ query.setParameter("parentUuidString", parentUuid);
+ query.setParameter("firstIndex", startingIndex);
+ query.setParameter("afterIndex", startingIndex +
request.count());
+ @SuppressWarnings( "unchecked" )
+ List<ChildEntity> children = query.getResultList();
+ LinkedList<Location> allChildren = null;
+ if (startingIndex == 1 && children.size() <
request.count()) {
+ // The previous child was the first sibling, and we got fewer
children than
+ // the max count. This means we know all of the children, so
accumulate the locations
+ // so they can be cached ...
+ allChildren = new LinkedList<Location>();
+ allChildren.add(actualSibling.location);
+ }
+ for (ChildEntity child : children) {
+ String namespaceUri = child.getChildNamespace().getUri();
+ String localName = child.getChildName();
+ Name childName = nameFactory.create(namespaceUri, localName);
+ int sns = child.getSameNameSiblingIndex();
+ Path childPath = pathFactory.create(parentPath, childName, sns);
+ String childUuidString = child.getId().getChildUuidString();
+ Location childLocation = new Location(childPath,
UUID.fromString(childUuidString));
+ request.addChild(childLocation);
+ if (allChildren != null) {
+ // We're going to cache the results, so add this child
...
+ allChildren.add(childLocation);
+ }
+ }
+
+ if (allChildren != null) {
+ cache.setAllChildren(parentPath, allChildren);
+ }
+ }
+ }
+
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null)
request.setActualLocationOfStartingAfterNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllPropertiesRequest)
*/
@Override
@@ -882,12 +1036,14 @@
String nodeUuidString = uuidString;
LinkedList<Path.Segment> segments = new
LinkedList<Path.Segment>();
ChildEntity entity = null;
+ ChildEntity originalEntity = null;
while (uuidString != null &&
!uuidString.equals(this.rootNodeUuidString)) {
Query query =
entities.createNamedQuery("ChildEntity.findByChildUuid");
query.setParameter("childUuidString", uuidString);
try {
// Find the parent of the UUID ...
entity = (ChildEntity)query.getSingleResult();
+ if (originalEntity == null) originalEntity = entity;
String localName = entity.getChildName();
String uri = entity.getChildNamespace().getUri();
int sns = entity.getSameNameSiblingIndex();
@@ -901,7 +1057,7 @@
Path fullPath = pathFactory.createAbsolutePath(segments);
Location newLocation = new Location(fullPath, uuidProperty);
cache.addNewNode(newLocation);
- return new ActualLocation(newLocation, nodeUuidString, entity);
+ return new ActualLocation(newLocation, nodeUuidString, originalEntity);
}
// There is no UUID, so look for a path ...
@@ -917,6 +1073,18 @@
cache.addNewNode(newLocation);
return new ActualLocation(newLocation, rootNodeUuidString, null);
}
+ // See if the parent location is known in the cache ...
+ Location cachedParent = cache.getLocationFor(path.getParent());
+ if (cachedParent != null) {
+ // We know the UUID of the parent, so we can find the child a little faster
...
+ ChildEntity child = findByPathSegment(cachedParent.getUuid().toString(),
path.getLastSegment());
+ uuidString = child.getId().getChildUuidString();
+ Location newLocation = original.with(UUID.fromString(uuidString));
+ cache.addNewNode(newLocation);
+ return new ActualLocation(newLocation, uuidString, child);
+ }
+
+ // We couldn't find the parent, so we need to search by path ...
String parentUuid = this.rootNodeUuidString;
ChildEntity child = null;
for (Path.Segment segment : path) {
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -64,6 +64,7 @@
@Id
private ChildId id;
+ /** The zero-based index */
@Column( name = "CHILD_INDEX", nullable = false, unique = false )
private int indexInParent;
@@ -118,7 +119,9 @@
}
/**
- * @return indexInParent
+ * Get the zero-based index of this child within the parent's list of children
+ *
+ * @return the zero-based index of this child
*/
public int getIndexInParent() {
return indexInParent;
Modified:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-12-12
00:05:32 UTC (rev 684)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-12-12
23:00:10 UTC (rev 685)
@@ -29,6 +29,7 @@
import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
@@ -58,7 +59,7 @@
*
* @author Randall Hauch
*/
-public abstract class JpaConnectionTest {
+public class JpaConnectionTest {
private ExecutionContext context;
private JpaConnection connection;
@@ -91,11 +92,6 @@
Ejb3Configuration configurator = new Ejb3Configuration();
model.configure(configurator);
configureDatabaseProperties(configurator);
- // configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
- // configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
- // configurator.setProperty("hibernate.connection.username",
"sa");
- // configurator.setProperty("hibernate.connection.password",
"");
- // configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
configurator.setProperty("hibernate.show_sql", "false");
configurator.setProperty("hibernate.format_sql", "true");
configurator.setProperty("hibernate.use_sql_comments",
"true");
@@ -111,7 +107,13 @@
graph = Graph.create(connection, context);
}
- protected abstract void configureDatabaseProperties( Ejb3Configuration configurator
);
+ protected void configureDatabaseProperties( Ejb3Configuration configurator ) {
+ configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username",
"sa");
+ configurator.setProperty("hibernate.connection.password",
"");
+ configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
+ }
@After
public void afterEach() throws Exception {
@@ -686,6 +688,94 @@
assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
}
+ @Test
+ public void shouldReadRangeOfChildren() {
+ // Create a shallow tree with many children under one node ...
+ // /
+ // /node1
+ // /node1/node1
+ // /node1/node2
+ // ...
+ // /node1/node10
+ // /node1/secondBranch1
+ // ...
+
+ graph.batch().create("/node1").with("prop1",
"value1").and("prop2", "value2").execute();
+ numPropsOnEach = 3;
+ createTree("/node1", 10, 2, numPropsOnEach, null, true, false);
+
+ // Verify that the children were created ...
+ List<Location> allChildren = graph.getChildren().of("/node1");
+ assertThat(allChildren, hasChildren(child("node1"),
+ child("node2"),
+ child("node3"),
+ child("node4"),
+ child("node5"),
+ child("node6"),
+ child("node7"),
+ child("node8"),
+ child("node9"),
+ child("node10")));
+
+ // Now test reading children in various ranges ...
+ List<Location> children =
graph.getChildren().inBlockOf(4).startingAt(4).under("/node1");
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7"), child("node8")));
+
+ children =
graph.getChildren().inBlockOf(3).startingAt(4).under("/node1");
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7")));
+
+ children =
graph.getChildren().inBlockOf(10).startingAt(7).under("/node1");
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node8"),
child("node9"), child("node10")));
+ }
+
+ @Test
+ public void shouldReadNextBlockOfChildren() {
+ // Create a shallow tree with many children under one node ...
+ // /
+ // /node1
+ // /node1/node1
+ // /node1/node2
+ // ...
+ // /node1/node10
+ // /node1/secondBranch1
+ // ...
+
+ graph.batch().create("/node1").with("prop1",
"value1").and("prop2", "value2").execute();
+ numPropsOnEach = 3;
+ createTree("/node1", 10, 2, numPropsOnEach, null, true, false);
+
+ // Verify that the children were created ...
+ List<Location> allChildren = graph.getChildren().of("/node1");
+ assertThat(allChildren, hasChildren(child("node1"),
+ child("node2"),
+ child("node3"),
+ child("node4"),
+ child("node5"),
+ child("node6"),
+ child("node7"),
+ child("node8"),
+ child("node9"),
+ child("node10")));
+
+ // Now test reading children in various ranges ...
+ Location node4 = allChildren.get(3);
+ List<Location> children =
graph.getChildren().inBlockOf(4).startingAfter(node4);
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7"), child("node8")));
+
+ children = graph.getChildren().inBlockOf(3).startingAfter(node4);
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7")));
+
+ Location node7 = allChildren.get(6);
+ children = graph.getChildren().inBlockOf(10).startingAfter(node7);
+ assertThat(children, is(notNullValue()));
+ assertThat(children, hasChildren(child("node8"),
child("node9"), child("node10")));
+ }
+
protected int createTree( String initialPath,
int numberPerDepth,
int depth,