Index: src/main/java/org/jboss/dna/graph/requests/CompositeRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/CompositeRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/CompositeRequest.java	(revision 0)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A request that wraps multiple other requests.
+ * 
+ * @author Randall Hauch
+ */
+public class CompositeRequest extends Request implements Iterable<Request> {
+
+    /**
+     * Return a request that either wraps multiple requests, or the single request if only one is supplied.
+     * 
+     * @param requests the requests to wrap
+     * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if
+     *         there are no request
+     */
+    public static Request with( Request... requests ) {
+        if (requests == null) return null;
+        if (requests.length == 1) {
+            return requests[0];
+        }
+        return new CompositeRequest(requests);
+    }
+
+    /**
+     * Add requests to the supplied composite request.
+     * 
+     * @param composite the composite request to which the requests are to be added
+     * @param requests the requests to wrap
+     * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if
+     *         there are no request
+     * @throws IllegalArgumentException if the composite request is null
+     */
+    public static CompositeRequest add( CompositeRequest composite,
+                                        Request... requests ) {
+        CheckArg.isNotNull(composite, "composite");
+        if (requests == null || requests.length == 0) return composite;
+        List<Request> list = new ArrayList<Request>(requests.length + composite.size());
+        if (composite.size() != 0) list.addAll(composite.getRequests());
+        for (Request request : requests) {
+            if (request != null) list.add(request);
+        }
+        return new CompositeRequest(list);
+    }
+
+    private final List<Request> requests;
+
+    /**
+     * Create a composite request from the supplied array of requests.
+     * 
+     * @param requests the requests; may not be null
+     */
+    protected CompositeRequest( Request... requests ) {
+        this.requests = Collections.unmodifiableList(Arrays.asList(requests));
+    }
+
+    /**
+     * Create a composite request from the supplied list of requests.
+     * 
+     * @param requests the modifiable list of requests; may not be null
+     */
+    private CompositeRequest( List<Request> requests ) {
+        this.requests = Collections.unmodifiableList(requests);
+    }
+
+    /**
+     * Return the unmodifiable requests contained in this composite request.
+     * 
+     * @return requests
+     */
+    public List<Request> getRequests() {
+        return requests;
+    }
+
+    /**
+     * Get the number of requests.
+     * 
+     * @return the number of requests
+     */
+    public int size() {
+        return requests.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Request> iterator() {
+        return requests.iterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj instanceof CompositeRequest) {
+            CompositeRequest that = (CompositeRequest)obj;
+            if (this.size() != that.size()) return false;
+            Iterator<Request> thisIter = this.iterator();
+            Iterator<Request> thatIter = that.iterator();
+            while (thisIter.hasNext()) {
+                Request thisRequest = thisIter.next();
+                Request thatRequest = thatIter.next();
+                if (thisRequest == null) {
+                    if (thatRequest != null) return false;
+                } else {
+                    if (!thisRequest.equals(thatRequest)) return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/CopyBranchRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/CopyBranchRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/CopyBranchRequest.java	(revision 0)
@@ -0,0 +1,94 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * Request that a branch be copied from one location into another.
+ * 
+ * @author Randall Hauch
+ */
+public class CopyBranchRequest extends Request {
+
+    private final Location from;
+    private final Location into;
+
+    /**
+     * Create a request to copy a branch to another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be copied
+     * @param into the location of the existing node into which the copy should be placed
+     * @throws IllegalArgumentException if <code>from</code> or <code>into</code> are null
+     */
+    public CopyBranchRequest( Location from,
+                              Location into ) {
+        CheckArg.isNotNull(from, "from");
+        CheckArg.isNotNull(into, "into");
+        this.from = from;
+        this.into = into;
+    }
+
+    /**
+     * Get the location defining the top of the branch to be copied
+     * 
+     * @return the from location; never null
+     */
+    public Location from() {
+        return from;
+    }
+
+    /**
+     * Get the location defining the parent where the new copy is to be placed
+     * 
+     * @return the to location; never null
+     */
+    public Location into() {
+        return into;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            CopyBranchRequest that = (CopyBranchRequest)obj;
+            if (!this.from().equals(that.from())) return false;
+            if (!this.into().equals(that.into())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "copy branch " + from() + " into " + into();
+    }
+}
Index: src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java	(revision 0)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to create the node at the specified location. This command will create the node and set the initial properties.
+ * 
+ * @author Randall Hauch
+ */
+public class CreateNodeRequest extends Request implements Iterable<Property> {
+
+    private final Location at;
+    private final List<Property> properties;
+
+    /**
+     * Create a request to create a node with the given properties at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param properties the properties of the new node, which should not include the location's
+     *        {@link Location#getIdProperties() identification properties}
+     * @throws IllegalArgumentException if the location is null
+     */
+    public CreateNodeRequest( Location at,
+                              Property... properties ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+        int number = properties.length + (at.hasIdProperties() ? at.getIdProperties().size() : 0);
+        List<Property> props = new ArrayList<Property>(number);
+        for (Property property : properties) {
+            if (property != null) props.add(property);
+        }
+        // Add in the location properties ...
+        if (at.hasIdProperties()) {
+            for (Property property : at.getIdProperties()) {
+                if (property != null) props.add(property);
+            }
+        }
+        this.properties = Collections.unmodifiableList(props);
+    }
+
+    /**
+     * Create a request to create a node with the given properties at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param properties the properties of the new node, which should not include the location's
+     *        {@link Location#getIdProperties() identification properties}
+     * @throws IllegalArgumentException if the location is null
+     */
+    public CreateNodeRequest( Location at,
+                              Iterable<Property> properties ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+        List<Property> props = new LinkedList<Property>();
+        for (Property property : properties) {
+            if (property != null) props.add(property);
+        }
+        // Add in the location properties ...
+        if (at.hasIdProperties()) {
+            for (Property property : at.getIdProperties()) {
+                if (property != null) props.add(property);
+            }
+        }
+        this.properties = Collections.unmodifiableList(props);
+    }
+
+    /**
+     * Create a request to create a node with the given properties at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param properties the properties of the new node, which should not include the location's
+     *        {@link Location#getIdProperties() identification properties}
+     * @throws IllegalArgumentException if the location is null
+     */
+    public CreateNodeRequest( Location at,
+                              Iterator<Property> properties ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+        List<Property> props = new LinkedList<Property>();
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            if (property != null) props.add(property);
+        }
+        // Add in the location properties ...
+        if (at.hasIdProperties()) {
+            for (Property property : at.getIdProperties()) {
+                if (property != null) props.add(property);
+            }
+        }
+        this.properties = Collections.unmodifiableList(props);
+    }
+
+    /**
+     * Get the location defining the node that is to be created.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Property> iterator() {
+        return this.properties.iterator();
+    }
+
+    /**
+     * Get the properties for the node. If the node's {@link #at() location} has identification properties, the resulting
+     * properties will include the {@link Location#getIdProperties() identification properties}.
+     * 
+     * @return the collection of properties; never null
+     */
+    public Collection<Property> properties() {
+        return properties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            CreateNodeRequest that = (CreateNodeRequest)obj;
+            if (!this.at().equals(that.at())) return false;
+            if (!this.properties().equals(that.properties())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "create node at " + at() + " with properties " + StringUtil.readableString(properties());
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/DeleteBranchRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/DeleteBranchRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/DeleteBranchRequest.java	(revision 0)
@@ -0,0 +1,79 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * Request that a branch be deleted.
+ * 
+ * @author Randall Hauch
+ */
+public class DeleteBranchRequest extends Request {
+
+    private final Location at;
+
+    /**
+     * Create a request to delete a branch.
+     * 
+     * @param at the location of the top node in the existing branch that is to be deleted
+     * @throws IllegalArgumentException if the location is null
+     */
+    public DeleteBranchRequest( Location at ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+    }
+
+    /**
+     * Get the location defining the top of the branch to be deleted
+     * 
+     * @return the location of the branch; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            DeleteBranchRequest that = (DeleteBranchRequest)obj;
+            if (!this.at().equals(that.at())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "delete branch " + at();
+    }
+}
Index: src/main/java/org/jboss/dna/graph/requests/Location.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/Location.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/Location.java	(revision 0)
@@ -0,0 +1,276 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.basic.BasicSingleValueProperty;
+
+/**
+ * The location of a node, as specified by either its path, UUID, and/or identification properties.
+ * 
+ * @author Randall Hauch
+ */
+@Immutable
+public class Location {
+
+    private final Path path;
+    private final List<Property> idProperties;
+
+    /**
+     * Create a location defined by a path.
+     * 
+     * @param path the path
+     * @throws IllegalArgumentException if <code>path</code> is null
+     */
+    public Location( Path path ) {
+        CheckArg.isNotNull(path, "path");
+        this.path = path;
+        this.idProperties = null;
+    }
+
+    /**
+     * Create a location defined by a UUID.
+     * 
+     * @param uuid the UUID
+     * @throws IllegalArgumentException if <code>uuid</code> is null
+     */
+    public Location( UUID uuid ) {
+        CheckArg.isNotNull(uuid, "uuid");
+        this.path = null;
+        Property idProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
+        this.idProperties = Collections.singletonList(idProperty);
+    }
+
+    /**
+     * Create a location defined by a path and an UUID.
+     * 
+     * @param path the path
+     * @param uuid the UUID
+     * @throws IllegalArgumentException if <code>uuid</code> is null
+     */
+    public Location( Path path,
+                     UUID uuid ) {
+        CheckArg.isNotNull(uuid, "uuid");
+        CheckArg.isNotNull(path, "path");
+        this.path = path;
+        Property idProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
+        this.idProperties = Collections.singletonList(idProperty);
+    }
+
+    /**
+     * Create a location defined by a path and a single identification property.
+     * 
+     * @param path the path
+     * @param idProperty the identification property
+     * @throws IllegalArgumentException if <code>path</code> or <code>idProperty</code> is null
+     */
+    public Location( Path path,
+                     Property idProperty ) {
+        CheckArg.isNotNull(path, "path");
+        CheckArg.isNotNull(idProperty, "idProperty");
+        this.path = path;
+        this.idProperties = Collections.singletonList(idProperty);
+    }
+
+    /**
+     * Create a location defined by a path and multiple identification properties.
+     * 
+     * @param path the path
+     * @param firstIdProperty the first identification property
+     * @param remainingIdProperties the remaining identification property
+     * @throws IllegalArgumentException if any of the arguments are null
+     */
+    public Location( Path path,
+                     Property firstIdProperty,
+                     Property... remainingIdProperties ) {
+        CheckArg.isNotNull(path, "path");
+        CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
+        CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
+        this.path = path;
+        List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
+        idProperties.add(firstIdProperty);
+        for (Property property : remainingIdProperties) {
+            idProperties.add(property);
+        }
+        this.idProperties = Collections.unmodifiableList(idProperties);
+    }
+
+    /**
+     * Create a location defined by a single identification property.
+     * 
+     * @param idProperty the identification property
+     * @throws IllegalArgumentException if <code>idProperty</code> is null
+     */
+    public Location( Property idProperty ) {
+        CheckArg.isNotNull(idProperty, "idProperty");
+        this.path = null;
+        this.idProperties = Collections.singletonList(idProperty);
+    }
+
+    /**
+     * Create a location defined by multiple identification properties.
+     * 
+     * @param firstIdProperty the first identification property
+     * @param remainingIdProperties the remaining identification property
+     * @throws IllegalArgumentException if any of the arguments are null
+     */
+    public Location( Property firstIdProperty,
+                     Property... remainingIdProperties ) {
+        CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
+        CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
+        this.path = null;
+        List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
+        idProperties.add(firstIdProperty);
+        for (Property property : remainingIdProperties) {
+            idProperties.add(property);
+        }
+        this.idProperties = Collections.unmodifiableList(idProperties);
+    }
+
+    /**
+     * Create a location defined by multiple identification properties.
+     * 
+     * @param idProperties the identification properties
+     * @throws IllegalArgumentException if <code>idProperties</code> is null or empty
+     */
+    public Location( List<Property> idProperties ) {
+        CheckArg.isNotEmpty(idProperties, "idProperties");
+        this.path = null;
+        this.idProperties = idProperties;
+    }
+
+    /**
+     * Create a location defined by a path and multiple identification properties.
+     * 
+     * @param path the path
+     * @param idProperties the identification properties
+     * @throws IllegalArgumentException if <code>path</code> or <code>idProperties</code> is null, or if <code>idProperties</code>
+     *         is empty
+     */
+    public Location( Path path,
+                     List<Property> idProperties ) {
+        CheckArg.isNotNull(path, "path");
+        CheckArg.isNotEmpty(idProperties, "idProperties");
+        this.path = path;
+        this.idProperties = idProperties;
+    }
+
+    /**
+     * Get the path that (at least in part) defines this location.
+     * 
+     * @return the path, or null if this location is not defined with a path
+     */
+    public Path getPath() {
+        return path;
+    }
+
+    /**
+     * Return whether this location is defined (at least in part) by a path.
+     * 
+     * @return true if a {@link #getPath() path} helps define this location
+     */
+    public boolean hasPath() {
+        return path != null;
+    }
+
+    /**
+     * Get the identification properties that (at least in part) define this location.
+     * 
+     * @return the identification properties, or null if this location is not defined with identification properties
+     */
+    public List<Property> getIdProperties() {
+        return idProperties;
+    }
+
+    /**
+     * Return whether this location is defined (at least in part) with identification properties.
+     * 
+     * @return true if a {@link #getIdProperties() identification properties} help define this location
+     */
+    public boolean hasIdProperties() {
+        return idProperties != null && idProperties.size() != 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return HashCode.compute(path, idProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj instanceof Location) {
+            Location that = (Location)obj;
+            if (this.hasPath()) {
+                if (!this.getPath().equals(that.getPath())) return false;
+            } else {
+                if (that.hasPath()) return false;
+            }
+            if (this.hasIdProperties()) {
+                if (!this.getIdProperties().equals(that.getIdProperties())) return false;
+            } else {
+                if (that.hasIdProperties()) return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (this.hasPath()) {
+            if (this.hasIdProperties()) sb.append("[ ");
+            sb.append(this.getPath());
+            if (this.hasIdProperties()) sb.append(" && ");
+        }
+        if (this.hasIdProperties()) {
+            sb.append(StringUtil.readableString(this.getIdProperties()));
+            if (this.hasPath()) sb.append(" ]");
+        }
+        return sb.toString();
+    }
+}
Index: src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java	(revision 0)
@@ -0,0 +1,94 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * Request that a branch be moved from one location into another.
+ * 
+ * @author Randall Hauch
+ */
+public class MoveBranchRequest extends Request {
+
+    private final Location from;
+    private final Location into;
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @throws IllegalArgumentException if <code>from</code> or <code>into</code> are null
+     */
+    public MoveBranchRequest( Location from,
+                              Location into ) {
+        CheckArg.isNotNull(from, "from");
+        CheckArg.isNotNull(into, "into");
+        this.from = from;
+        this.into = into;
+    }
+
+    /**
+     * Get the location defining the top of the branch to be moved
+     * 
+     * @return the from location; never null
+     */
+    public Location from() {
+        return from;
+    }
+
+    /**
+     * Get the location defining the parent where the branch is to be placed
+     * 
+     * @return the to location; never null
+     */
+    public Location into() {
+        return into;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            MoveBranchRequest that = (MoveBranchRequest)obj;
+            if (!this.from().equals(that.from())) return false;
+            if (!this.into().equals(that.into())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "move branch " + from() + " into " + into();
+    }
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadAllChildrenRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadAllChildrenRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadAllChildrenRequest.java	(revision 0)
@@ -0,0 +1,124 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to read all of the children of a node at a specific location.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadAllChildrenRequest extends Request {
+
+    private final Location of;
+    private final List<Location> children = new LinkedList<Location>();
+
+    /**
+     * Create a request to read the children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @throws IllegalArgumentException if the location is null
+     */
+    public ReadAllChildrenRequest( Location of ) {
+        CheckArg.isNotNull(of, "of");
+        this.of = of;
+    }
+
+    /**
+     * 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 children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
+     * represented by a location.
+     * 
+     * @return the children that were read; never null
+     */
+    public List<Location> getChildren() {
+        return children;
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification properties.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param firstIdProperty the first identification property of the child that was just read
+     * @param remainingIdProperties the remaining identification properties of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property firstIdProperty,
+                          Property... remainingIdProperties ) {
+        Location child = new Location(pathToChild, firstIdProperty, remainingIdProperties);
+        this.children.add(child);
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification property.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param idProperty the identification property of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property idProperty ) {
+        Location child = new Location(pathToChild, idProperty);
+        this.children.add(child);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadAllChildrenRequest that = (ReadAllChildrenRequest)obj;
+            if (!this.of().equals(that.of())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "read children of " + of();
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadBlockOfChildrenRequest.java	(revision 0)
@@ -0,0 +1,182 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.common.text.Inflector;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to read a block of the children of a node, where the block is dictated by the {@link #startingAt() starting index} 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.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadBlockOfChildrenRequest extends Request {
+
+    private final Location of;
+    private final List<Location> children = new LinkedList<Location>();
+    private final int startingAt;
+    private final int count;
+
+    /**
+     * 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.
+     * 
+     * @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 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.
+     */
+    public ReadBlockOfChildrenRequest( Location of,
+                                       int startingIndex,
+                                       int count ) {
+        CheckArg.isNotNull(of, "of");
+        CheckArg.isNonNegative(startingIndex, "startingIndex");
+        CheckArg.isPositive(count, "count");
+        this.of = of;
+        this.startingAt = startingIndex;
+        this.count = count;
+    }
+
+    /**
+     * 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
+     * @see #startingAt()
+     * @see #endingBefore()
+     */
+    public int count() {
+        return this.count;
+    }
+
+    /**
+     * 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()}
+     * @see #endingBefore()
+     * @see #count()
+     */
+    public int startingAt() {
+        return this.startingAt;
+    }
+
+    /**
+     * Get the index past the last child that is to be included in the block. This index corresponds to the index of all children
+     * in the list, not the {@link Path.Segment#getIndex() same-name-sibiling index}.
+     * 
+     * @return the index just past the last child included in the block; always positive and always greater than
+     *         {@link #startingAt()}.
+     * @see #startingAt()
+     * @see #count()
+     */
+    public int endingBefore() {
+        return this.startingAt + this.count;
+    }
+
+    /**
+     * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
+     * represented by a location.
+     * 
+     * @return the children that were read; never null
+     */
+    public List<Location> getChildren() {
+        return children;
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification properties.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param firstIdProperty the first identification property of the child that was just read
+     * @param remainingIdProperties the remaining identification properties of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property firstIdProperty,
+                          Property... remainingIdProperties ) {
+        Location child = new Location(pathToChild, firstIdProperty, remainingIdProperties);
+        this.children.add(child);
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification property.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param idProperty the identification property of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property idProperty ) {
+        Location child = new Location(pathToChild, idProperty);
+        this.children.add(child);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadBlockOfChildrenRequest that = (ReadBlockOfChildrenRequest)obj;
+            if (!this.of().equals(that.of())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        Inflector inflector = Inflector.getInstance();
+        if (count() == 1) {
+            return "read " + inflector.ordinalize(startingAt()) + " thru " + inflector.ordinalize(endingBefore() - 1)
+                   + " children of " + of();
+        }
+        return "read " + inflector.ordinalize(startingAt()) + " child of " + of();
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadBranchRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadBranchRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadBranchRequest.java	(revision 0)
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * Request to read the properties and children of the nodes in the branch at the supplied location.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadBranchRequest extends Request {
+
+    public static final int DEFAULT_MAXIMUM_DEPTH = 2;
+
+    private final Location at;
+    private final int maxDepth;
+
+    /**
+     * Create a request to read the branch at the supplied location, to a maximum depth of 2.
+     * 
+     * @param at the location of the branch
+     * @throws IllegalArgumentException if the location is null
+     */
+    public ReadBranchRequest( Location at ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+        this.maxDepth = DEFAULT_MAXIMUM_DEPTH;
+    }
+
+    /**
+     * Create a request to read the branch (of given depth) at the supplied location.
+     * 
+     * @param at the location of the branch
+     * @param maxDepth the maximum depth to read
+     * @throws IllegalArgumentException if the location is null or if the maximum depth is not positive
+     */
+    public ReadBranchRequest( Location at,
+                              int maxDepth ) {
+        CheckArg.isNotNull(at, "at");
+        CheckArg.isPositive(maxDepth, "maxDepth");
+        this.at = at;
+        this.maxDepth = maxDepth;
+    }
+
+    /**
+     * Get the location defining the top of the branch to be deleted
+     * 
+     * @return the location of the branch; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * Get the maximum depth of the branch that is to be read.
+     * 
+     * @return the maximum depth; always positive
+     */
+    public int maximumDepth() {
+        return maxDepth;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadBranchRequest that = (ReadBranchRequest)obj;
+            if (!this.at().equals(that.at())) return false;
+            if (this.maximumDepth() != that.maximumDepth()) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "read branch " + at() + " to depth " + maximumDepth();
+    }
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadNodeRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadNodeRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadNodeRequest.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to read the properties and the number of children of the node at the specifed location.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadNodeRequest extends Request {
+
+    /**
+     * The value of the {@link #getNumberOfChildren()} if the number of children has not yet been determined (usually because the
+     * request has not yet been processed).
+     */
+    public static final int UNKNOWN_NUMBER_OF_CHILDREN = -1;
+
+    private final Location at;
+    private final Map<Name, Property> properties = new HashMap<Name, Property>();
+    private int numberOfChildren = UNKNOWN_NUMBER_OF_CHILDREN;
+
+    /**
+     * Create a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @throws IllegalArgumentException if the location is null
+     */
+    public ReadNodeRequest( Location at ) {
+        CheckArg.isNotNull(at, "at");
+        this.at = at;
+    }
+
+    /**
+     * Get the location defining the node that is to be read.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * Get the properties that were read from the {@link RepositoryConnection}.
+     * 
+     * @return the properties, as a map of property name to property; never null
+     */
+    public Map<Name, Property> getPropertiesByName() {
+        return properties;
+    }
+
+    /**
+     * Get the properties that were read from the {@link RepositoryConnection}.
+     * 
+     * @return the collection of properties; never null
+     */
+    public Collection<Property> getProperties() {
+        return properties.values();
+    }
+
+    /**
+     * Add a property that was read from the {@link RepositoryConnection}
+     * 
+     * @param property the property that was read
+     * @return the previous property that had the same name, or null if there was no previously-recorded property with the same
+     *         name
+     * @throws IllegalArgumentException if the property is null
+     */
+    public Property addProperty( Property property ) {
+        return this.properties.put(property.getName(), property);
+    }
+
+    /**
+     * Add a property that was read from the {@link RepositoryConnection}
+     * 
+     * @param properties the properties that were read
+     * @throws IllegalArgumentException if the property is null
+     */
+    public void addProperties( Property... properties ) {
+        for (Property property : properties) {
+            this.properties.put(property.getName(), property);
+        }
+    }
+
+    /**
+     * Get the number of children that were read from the {@link RepositoryConnection}.
+     * 
+     * @return the number of children, or {@link #UNKNOWN_NUMBER_OF_CHILDREN} if the children have not yet been read.
+     */
+    public int getNumberOfChildren() {
+        return numberOfChildren;
+    }
+
+    /**
+     * Set the number of children that were read from the {@link RepositoryConnection}.
+     * 
+     * @param numberOfChildren the number of children for the node
+     * @throws IllegalArgumentException if the number of children is negative
+     */
+    public void setNumberOfChildren( int numberOfChildren ) {
+        CheckArg.isNonNegative(numberOfChildren, "numberOfChildren");
+        this.numberOfChildren = numberOfChildren;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadNodeRequest that = (ReadNodeRequest)obj;
+            if (!this.at().equals(that.at())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "read node at " + at();
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadPropertyRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadPropertyRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadPropertyRequest.java	(revision 0)
@@ -0,0 +1,120 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to read a single property on the node at the specified location.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadPropertyRequest extends Request {
+
+    private final Location on;
+    private final Name propertyName;
+    private Property property;
+
+    /**
+     * Create a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param propertyName the name of the property to read
+     * @throws IllegalArgumentException if the location or property name are null
+     */
+    public ReadPropertyRequest( Location on,
+                                Name propertyName ) {
+        CheckArg.isNotNull(on, "on");
+        CheckArg.isNotNull(propertyName, "propertyName");
+        this.on = on;
+        this.propertyName = propertyName;
+    }
+
+    /**
+     * Get the location defining the node that is to be read.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location on() {
+        return on;
+    }
+
+    /**
+     * Get the name of the property that is to be read
+     * 
+     * @return the property name; never null
+     */
+    public Name named() {
+        return propertyName;
+    }
+
+    /**
+     * Get the property that was read.
+     * 
+     * @return the property, or null if the property was not read or did not exist on the node
+     */
+    public Property getProperty() {
+        return property;
+    }
+
+    /**
+     * Set the property on the node as read from the {@link RepositoryConnection}
+     * 
+     * @param property the property that was read
+     * @throws IllegalArgumentException if the property's name does not match the {@link #named() name of the property} that was
+     *         to be read
+     */
+    public void setProperty( Property property ) {
+        if (property != null) CheckArg.isEquals(property.getName(), "property's name", named(), "property name");
+        this.property = property;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadPropertyRequest that = (ReadPropertyRequest)obj;
+            if (!this.on().equals(that.on())) return false;
+            if (!this.named().equals(that.named())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "read " + named() + " property at " + on();
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/ReadSomeChildrenRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/ReadSomeChildrenRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/ReadSomeChildrenRequest.java	(revision 0)
@@ -0,0 +1,124 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to read all of the children of a node at a specific location.
+ * 
+ * @author Randall Hauch
+ */
+public class ReadSomeChildrenRequest extends Request {
+
+    private final Location of;
+    private final List<Location> children = new LinkedList<Location>();
+
+    /**
+     * Create a request to read the children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @throws IllegalArgumentException if the location is null
+     */
+    public ReadSomeChildrenRequest( Location of ) {
+        CheckArg.isNotNull(of, "of");
+        this.of = of;
+    }
+
+    /**
+     * Get the location defining the top of the branch to be deleted
+     * 
+     * @return the location of the branch; never null
+     */
+    public Location of() {
+        return of;
+    }
+
+    /**
+     * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
+     * represented by a location.
+     * 
+     * @return the children that were read; never null
+     */
+    public List<Location> getChildren() {
+        return children;
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification properties.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param firstIdProperty the first identification property of the child that was just read
+     * @param remainingIdProperties the remaining identification properties of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property firstIdProperty,
+                          Property... remainingIdProperties ) {
+        Location child = new Location(pathToChild, firstIdProperty, remainingIdProperties);
+        this.children.add(child);
+    }
+
+    /**
+     * Add to the list of children that has been read the child with the given path and identification property.
+     * 
+     * @param pathToChild the path of the child that was just read
+     * @param idProperty the identification property of the child that was just read
+     * @throws IllegalArgumentException if the path or identification properties are null
+     */
+    public void addChild( Path pathToChild,
+                          Property idProperty ) {
+        Location child = new Location(pathToChild, idProperty);
+        this.children.add(child);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            ReadSomeChildrenRequest that = (ReadSomeChildrenRequest)obj;
+            if (!this.of().equals(that.of())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "read children of " + of();
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/RemovePropertiesRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/RemovePropertiesRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/RemovePropertiesRequest.java	(revision 0)
@@ -0,0 +1,154 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.properties.Name;
+
+/**
+ * Request to remove properties from the node at the specified location.
+ * 
+ * @author Randall Hauch
+ */
+public class RemovePropertiesRequest extends Request implements Iterable<Name> {
+
+    private final Location from;
+    private final Set<Name> propertyNames;
+
+    /**
+     * Create a request to remove the properties with the given names from the node at the supplied location.
+     * 
+     * @param from the location of the node to be read
+     * @param propertyNames the names of the properties to be removed from the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
+     */
+    public RemovePropertiesRequest( Location from,
+                                    Name... propertyNames ) {
+        CheckArg.isNotNull(from, "from");
+        CheckArg.isNotEmpty(propertyNames, "propertyNames");
+        this.from = from;
+        Set<Name> names = new HashSet<Name>();
+        for (Name name : propertyNames) {
+            if (name != null) names.add(name);
+        }
+        this.propertyNames = Collections.unmodifiableSet(names);
+    }
+
+    /**
+     * Create a request to remove the properties with the given names from the node at the supplied location.
+     * 
+     * @param from the location of the node to be read
+     * @param propertyNames the names of the properties to be removed from the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
+     */
+    public RemovePropertiesRequest( Location from,
+                                    Iterable<Name> propertyNames ) {
+        CheckArg.isNotNull(from, "from");
+        this.from = from;
+        Set<Name> names = new HashSet<Name>();
+        for (Name name : propertyNames) {
+            if (name != null) names.add(name);
+        }
+        this.propertyNames = Collections.unmodifiableSet(names);
+        CheckArg.isNotEmpty(this.propertyNames, "propertyNames");
+    }
+
+    /**
+     * Create a request to remove the properties with the given names from the node at the supplied location.
+     * 
+     * @param from the location of the node to be read
+     * @param propertyNames the names of the properties to be removed from the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
+     */
+    public RemovePropertiesRequest( Location from,
+                                    Iterator<Name> propertyNames ) {
+        CheckArg.isNotNull(from, "from");
+        this.from = from;
+        Set<Name> names = new HashSet<Name>();
+        while (propertyNames.hasNext()) {
+            Name name = propertyNames.next();
+            if (name != null) names.add(name);
+        }
+        this.propertyNames = Collections.unmodifiableSet(names);
+        CheckArg.isNotEmpty(this.propertyNames, "propertyNames");
+    }
+
+    /**
+     * Get the location defining the node from which the properties are to be removed.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location from() {
+        return from;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Name> iterator() {
+        return this.propertyNames.iterator();
+    }
+
+    /**
+     * Get the names of the properties that are to be removed from the node.
+     * 
+     * @return the collection of property names; never null and never empty
+     */
+    public Collection<Name> propertyNames() {
+        return propertyNames;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            RemovePropertiesRequest that = (RemovePropertiesRequest)obj;
+            if (!this.from().equals(that.from())) return false;
+            if (!this.propertyNames().equals(that.propertyNames())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "remove from " + from() + " properties named " + StringUtil.readableString(propertyNames());
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/Request.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/Request.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/Request.java	(revision 0)
@@ -0,0 +1,62 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+
+/**
+ * The abstract base class for all classes representing requests to be executed against a {@link RepositoryConnection}.
+ * 
+ * @author Randall Hauch
+ */
+public abstract class Request {
+
+    private Throwable error;
+
+    /**
+     * Set the error for this request.
+     * 
+     * @param error the error to be associated with this request, or null if this request is to have no error
+     */
+    public void setError( Throwable error ) {
+        this.error = error;
+    }
+
+    /**
+     * Return whether there is an error associated with this request
+     * 
+     * @return true if there is an error, or false otherwise
+     */
+    public boolean hasError() {
+        return this.error != null;
+    }
+
+    /**
+     * Get the error associated with this request, if there is such an error.
+     * 
+     * @return the error, or null if there is none
+     */
+    public Throwable getError() {
+        return error;
+    }
+
+}
Index: src/main/java/org/jboss/dna/graph/requests/UpdatePropertiesRequest.java
===================================================================
--- src/main/java/org/jboss/dna/graph/requests/UpdatePropertiesRequest.java	(revision 0)
+++ src/main/java/org/jboss/dna/graph/requests/UpdatePropertiesRequest.java	(revision 0)
@@ -0,0 +1,151 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.properties.Property;
+
+/**
+ * Request to update the properties on the node at the specified location.
+ * 
+ * @author Randall Hauch
+ */
+public class UpdatePropertiesRequest extends Request implements Iterable<Property> {
+
+    private final Location on;
+    private final List<Property> properties;
+
+    /**
+     * Create a request to update the properties on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param properties the new properties on the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to update
+     */
+    public UpdatePropertiesRequest( Location on,
+                                    Property... properties ) {
+        CheckArg.isNotNull(on, "on");
+        CheckArg.isNotEmpty(properties, "properties");
+        this.on = on;
+        this.properties = Collections.unmodifiableList(Arrays.asList(properties));
+    }
+
+    /**
+     * Create a request to update the properties on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param properties the new properties on the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to update
+     */
+    public UpdatePropertiesRequest( Location on,
+                                    Iterable<Property> properties ) {
+        CheckArg.isNotNull(on, "on");
+        this.on = on;
+        List<Property> props = new LinkedList<Property>();
+        for (Property property : properties) {
+            if (property != null) props.add(property);
+        }
+        this.properties = Collections.unmodifiableList(props);
+        CheckArg.isNotEmpty(this.properties, "properties");
+    }
+
+    /**
+     * Create a request to update the properties on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param properties the new properties on the node
+     * @throws IllegalArgumentException if the location is null or if there are no properties to update
+     */
+    public UpdatePropertiesRequest( Location on,
+                                    Iterator<Property> properties ) {
+        CheckArg.isNotNull(on, "on");
+        this.on = on;
+        List<Property> props = new LinkedList<Property>();
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            if (property != null) props.add(property);
+        }
+        this.properties = Collections.unmodifiableList(props);
+        CheckArg.isNotEmpty(this.properties, "properties");
+    }
+
+    /**
+     * Get the location defining the node that is to be updated.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location on() {
+        return on;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Property> iterator() {
+        return this.properties.iterator();
+    }
+
+    /**
+     * Get the properties for the node.
+     * 
+     * @return the collection of properties; never null and never empty
+     */
+    public Collection<Property> properties() {
+        return properties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (this.getClass().isInstance(obj)) {
+            UpdatePropertiesRequest that = (UpdatePropertiesRequest)obj;
+            if (!this.on().equals(that.on())) return false;
+            if (!this.properties().equals(that.properties())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "update properties on " + on() + " to " + StringUtil.readableString(properties());
+    }
+
+}
