Author: bcarothers
Date: 2009-06-27 20:33:32 -0400 (Sat, 27 Jun 2009)
New Revision: 1066
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
Removed:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UuidConflictBehavior.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/UuidAlreadyExistsException.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcRequestProcesor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
Log:
DNA-370 JcrWorkspace.clone Is Not Implemented
I tried to make workspace.update work with the existing CopyBranchRequest, but it required
some changes that would have made the semantics of to(...) and into(...) ambiguous and
dependent on whether the UUID conflict behavior was ALWAYS_CREATE_NEW or not. Instead, I
pulled the UUID behavior out of the CopyBranchRequest and added it into a new request -
CloneBranchRequest. This provides clear semantics for copy (always creates new UUIDs, may
be between workspaces or within a single workspace, may function at the property or node
level) and clone (always preserves UUIDs, only works between workspaces, may remove nodes
from target workspace, only works at the node level).
Committed the patch that adds the new request and all of the associated changes on the
graph side (adding clone operation) and the connector (implementing clone behavior for
writable connectors) and modifies the workspace.clone and node.update operations
accordingly.
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 2009-06-27 02:54:06 UTC
(rev 1065)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-06-28 00:33:32 UTC
(rev 1066)
@@ -78,7 +78,6 @@
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.RequestBuilder;
import org.jboss.dna.graph.request.UnsupportedRequestException;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
@@ -525,7 +524,7 @@
* </p>
*
* @param firstIdProperty the first identification property of the node that is to be
moved
- * @param additionalIdProperties the remaining idenficiation properties of the node
that is to be moved
+ * @param additionalIdProperties the remaining identification properties of the node
that is to be moved
* @return the object that can be used to specify addition nodes to be moved or the
location of the node where the node is to
* be moved
*/
@@ -535,6 +534,174 @@
}
/**
+ * Begin the request to clone a node at the specified location into a parent node at
a different location, which is specified
+ * via the <code>into(...)</code> method on the returned {@link Clone}
object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param from the location of the node that is to be cloned.
+ * @return the object that can be used to specify the location of the node where the
node is to be cloned
+ */
+ public Clone<Graph> clone( Location from ) {
+ return new CloneAction<Graph>(this, from) {
+ @Override
+ protected Graph submit( String fromWorkspaceName,
+ Location from,
+ String intoWorkspaceName,
+ Location into,
+ Name desiredName,
+ Segment desiredSegment,
+ boolean removeExisting ) {
+ requests.cloneBranch(from,
+ fromWorkspaceName,
+ into,
+ intoWorkspaceName,
+ desiredName,
+ desiredSegment,
+ removeExisting);
+ return and();
+ }
+ };
+ }
+
+ /**
+ * Begin the request to clone the specified node into a parent node at a different
location, which is specified via the
+ * <code>into(...)</code> method on the returned {@link Clone} object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param from the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( Node from ) {
+ return clone(from.getLocation());
+ }
+
+ /**
+ * Begin the request to clone a node located at the supplied path into a parent node
at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned {@link
Clone} object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param fromPath the path to the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( String fromPath ) {
+ return clone(Location.create(createPath(fromPath)));
+ }
+
+ /**
+ * Begin the request to clone a node located at the supplied path into a parent node
at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned {@link
Clone} object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param from the path to the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( Path from ) {
+ return clone(Location.create(from));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified unique identifier into a
parent node at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned {@link
Clone} object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param from the UUID of the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( UUID from ) {
+ return clone(Location.create(from));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified unique identification
property into a parent node at a different
+ * location, which is specified via the <code>into(...)</code> method on
the returned {@link Clone} object. The identification
+ * property should uniquely identify a single node.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param idProperty the unique identification property of the node that is to be
copied.
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( Property idProperty ) {
+ return clone(Location.create(idProperty));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified identification properties
into a parent node at a different location,
+ * which is specified via the <code>into(...)</code> method on the
returned {@link Clone} object. The identification
+ * properties should uniquely identify a single node.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be performed
immediately when the {@link WithUuids UUID
+ * behavior} is specified.
+ * </p>
+ * <p>
+ * The clone operation differs from the copy operation in that it must replicate
nodes from one workspace to another (the copy
+ * operations supports replicating nodes within a workspace as well as across
workspaces) and that it preserves UUIDs (the
+ * copy operation always generates new UUIDs).
+ * </p>
+ *
+ * @param firstIdProperty the first identification property of the node that is to be
copied
+ * @param additionalIdProperties the remaining identification properties of the node
that is to be copied
+ * @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
+ * be copied
+ */
+ public Clone<Graph> clone( Property firstIdProperty,
+ Property... additionalIdProperties ) {
+ return clone(Location.create(firstIdProperty, additionalIdProperties));
+ }
+
+ /**
* Begin the request to copy the specified node into a parent node at a different
location, which is specified via the
* <code>into(...)</code> method on the returned {@link Copy} object.
* <p>
@@ -568,8 +735,7 @@
protected Graph submit( String fromWorkspaceName,
Locations from,
Location into,
- Name childName,
- UuidConflictBehavior uuidConflictBehavior ) {
+ Name childName ) {
String workspaceName = fromWorkspaceName != null ? fromWorkspaceName :
getCurrentWorkspaceName();
do {
requests.copyBranch(from.getLocation(),
@@ -577,8 +743,7 @@
into,
getCurrentWorkspaceName(),
childName,
- NodeConflictBehavior.APPEND,
- uuidConflictBehavior);
+ NodeConflictBehavior.APPEND);
} while ((from = from.next()) != null);
return and();
}
@@ -660,7 +825,7 @@
* </p>
*
* @param firstIdProperty the first identification property of the node that is to be
copied
- * @param additionalIdProperties the remaining idenficiation properties of the node
that is to be copied
+ * @param additionalIdProperties the remaining identification properties of the node
that is to be copied
* @return the object that can be used to specify addition nodes to be copied or the
location of the node where the node is to
* be copied
*/
@@ -737,7 +902,7 @@
* a single node. This request is submitted to the repository immediately.
*
* @param firstIdProperty the first identification property of the node that is to be
copied
- * @param additionalIdProperties the remaining idenficiation properties of the node
that is to be copied
+ * @param additionalIdProperties the remaining identification properties of the node
that is to be copied
* @return an object that may be used to start another request
*/
public Conjunction<Graph> delete( Property firstIdProperty,
@@ -2312,7 +2477,7 @@
* </p>
*
* @param firstIdProperty the first identification property of the node that is
to be moved
- * @param additionalIdProperties the remaining idenficiation properties of the
node that is to be moved
+ * @param additionalIdProperties the remaining identification properties of the
node that is to be moved
* @return the object that can be used to specify addition nodes to be moved or
the location of the node where the node is
* to be moved
*/
@@ -2330,7 +2495,7 @@
* called.
* </p>
*
- * @param idProperties the idenficiation properties of the node that is to be
moved
+ * @param idProperties the identification properties of the node that is to be
moved
* @return the object that can be used to specify addition nodes to be moved or
the location of the node where the node is
* to be moved
*/
@@ -2339,6 +2504,158 @@
}
/**
+ * Begin the request to clone the specified node into a parent node at a
different location, which is specified via the
+ * <code>into(...)</code> method on the returned {@link Clone}
object.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param from the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Node from ) {
+ return clone(from.getLocation());
+ }
+
+ /**
+ * Begin the request to clone a node at the specified location into a parent node
at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned
{@link Clone} object.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param from the location of the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Location from ) {
+ assertNotExecuted();
+ return new CloneAction<BatchConjunction>(this.nextRequests, from) {
+ @Override
+ protected BatchConjunction submit( String fromWorkspaceName,
+ Location from,
+ String intoWorkspaceName,
+ Location into,
+ Name desiredName,
+ Segment desiredSegment,
+ boolean removeExisting ) {
+ requests.cloneBranch(from,
+ fromWorkspaceName,
+ into,
+ intoWorkspaceName,
+ desiredName,
+ desiredSegment,
+ removeExisting);
+ return and();
+ }
+ };
+ }
+
+ /**
+ * Begin the request to clone a node located at the supplied path into a parent
node at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned
{@link Clone} object.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param fromPath the path to the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( String fromPath ) {
+ return clone(Location.create(createPath(fromPath)));
+ }
+
+ /**
+ * Begin the request to clone a node located at the supplied path into a parent
node at a different location, which is
+ * specified via the <code>into(...)</code> method on the returned
{@link Clone} object.
+ * <p>
+ * Like all other methods on the {@link Graph}, the clone request will be
performed immediately when the
+ * <code>into(...)</code> method is called.
+ * </p>
+ *
+ * @param from the path to the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Path from ) {
+ return clone(Location.create(from));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified unique identifier into a
parent node at a different location,
+ * which is specified via the <code>into(...)</code> method on the
returned {@link Clone} object.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param from the UUID of the node that is to be copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( UUID from ) {
+ return clone(Location.create(from));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified unique identification
property into a parent node at a different
+ * location, which is specified via the <code>into(...)</code> method
on the returned {@link Clone} object. The
+ * identification property should uniquely identify a single node.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param idProperty the unique identification property of the node that is to be
copied.
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Property idProperty ) {
+ return clone(Location.create(idProperty));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified identification properties
into a parent node at a different
+ * location, which is specified via the <code>into(...)</code> method
on the returned {@link Clone} object. The
+ * identification properties should uniquely identify a single node.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param firstIdProperty the first identification property of the node that is
to be copied
+ * @param additionalIdProperties the remaining identification properties of the
node that is to be copied
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Property firstIdProperty,
+ Property... additionalIdProperties ) {
+ return clone(Location.create(firstIdProperty, additionalIdProperties));
+ }
+
+ /**
+ * Begin the request to clone a node with the specified identification properties
into a parent node at a different
+ * location, which is specified via the <code>into(...)</code> method
on the returned {@link Clone} object. The
+ * identification properties should uniquely identify a single node.
+ * <p>
+ * Like all other methods on the {@link Batch}, the request will be performed
when the {@link #execute()} method is
+ * called.
+ * </p>
+ *
+ * @param idProperties the identification properties of the node that is to be
copied
+ * @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
+ * is to be copied
+ */
+ public Clone<BatchConjunction> clone( Iterable<Property> idProperties
) {
+ return clone(Location.create(idProperties));
+ }
+
+ /**
* Begin the request to copy the specified node into a parent node at a different
location, which is specified via the
* <code>into(...)</code> method on the returned {@link Copy}
object.
* <p>
@@ -2373,17 +2690,10 @@
protected BatchConjunction submit( String fromWorkspaceName,
Locations from,
Location into,
- Name copyName,
- UuidConflictBehavior
uuidConflictBehavior ) {
+ Name copyName ) {
String workspaceName = fromWorkspaceName != null ? fromWorkspaceName
: getCurrentWorkspaceName();
do {
- requestQueue.copyBranch(from.getLocation(),
- workspaceName,
- into,
- workspaceName,
- copyName,
- null,
- uuidConflictBehavior);
+ requestQueue.copyBranch(from.getLocation(), workspaceName, into,
workspaceName, copyName, null);
} while ((from = from.next()) != null);
return and();
}
@@ -2465,7 +2775,7 @@
* </p>
*
* @param firstIdProperty the first identification property of the node that is
to be copied
- * @param additionalIdProperties the remaining idenficiation properties of the
node that is to be copied
+ * @param additionalIdProperties the remaining identification properties of the
node that is to be copied
* @return the object that can be used to specify addition nodes to be copied or
the location of the node where the node
* is to be copied
*/
@@ -2586,7 +2896,7 @@
* </p>
*
* @param firstIdProperty the first identification property of the node that is
to be copied
- * @param additionalIdProperties the remaining idenficiation properties of the
node that is to be copied
+ * @param additionalIdProperties the remaining identification properties of the
node that is to be copied
* @return an object that may be used to start another request
*/
public BatchConjunction delete( Property firstIdProperty,
@@ -3894,6 +4204,31 @@
}
/**
+ * A component that defines a new child name for a node.
+ *
+ * @param <Next> The interface that is to be returned when this request is
completed
+ */
+ public interface AsChild<Next> {
+ /**
+ * Finish the request by specifying the exact segment for the new child node. If
no segment already exists, this request
+ * should fail.
+ *
+ * @param newSegment the new name
+ * @return the interface for additional requests or actions
+ */
+ Next as( Path.Segment newSegment );
+
+ /**
+ * Finish the request by specifying the name of the new child node. This method
indicates that the child should be added
+ * as a new node with the given name at the end of the parents children
+ *
+ * @param newName the new name
+ * @return the interface for additional requests or actions
+ */
+ Next as( Name newName );
+ }
+
+ /**
* A component that defines a new name for a node.
*
* @param <Next> The interface that is to be returned when this request is
completed
@@ -4003,14 +4338,32 @@
* @param <Next> The interface that is to be returned when this request is
completed
* @author Randall Hauch
*/
- public interface Copy<Next>
- extends WithUuids<FromWorkspace<CopyTarget<Next>>>,
FromWorkspace<CopyTarget<Next>>, CopyTarget<Next>,
And<Copy<Next>> {
+ public interface Copy<Next> extends
FromWorkspace<CopyTarget<Next>>, CopyTarget<Next>,
And<Copy<Next>> {
}
public interface CopyTarget<Next> extends To<Next>, Into<Next> {
}
/**
+ * The interface for defining a branch of nodes to be cloned and the location where
the clone is to be placed. Cloning a
+ * branch differs from copying a branch in that:
+ * <ol>
+ * <li>Nodes can be copied within the same workspace or to another workspace;
cloned nodes must be copied from one workspace
+ * into another.</li>
+ * <li>Copied nodes always get new UUIDs; cloned nodes always maintain their
UUIDs and hence must define the behavior that
+ * occurs if a node with the same UUID already exists in the new
workspace.</li>
+ * <li>Nodes can be copied to a specific name under a specific parent, but can
only be added as a new child node at the end of
+ * the new parent's children; nodes can be cloned to an exact location among the
parent's children, replacing the existing
+ * node at that location.</li>
+ * </ol>
+ *
+ * @param <Next>
+ */
+ public interface Clone<Next> extends
FromWorkspace<AsChild<Into<WithUuids<Next>>>> {
+
+ }
+
+ /**
* The interface for specifying that a node should come from a workspace other than
the current workspace.
*
* @param <Next> The interface that is to be returned when this request is
completed
@@ -4025,10 +4378,8 @@
* @param <Next> The interface that is to be returned when this request is
completed
*/
public interface WithUuids<Next> {
- Next withNewUuids();
+ Next failingIfAnyUuidsMatch();
- Next failingIfUuidsMatch();
-
Next replacingExistingNodesWithSameUuids();
}
@@ -5797,14 +6148,12 @@
protected abstract class CopyAction<T> extends AbstractAction<T>
implements Copy<T> {
protected Locations from;
protected String fromWorkspaceName;
- protected UuidConflictBehavior uuidConflictBehavior;
/*package*/CopyAction( T afterConjunction,
Location from ) {
super(afterConjunction);
this.from = new Locations(from);
this.fromWorkspaceName = Graph.this.getCurrentWorkspaceName();
- this.uuidConflictBehavior = UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID;
}
public Copy<T> and( Location from ) {
@@ -5850,31 +6199,13 @@
* @param from the locations that are being copied
* @param into the parent location
* @param nameForCopy the name that should be used for the copy, or null if the
name should be the same as the original
- * @param uuidConflictBehavior the behavior to be used for resolving any UUID
collisions that the copy request might
- * create
* @return this object, for method chaining
*/
protected abstract T submit( String fromWorkspaceName,
Locations from,
Location into,
- Name nameForCopy,
- UuidConflictBehavior uuidConflictBehavior );
+ Name nameForCopy );
- public FromWorkspace<CopyTarget<T>> failingIfUuidsMatch() {
- this.uuidConflictBehavior = UuidConflictBehavior.THROW_EXCEPTION;
- return this;
- }
-
- public FromWorkspace<CopyTarget<T>> withNewUuids() {
- this.uuidConflictBehavior = UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID;
- return this;
- }
-
- public FromWorkspace<CopyTarget<T>>
replacingExistingNodesWithSameUuids() {
- this.uuidConflictBehavior = UuidConflictBehavior.REPLACE_EXISTING_NODE;
- return this;
- }
-
public CopyTarget<T> fromWorkspace( String workspaceName ) {
this.fromWorkspaceName = workspaceName;
@@ -5882,32 +6213,28 @@
}
public T into( Location into ) {
- return submit(this.fromWorkspaceName, this.from, into, null,
this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, into, null);
}
public T into( Path into ) {
- return submit(this.fromWorkspaceName, this.from, Location.create(into), null,
this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, Location.create(into),
null);
}
public T into( UUID into ) {
- return submit(this.fromWorkspaceName, this.from, Location.create(into), null,
this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, Location.create(into),
null);
}
public T into( Property firstIdProperty,
Property... additionalIdProperties ) {
- return submit(this.fromWorkspaceName,
- this.from,
- Location.create(firstIdProperty, additionalIdProperties),
- null,
- this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from,
Location.create(firstIdProperty, additionalIdProperties), null);
}
public T into( Property into ) {
- return submit(this.fromWorkspaceName, this.from, Location.create(into), null,
this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, Location.create(into),
null);
}
public T into( String into ) {
- return submit(this.fromWorkspaceName, this.from,
Location.create(createPath(into)), null, this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from,
Location.create(createPath(into)), null);
}
public T to( Location desiredLocation ) {
@@ -5919,11 +6246,7 @@
throw new
IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from,
desiredLocation));
}
Path parent = desiredPath.getParent();
- return submit(this.fromWorkspaceName,
- this.from,
- Location.create(parent),
- desiredPath.getLastSegment().getName(),
- this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, Location.create(parent),
desiredPath.getLastSegment().getName());
}
public T to( Path desiredPath ) {
@@ -5931,11 +6254,7 @@
throw new
IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredPath));
}
Path parent = desiredPath.getParent();
- return submit(this.fromWorkspaceName,
- this.from,
- Location.create(parent),
- desiredPath.getLastSegment().getName(),
- this.uuidConflictBehavior);
+ return submit(this.fromWorkspaceName, this.from, Location.create(parent),
desiredPath.getLastSegment().getName());
}
public T to( String desiredPath ) {
@@ -5944,6 +6263,103 @@
}
@NotThreadSafe
+ public abstract class CloneAction<T> extends AbstractAction<T> implements
Clone<T> {
+ private final Location from;
+
+ /*package*/CloneAction( T afterConjunction,
+ Location from ) {
+ super(afterConjunction);
+ this.from = from;
+ }
+
+ protected abstract T submit( String fromWorkspaceName,
+ Location from,
+ String intoWorkspaceName,
+ Location into,
+ Name desiredName,
+ Segment desiredSegment,
+ boolean removeExisting );
+
+ public AsChild<Into<WithUuids<T>>> fromWorkspace( final String
workspaceName ) {
+ final CloneAction<T> source = this;
+ return new AsChild<Into<WithUuids<T>>>() {
+ public Into<WithUuids<T>> as( final Name name ) {
+ return new CloneTargetAction<T>(afterConjunction(), source) {
+ @Override
+ protected T submit( Location into,
+ boolean removeExisting ) {
+ String intoWorkspaceName = getCurrentWorkspaceName();
+ return source.submit(workspaceName, from, intoWorkspaceName,
into, name, null, removeExisting);
+ }
+ };
+ }
+
+ public Into<WithUuids<T>> as( final Segment segment ) {
+ return new CloneTargetAction<T>(afterConjunction(), source) {
+ @Override
+ protected T submit( Location into,
+ boolean removeExisting ) {
+ String intoWorkspaceName = getCurrentWorkspaceName();
+ return source.submit(workspaceName, from, intoWorkspaceName,
into, null, segment, removeExisting);
+ }
+ };
+ }
+
+ };
+ }
+ }
+
+ @NotThreadSafe
+ public abstract class CloneTargetAction<T> extends AbstractAction<T>
implements Into<WithUuids<T>> {
+ protected final CloneAction<T> source;
+
+ /*package*/CloneTargetAction( T afterConjunction,
+ CloneAction<T> source ) {
+ super(afterConjunction);
+ this.source = source;
+ }
+
+ protected abstract T submit( Location into,
+ boolean removeExisting );
+
+ public WithUuids<T> into( final Location into ) {
+ return new WithUuids<T>() {
+ public T failingIfAnyUuidsMatch() {
+ submit(into, false);
+ return and();
+ }
+
+ public T replacingExistingNodesWithSameUuids() {
+ submit(into, true);
+ return and();
+
+ }
+ };
+ }
+
+ public WithUuids<T> into( Path into ) {
+ return into(Location.create(into));
+ }
+
+ public WithUuids<T> into( UUID into ) {
+ return into(Location.create(into));
+ }
+
+ public WithUuids<T> into( Property firstIdProperty,
+ Property... additionalIdProperties ) {
+ return into(Location.create(firstIdProperty, additionalIdProperties));
+ }
+
+ public WithUuids<T> into( Property into ) {
+ return into(Location.create(into));
+ }
+
+ public WithUuids<T> into( String into ) {
+ return into(Location.create(createPath(into)));
+ }
+ }
+
+ @NotThreadSafe
protected abstract class CreateAction<T> extends AbstractAction<T>
implements Create<T> {
private final String workspaceName;
private final Location parent;
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-06-27 02:54:06
UTC (rev 1065)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-06-28 00:33:32
UTC (rev 1066)
@@ -77,6 +77,8 @@
public static I18n unableToCopyToTheRoot;
public static I18n actualLocationIsNotSameAsInputLocation;
public static I18n actualLocationIsNotChildOfInputLocation;
+ public static I18n actualLocationIsNotAtCorrectChildSegment;
+ public static I18n actualLocationDoesNotHaveCorrectChildName;
public static I18n actualLocationMustHavePath;
public static I18n actualNewLocationIsNotSameAsInputLocation;
public static I18n actualNewLocationMustHavePath;
@@ -117,6 +119,7 @@
public static I18n unableToDeletePlaceholder;
public static I18n copyLimitedToBeWithinSingleSource;
public static I18n moveLimitedToBeWithinSingleSource;
+ public static I18n cloneLimitedToBeWithinSingleSource;
static {
try {
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/UuidAlreadyExistsException.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/UuidAlreadyExistsException.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/UuidAlreadyExistsException.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -25,13 +25,12 @@
import java.util.UUID;
import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.request.CopyBranchRequest;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
+import org.jboss.dna.graph.request.CloneBranchRequest;
/**
* Exception that indicates that a copy request failed because one of the UUIDs in the
source branch already exists in the target
- * workspace and the {@link CopyBranchRequest#uuidConflictBehavior() UUID conflict
behavior} is set to
- * {@link UuidConflictBehavior#THROW_EXCEPTION}.
+ * workspace and the {@link CloneBranchRequest#removeExisting() UUID conflict behavior}
is set to throw an exception instead of
+ * removing the existing nodes.
*/
public class UuidAlreadyExistsException extends RepositorySourceException {
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -54,6 +54,7 @@
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -1262,7 +1263,7 @@
// Create the pushed-down request ...
CopyBranchRequest pushDown = new CopyBranchRequest(fromProxy.location(),
fromProxy.workspaceName(), intoProxy.location(),
intoProxy.workspaceName(),
request.desiredName(),
-
request.nodeConflictBehavior(), request.uuidConflictBehavior());
+
request.nodeConflictBehavior());
// Create the federated request ...
FederatedRequest federatedRequest = new FederatedRequest(request);
federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(),
intoProxy.projection());
@@ -1274,6 +1275,65 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ // Figure out where the 'from' is projected ...
+ ProjectedNode projectedFromNode = project(request.from(),
request.fromWorkspace(), request, false);
+ if (projectedFromNode == null) return;
+ ProjectedNode projectedIntoNode = project(request.into(),
request.intoWorkspace(), request, true);
+ if (projectedIntoNode == null) return;
+
+ // Limitation: only able to project the copy if the 'from' and
'into' are in the same source & projection ...
+ while (projectedFromNode != null) {
+ if (projectedFromNode.isProxy()) {
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ // Look for a projectedIntoNode that has the same source/projection ...
+ while (projectedIntoNode != null) {
+ if (projectedIntoNode.isProxy()) {
+ // Both are proxies, so compare the projection ...
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ if
(fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName()))
break;
+ }
+ projectedIntoNode = projectedIntoNode.next();
+ }
+ if (projectedIntoNode != null) break;
+ }
+ projectedFromNode = projectedFromNode.next();
+ }
+ if (projectedFromNode == null || projectedIntoNode == null) {
+ // The copy is not done within a single source ...
+ String msg =
GraphI18n.cloneLimitedToBeWithinSingleSource.text(readable(request.from()),
+
request.fromWorkspace(),
+
readable(request.into()),
+
request.intoWorkspace(),
+
getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ return;
+ }
+
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ assert
fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName());
+ boolean sameLocation = fromProxy.isSameLocationAsOriginal() &&
intoProxy.isSameLocationAsOriginal();
+
+ // Create the pushed-down request ...
+ CloneBranchRequest pushDown = new CloneBranchRequest(fromProxy.location(),
fromProxy.workspaceName(),
+ intoProxy.location(),
intoProxy.workspaceName(),
+ request.desiredName(),
request.desiredSegment(),
+ request.removeExisting());
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(),
intoProxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -33,6 +33,7 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -173,8 +174,7 @@
*/
@Override
public void process( ReadNextBlockOfChildrenRequest request ) {
- ReadNextBlockOfChildrenRequest source =
(ReadNextBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest()
-
.getRequest();
+ ReadNextBlockOfChildrenRequest source =
(ReadNextBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
if (checkErrorOrCancel(request, source)) return;
request.setActualLocationOfStartingAfterNode(source.getActualLocationOfStartingAfterNode());
for (Location childInSource : source.getChildren()) {
@@ -317,6 +317,18 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ CloneBranchRequest source =
(CloneBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(),
source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -45,6 +45,7 @@
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.property.ValueComparators;
import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -902,6 +903,24 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ CloneBranchRequest source = (CloneBranchRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location locationBefore = source.getActualLocationBefore();
+ Location locationAfter = source.getActualLocationBefore();
+ locationBefore = projectToFederated(request.from(), projected.getProjection(),
locationBefore, request);
+ locationAfter = projectToFederated(request.into(),
projected.getSecondProjection(), locationAfter, request);
+ request.setActualLocations(locationBefore, locationAfter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -45,8 +45,8 @@
import org.jboss.dna.graph.property.Reference;
import org.jboss.dna.graph.property.UuidFactory;
import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.Path.Segment;
import org.jboss.dna.graph.property.basic.RootPath;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
/**
* A default implementation of {@link MapWorkspace} that only requires the user to
implement some simple, map-like operations.
@@ -415,8 +415,6 @@
* @param newParent the parent where the copy is to be placed; may not be null
* @param desiredName the desired name for the node; if null, the name will be
obtained from the original node
* @param recursive true if the copy should be recursive
- * @param uuidConflictBehavior the behavior to use if a UUID in the branch rooted at
{@code original} already exists in the
- * new workspace
* @return the new node, which is the top of the new subgraph
*/
public MapNode copyNode( ExecutionContext context,
@@ -424,48 +422,8 @@
MapWorkspace newWorkspace,
MapNode newParent,
Name desiredName,
- boolean recursive,
- UuidConflictBehavior uuidConflictBehavior ) throws
UuidAlreadyExistsException {
- Map<UUID, UUID> copyMap = null;
- Set<UUID> uuidsInFromBranch = null;
-
- switch (uuidConflictBehavior) {
- case ALWAYS_CREATE_NEW_UUID:
-
- /*
- * The copyNode method uses the presence of a non-null map as an
indicator that new UUIDs should be created
- * during the copy operation
- */
- copyMap = new HashMap<UUID, UUID>();
- break;
- case REPLACE_EXISTING_NODE:
- uuidsInFromBranch = getUuidsUnderNode(original);
-
- for (UUID uuid : uuidsInFromBranch) {
- MapNode existing;
- if (null != (existing = newWorkspace.getNode(uuid))) {
- newWorkspace.removeNode(context, existing);
- }
- }
- break;
- case THROW_EXCEPTION:
- uuidsInFromBranch = getUuidsUnderNode(original);
- PathFactory pathFactory = context.getValueFactories().getPathFactory();
- for (UUID uuid : uuidsInFromBranch) {
- MapNode existing;
- if (null != (existing = newWorkspace.getNode(uuid))) {
- NamespaceRegistry namespaces = context.getNamespaceRegistry();
- String path = newWorkspace.pathFor(pathFactory,
existing).getString(namespaces);
- throw new UuidAlreadyExistsException(repository.getSourceName(),
uuid, path, newWorkspace.getName());
- }
- }
- break;
-
- default:
- throw new IllegalStateException("Unexpected UUID conflict behavior:
" + uuidConflictBehavior);
- }
-
- return copyNode(context, original, newWorkspace, newParent, desiredName, true,
copyMap);
+ boolean recursive ) {
+ return copyNode(context, original, newWorkspace, newParent, desiredName, true,
new HashMap<UUID, UUID>());
}
/**
@@ -483,12 +441,12 @@
* @return the new node, which is the top of the new subgraph
*/
protected MapNode copyNode( ExecutionContext context,
- MapNode original,
- MapWorkspace newWorkspace,
- MapNode newParent,
- Name desiredName,
- boolean recursive,
- Map<UUID, UUID> oldToNewUuids ) {
+ MapNode original,
+ MapWorkspace newWorkspace,
+ MapNode newParent,
+ Name desiredName,
+ boolean recursive,
+ Map<UUID, UUID> oldToNewUuids ) {
assert context != null;
assert original != null;
assert newParent != null;
@@ -557,8 +515,83 @@
}
/**
- * Returns all of the UUIDs in the branch rooted at {@code node}
+ * {@inheritDoc}
*
+ * @see MapWorkspace#cloneNode(ExecutionContext, MapNode, MapWorkspace, MapNode,
Name, Segment, boolean)
+ */
+ public MapNode cloneNode( ExecutionContext context,
+ MapNode original,
+ MapWorkspace newWorkspace,
+ MapNode newParent,
+ Name desiredName,
+ Segment desiredSegment,
+ boolean removeExisting ) throws UuidAlreadyExistsException
{
+ assert context != null;
+ assert original != null;
+ assert newWorkspace != null;
+ assert newParent != null;
+
+ Set<UUID> uuidsInFromBranch = getUuidsUnderNode(original);
+ MapNode existing;
+
+ // TODO: Need to handle removing/throwing root node
+ if (removeExisting) {
+
+ for (UUID uuid : uuidsInFromBranch) {
+ if (null != (existing = newWorkspace.getNode(uuid))) {
+ newWorkspace.removeNode(context, existing);
+ }
+ }
+ } else {
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ uuidsInFromBranch.add(original.getUuid());
+ for (UUID uuid : uuidsInFromBranch) {
+ if (null != (existing = newWorkspace.getNode(uuid))) {
+ NamespaceRegistry namespaces = context.getNamespaceRegistry();
+ String path = newWorkspace.pathFor(pathFactory,
existing).getString(namespaces);
+ throw new UuidAlreadyExistsException(repository.getSourceName(),
uuid, path, newWorkspace.getName());
+ }
+ }
+ }
+
+ if (desiredSegment != null) {
+ MapNode newRoot = null;
+ for (MapNode child : newParent.getChildren()) {
+ if (desiredSegment.equals(child.getName())) {
+ newRoot = child;
+ break;
+ }
+ }
+
+ assert newRoot != null;
+
+ newRoot.getProperties().clear();
+ for (MapNode child : newRoot.getChildren()) {
+ newWorkspace.removeNode(context, child);
+ }
+
+ for (Property property : original.getProperties().values()) {
+ newRoot.setProperty(property);
+ }
+
+ for (MapNode child : original.getChildren()) {
+ copyNode(context, child, newWorkspace, newRoot, null, true, (Map<UUID,
UUID>)null);
+ }
+ return newRoot;
+ }
+
+ existing = newWorkspace.getNode(original.getUuid());
+
+ if (existing != null) {
+ newWorkspace.removeNode(context, existing);
+ }
+ return copyNode(context, original, newWorkspace, newParent, desiredName, true,
(Map<UUID, UUID>)null);
+ }
+
+ /**
+ * Returns all of the UUIDs in the branch rooted at {@code node}. The UUID of {@code
node} will not be included in the set of
+ * returned UUIDs.
+ *
* @param node the root of the branch
* @return all of the UUIDs in the branch rooted at {@code node}
*/
@@ -571,9 +604,8 @@
private void uuidsUnderNode( MapNode node,
Set<UUID> accumulator ) {
- accumulator.add(node.getUuid());
-
for (MapNode child : node.getChildren()) {
+ accumulator.add(child.getUuid());
uuidsUnderNode(child, accumulator);
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -32,7 +32,6 @@
import net.jcip.annotations.GuardedBy;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
/**
@@ -249,7 +248,7 @@
// Loop over each child and call this method to copy the immediate children
(and below).
// Note that this makes the copy have the same UUID as the original.
for (MapNode originalNode : origRoot.getChildren()) {
- original.copyNode(context, originalNode, workspace, root,
originalNode.getName().getName(), true, UuidConflictBehavior.REPLACE_EXISTING_NODE);
+ original.cloneNode(context, originalNode, workspace, root,
originalNode.getName().getName(), null, true);
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -44,6 +44,7 @@
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -62,7 +63,6 @@
/**
* The default implementation of the {@link RequestProcessor} for map repositories.
- *
*/
public class MapRequestProcessor extends RequestProcessor {
private final PathFactory pathFactory;
@@ -70,8 +70,8 @@
private final MapRepository repository;
public MapRequestProcessor( ExecutionContext context,
- MapRepository repository,
- RepositoryContext repositoryContext ) {
+ MapRepository repository,
+ RepositoryContext repositoryContext ) {
super(repository.getSourceName(), context, repositoryContext != null ?
repositoryContext.getObserver() : null);
this.repository = repository;
pathFactory = context.getValueFactories().getPathFactory();
@@ -119,6 +119,36 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ MapWorkspace workspace = getWorkspace(request, request.fromWorkspace());
+ MapWorkspace newWorkspace = getWorkspace(request, request.intoWorkspace());
+ if (workspace == null || newWorkspace == null) return;
+ MapNode node = getTargetNode(workspace, request, request.from());
+ if (node == null) return;
+
+ // Look up the new parent, which must exist ...
+ Path newParentPath = request.into().getPath();
+ MapNode newParent = newWorkspace.getNode(newParentPath);
+ MapNode newNode = workspace.cloneNode(getExecutionContext(),
+ node,
+ newWorkspace,
+ newParent,
+ request.desiredName(),
+ request.desiredSegment(),
+ request.removeExisting());
+ Path newPath =
getExecutionContext().getValueFactories().getPathFactory().create(newParentPath,
newNode.getName());
+ Location oldLocation = getActualLocation(request.from().getPath(), node);
+ Location newLocation = Location.create(newPath, newNode.getUuid());
+ request.setActualLocations(oldLocation, newLocation);
+ recordChange(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
*/
@Override
@@ -133,13 +163,7 @@
Path newParentPath = request.into().getPath();
Name desiredName = request.desiredName();
MapNode newParent = newWorkspace.getNode(newParentPath);
- MapNode newNode = workspace.copyNode(getExecutionContext(),
- node,
- newWorkspace,
- newParent,
- desiredName,
- true,
- request.uuidConflictBehavior());
+ MapNode newNode = workspace.copyNode(getExecutionContext(), node, newWorkspace,
newParent, desiredName, true);
Path newPath =
getExecutionContext().getValueFactories().getPathFactory().create(newParentPath,
newNode.getName());
Location oldLocation = getActualLocation(request.from().getPath(), node);
Location newLocation = Location.create(newPath, newNode.getUuid());
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -29,7 +29,6 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
/**
* The {@code MapWorkspace} defines the required methods for workspaces in a {@link
MapRepository map repository}. By default, a
@@ -138,20 +137,44 @@
* @param newParent the parent where the copy is to be placed; may not be null
* @param desiredName the desired name for the node; if null, the name will be
obtained from the original node
* @param recursive true if the copy should be recursive
- * @param uuidConflictBehavior the behavior to use to manage UUIDs from the source
into the target
* @return the new node, which is the top of the new subgraph
- * @throws UuidAlreadyExistsException if {@code uuidConflictBehavior} is true and and
a UUID in the source tree alread exists
- * in the new workspace
*/
MapNode copyNode( ExecutionContext context,
MapNode original,
MapWorkspace newWorkspace,
MapNode newParent,
Name desiredName,
- boolean recursive,
- UuidConflictBehavior uuidConflictBehavior ) throws
UuidAlreadyExistsException;
+ boolean recursive );
/**
+ * This should clone the subgraph given by the original node and place the cloned
copy under the supplied new parent. Note
+ * that internal references between nodes within the original subgraph must be
reflected as internal nodes within the new
+ * subgraph.
+ *
+ * @param context the context; may not be null
+ * @param original the node to be cloned; may not be null
+ * @param newWorkspace the workspace containing the new parent node; may not be null
+ * @param newParent the parent where the clone is to be placed; may not be null
+ * @param desiredName the desired name for the node; if null, the name will be
calculated from {@code desiredSegment}; Exactly
+ * one of {@code desiredSegment} and {@code desiredName} must be non-null
+ * @param desiredSegment the exact segment at which the clone should be rooted; if
null, the name will be inferred from
+ * {@code desiredName}; Exactly one of {@code desiredSegment} and {@code
desiredName} must be non-null
+ * @param removeExisting true if existing nodes in the new workspace with the same
UUIDs as nodes in the branch rooted at
+ * {@code original} should be removed; if false, a UuidAlreadyExistsException
will be thrown if a UUID conflict is
+ * detected
+ * @return the new node, which is the top of the new subgraph
+ * @throws UuidAlreadyExistsException if {@code removeExisting} is true and and a
UUID in the source tree already exists in
+ * the new workspace
+ */
+ MapNode cloneNode( ExecutionContext context,
+ MapNode original,
+ MapWorkspace newWorkspace,
+ MapNode newParent,
+ Name desiredName,
+ Path.Segment desiredSegment,
+ boolean removeExisting ) throws UuidAlreadyExistsException;
+
+ /**
* Find the lowest existing node along the path.
*
* @param path the path to the node; may not be null
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -572,8 +572,7 @@
String intoWorkspace,
Name nameForCopy ) {
return add(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForCopy,
-
CopyBranchRequest.DEFAULT_NODE_CONFLICT_BEHAVIOR,
- UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID));
+
CopyBranchRequest.DEFAULT_NODE_CONFLICT_BEHAVIOR));
}
/**
@@ -587,9 +586,6 @@
* used
* @param conflictBehavior the expected behavior if an equivalently-named child
already exists at the <code>into</code>
* location, or null if the default node conflict behavior should be used
- * @param uuidConflictBehavior the expected behavior if a node with the same UUID as
any of the nodes in the branch under the
- * {@code from} location in the {@code fromWorkspace} workspace already exists
in the workspace, or null if the default
- * UUID conflict behavior should be used
* @return this builder for method chaining; never null
* @throws IllegalArgumentException if either of the locations or workspace names are
null
*/
@@ -598,12 +594,9 @@
Location into,
String intoWorkspace,
Name nameForCopy,
- NodeConflictBehavior conflictBehavior,
- UuidConflictBehavior uuidConflictBehavior) {
+ NodeConflictBehavior conflictBehavior ) {
if (conflictBehavior == null) conflictBehavior =
CopyBranchRequest.DEFAULT_NODE_CONFLICT_BEHAVIOR;
- if (uuidConflictBehavior == null) uuidConflictBehavior =
CopyBranchRequest.DEFAULT_UUID_CONFLICT_BEHAVIOR;
- return add(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForCopy, conflictBehavior,
- uuidConflictBehavior));
+ return add(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForCopy, conflictBehavior));
}
/**
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -0,0 +1,313 @@
+package org.jboss.dna.graph.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.UuidAlreadyExistsException;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Instruction that a branch be cloned from one workspace into another. Cloning a branch
differs from cloning a branch in that:
+ * <ol>
+ * <li>Nodes can be cloned within the same workspace or to another workspace;
cloned nodes must be cloned from one workspace into
+ * another.</li>
+ * <li>Copied nodes always get new UUIDs; cloned nodes always maintain their UUIDs
and hence must define the behavior that occurs
+ * if a node with the same UUID already exists in the new workspace.</li>
+ * <li>Nodes can be cloned to a specific name under a specific parent, but can only
be added as a new child node at the end of the
+ * new parent's children; nodes can be cloned to an exact location among the
parent's children, replacing the existing node at
+ * that location.</li>
+ * </ol>
+ *
+ * @author Brian Carothers
+ */
+public class CloneBranchRequest extends ChangeRequest {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String fromWorkspace;
+ private final String intoWorkspace;
+ private final Location from;
+ private final Location into;
+ private final Name desiredName;
+ private final Path.Segment desiredSegment;
+ private final boolean removeExisting;
+ private Location actualFromLocation;
+ private Location actualIntoLocation;
+
+ /**
+ * Create a request to clone a branch to another.
+ *
+ * @param from the location of the top node in the existing branch that is to be
cloned
+ * @param fromWorkspace the name of the workspace where the
<code>from</code> node exists
+ * @param into the location of the existing node into which the clone should be
placed
+ * @param intoWorkspace the name of the workspace where the
<code>into</code> node is to be cloned
+ * @param nameForClone the desired name for the node that results from the clone, or
null if the name of the original should
+ * be used
+ * @param exactSegmentForClone the exact {@link Path.Segment segment} at which the
cloned tree should be rooted.
+ * @param removeExisting whether any nodes in the intoWorkspace with the same UUIDs
as a node in the source branch should be
+ * removed (if true) or a {@link UuidAlreadyExistsException} should be
thrown.
+ * @throws IllegalArgumentException if any of the parameters are null except for
{@code nameForClone} or {@code
+ * exactSegmentForClone}. Exactly one of {@code nameForClone} and {@code
exactSegmentForClone} must be null.
+ */
+ public CloneBranchRequest( Location from,
+ String fromWorkspace,
+ Location into,
+ String intoWorkspace,
+ Name nameForClone,
+ Path.Segment exactSegmentForClone,
+ boolean removeExisting ) {
+ CheckArg.isNotNull(from, "from");
+ CheckArg.isNotNull(into, "into");
+ CheckArg.isNotNull(fromWorkspace, "fromWorkspace");
+ CheckArg.isNotNull(intoWorkspace, "intoWorkspace");
+ CheckArg.isNotSame(from, fromWorkspace, into, intoWorkspace);
+ assert nameForClone == null ? exactSegmentForClone != null : exactSegmentForClone
== null;
+ this.from = from;
+ this.into = into;
+ this.fromWorkspace = fromWorkspace;
+ this.intoWorkspace = intoWorkspace;
+ this.desiredName = nameForClone;
+ this.desiredSegment = exactSegmentForClone;
+ this.removeExisting = removeExisting;
+ }
+
+ /**
+ * Get the location defining the top of the branch to be cloned
+ *
+ * @return the from location; never null
+ */
+ public Location from() {
+ return from;
+ }
+
+ /**
+ * Get the location defining the parent where the new clone is to be placed
+ *
+ * @return the to location; never null
+ */
+ public Location into() {
+ return into;
+ }
+
+ /**
+ * Get the name of the workspace containing the branch to be cloned.
+ *
+ * @return the name of the workspace containing the branch to be cloned; never null
+ */
+ public String fromWorkspace() {
+ return fromWorkspace;
+ }
+
+ /**
+ * Get the name of the workspace where the clone is to be placed
+ *
+ * @return the name of the workspace where the clone is to be placed; never null
+ */
+ public String intoWorkspace() {
+ return intoWorkspace;
+ }
+
+ /**
+ * Determine whether this clone operation is within the same workspace.
+ *
+ * @return true if this operation is to be performed within the same workspace, or
false if the workspace of the
+ * {@link #from() original} is different than that of the {@link #into()
clone}
+ */
+ public boolean isSameWorkspace() {
+ return false;
+ }
+
+ /**
+ * Get the name of the clone if it is to be different than that of the original.
+ *
+ * @return the desired name of the clone, or null if an {@link #desiredSegment()
exact segment} is specified.
+ */
+ public Name desiredName() {
+ return desiredName;
+ }
+
+ /**
+ * Get the exact {@link Path.Segment segment} at which the clone should be rooted
+ *
+ * @return the desired segment of the clone, or null if the desired name should be
used to generate a new child node for the
+ * {@code into} location
+ */
+ public Path.Segment desiredSegment() {
+ return desiredSegment;
+ }
+
+ /**
+ * Gets whether the clone should remove existing nodes in the {@link #intoWorkspace
new workspace} with the same UUID as any
+ * of the nodes in the source branch or should throw an exception if such conflict is
detected.
+ *
+ * @return whether the clone should remove existing nodes in the {@link
#intoWorkspace new workspace} with the same UUID as
+ * any of the nodes in the source branch or should throw an exception if such
conflict is detected; true indicates
+ * that the nodes should be removed and false indicates that an exception
should be thrown
+ */
+ public boolean removeExisting() {
+ return removeExisting;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#isReadOnly()
+ */
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ /**
+ * Sets the actual and complete location of the node being renamed and its new
location. This method must be called when
+ * processing the request, and the actual location must have a {@link
Location#getPath() path}.
+ *
+ * @param fromLocation the actual location of the node being cloned
+ * @param intoLocation the actual location of the new clone of the node
+ * @throws IllegalArgumentException if the either location is null; if the old
location does not represent the
+ * {@link Location#isSame(Location) same location} as the {@link #from() from
location}; if the new location does not
+ * represent the {@link Location#isSame(Location) same location} as the
{@link #into() into location}; if the either
+ * location does not have a path
+ * @throws IllegalStateException if the request is frozen
+ */
+ public void setActualLocations( Location fromLocation,
+ Location intoLocation ) {
+ checkNotFrozen();
+ if (!from.isSame(fromLocation)) { // not same if actual is null
+ throw new
IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(fromLocation,
from));
+ }
+ CheckArg.isNotNull(intoLocation, "intoLocation");
+ assert fromLocation != null;
+ assert intoLocation != null;
+ if (!fromLocation.hasPath()) {
+ throw new
IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(fromLocation));
+ }
+ if (!intoLocation.hasPath()) {
+ throw new
IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(intoLocation));
+ }
+ // The 'into' should be the parent of the 'newLocation' ...
+ if (into.hasPath() &&
!intoLocation.getPath().getParent().equals(into.getPath())) {
+ throw new
IllegalArgumentException(GraphI18n.actualLocationIsNotChildOfInputLocation.text(intoLocation,
into));
+ }
+
+ if (desiredSegment != null &&
!desiredSegment.equals(intoLocation.getPath().getLastSegment())) {
+ throw new
IllegalArgumentException(GraphI18n.actualLocationIsNotAtCorrectChildSegment.text(intoLocation,
+
desiredSegment));
+ }
+
+ if (desiredName != null &&
!desiredName.equals(intoLocation.getPath().getLastSegment().getName())) {
+ throw new IllegalArgumentException(
+
GraphI18n.actualLocationDoesNotHaveCorrectChildName.text(intoLocation, desiredName));
+ }
+
+ this.actualFromLocation = fromLocation;
+ this.actualIntoLocation = intoLocation;
+ }
+
+ /**
+ * Get the actual location of the node before being cloned.
+ *
+ * @return the actual location of the node before being moved, or null if the actual
location was not set
+ */
+ public Location getActualLocationBefore() {
+ return actualFromLocation;
+ }
+
+ /**
+ * Get the actual location of the node after being cloned.
+ *
+ * @return the actual location of the node after being cloned, or null if the actual
location was not set
+ */
+ public Location getActualLocationAfter() {
+ return actualIntoLocation;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ @Override
+ public boolean changes( String workspace,
+ Path path ) {
+ return into.hasPath() && into.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ @Override
+ public Location changedLocation() {
+ return into;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace()
+ */
+ @Override
+ public String changedWorkspace() {
+ return intoWorkspace();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, fromWorkspace, into, intoWorkspace);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualFromLocation = null;
+ this.actualIntoLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (this.getClass().isInstance(obj)) {
+ CloneBranchRequest that = (CloneBranchRequest)obj;
+ if (!this.from().equals(that.from())) return false;
+ if (!this.into().equals(that.into())) return false;
+ if (!this.fromWorkspace.equals(that.fromWorkspace)) return false;
+ if (!this.intoWorkspace.equals(that.intoWorkspace)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (desiredName != null) {
+ return "clone branch " + from() + " in the \"" +
fromWorkspace + "\" workspace into " + into() + " with name "
+ + desiredName + " in the \"" + intoWorkspace +
"\" workspace";
+ }
+ return "clone branch " + from() + " in the \"" +
fromWorkspace + "\" workspace into " + into() + " in the
\""
+ + intoWorkspace + "\" workspace as child " +
desiredSegment();
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -33,16 +33,18 @@
/**
* Instruction that a branch be copied from one location into another. This request can
copy a branch in one workspace into
- * another workspace, or it can copy a branch in the same workspace.
+ * another workspace, or it can copy a branch in the same workspace. Copying a branch
always results in the generation of new
+ * UUIDs for the copied nodes. {@link CloneBranchRequest Cloning a branch} provides
functionality similar to copy, but with the
+ * ability to preserve UUIDs in the move.
*
* @author Randall Hauch
+ * @see CloneBranchRequest
*/
public class CopyBranchRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
public static final NodeConflictBehavior DEFAULT_NODE_CONFLICT_BEHAVIOR =
NodeConflictBehavior.APPEND;
- public static final UuidConflictBehavior DEFAULT_UUID_CONFLICT_BEHAVIOR =
UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID;
private final Location from;
private final Location into;
@@ -50,7 +52,6 @@
private final String intoWorkspace;
private final Name desiredNameForCopy;
private final NodeConflictBehavior nodeConflictBehavior;
- private final UuidConflictBehavior uuidConflictBehavior;
private Location actualFromLocation;
private Location actualIntoLocation;
@@ -68,7 +69,7 @@
String fromWorkspace,
Location into,
String intoWorkspace ) {
- this(from, fromWorkspace, into, intoWorkspace, null,
DEFAULT_NODE_CONFLICT_BEHAVIOR, DEFAULT_UUID_CONFLICT_BEHAVIOR);
+ this(from, fromWorkspace, into, intoWorkspace, null,
DEFAULT_NODE_CONFLICT_BEHAVIOR);
}
/**
@@ -87,7 +88,7 @@
Location into,
String intoWorkspace,
Name nameForCopy ) {
- this(from, fromWorkspace, into, intoWorkspace, nameForCopy,
DEFAULT_NODE_CONFLICT_BEHAVIOR, DEFAULT_UUID_CONFLICT_BEHAVIOR);
+ this(from, fromWorkspace, into, intoWorkspace, nameForCopy,
DEFAULT_NODE_CONFLICT_BEHAVIOR);
}
/**
@@ -101,8 +102,6 @@
* used
* @param nodeConflictBehavior the expected behavior if an equivalently-named child
already exists at the <code>into</code>
* location
- * @param uuidConflictBehavior the expected behavior if a node with the same UUID as
any of the nodes in the branch under the
- * {@code from} location in the {@code fromWorkspace} workspace already exists
in the workspace
* @throws IllegalArgumentException if any of the parameters are null
*/
public CopyBranchRequest( Location from,
@@ -110,21 +109,18 @@
Location into,
String intoWorkspace,
Name nameForCopy,
- NodeConflictBehavior nodeConflictBehavior,
- UuidConflictBehavior uuidConflictBehavior ) {
+ NodeConflictBehavior nodeConflictBehavior ) {
CheckArg.isNotNull(from, "from");
CheckArg.isNotNull(into, "into");
CheckArg.isNotNull(fromWorkspace, "fromWorkspace");
CheckArg.isNotNull(intoWorkspace, "intoWorkspace");
CheckArg.isNotNull(nodeConflictBehavior, "nodeConflictBehavior");
- CheckArg.isNotNull(uuidConflictBehavior, "uuidConflictBehavior");
this.from = from;
this.into = into;
this.fromWorkspace = fromWorkspace;
this.intoWorkspace = intoWorkspace;
this.desiredNameForCopy = nameForCopy;
this.nodeConflictBehavior = nodeConflictBehavior;
- this.uuidConflictBehavior = uuidConflictBehavior;
}
/**
@@ -203,15 +199,6 @@
}
/**
- * Get the expected behavior when one of the nodes in the branch has the same UUID as
an existing node in the workspace.
- *
- * @return the behavior specification
- */
- public UuidConflictBehavior uuidConflictBehavior() {
- return uuidConflictBehavior;
- }
-
- /**
* Sets the actual and complete location of the node being renamed and its new
location. This method must be called when
* processing the request, and the actual location must have a {@link
Location#getPath() path}.
*
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -28,6 +28,7 @@
import java.util.Map;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.connector.UuidAlreadyExistsException;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
@@ -432,8 +433,6 @@
* used
* @param conflictBehavior the expected behavior if an equivalently-named child
already exists at the <code>into</code>
* location, or null if the default conflict behavior should be used
- * @param uuidConflictBehavior the expected behavior if a node with the same UUID as
any of the nodes in the branch under the
- * {@code from} location in the {@code fromWorkspace} workspace already exists
in the workspace
* @return the request; never null
* @throws IllegalArgumentException if either of the locations or workspace names are
null
*/
@@ -442,14 +441,39 @@
Location into,
String intoWorkspace,
Name nameForCopy,
- NodeConflictBehavior conflictBehavior,
- UuidConflictBehavior uuidConflictBehavior) {
+ NodeConflictBehavior conflictBehavior ) {
if (conflictBehavior == null) conflictBehavior =
CopyBranchRequest.DEFAULT_NODE_CONFLICT_BEHAVIOR;
- return process(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForCopy, conflictBehavior,
- uuidConflictBehavior));
+ return process(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForCopy, conflictBehavior));
}
/**
+ * Add a request to clone a branch to another.
+ *
+ * @param from the location of the top node in the existing branch that is to be
cloned
+ * @param fromWorkspace the name of the workspace where the
<code>from</code> node exists
+ * @param into the location of the existing node into which the clone should be
placed
+ * @param intoWorkspace the name of the workspace where the
<code>into</code> node is to be cloned
+ * @param nameForClone the desired name for the node that results from the clone, or
null if the name of the original should
+ * be used
+ * @param exactSegmentForClone the exact {@link Path.Segment segment} at which the
cloned tree should be rooted.
+ * @param removeExisting whether any nodes in the intoWorkspace with the same UUIDs
as a node in the source branch should be
+ * removed (if true) or a {@link UuidAlreadyExistsException} should be
thrown.
+ * @return the request; never null
+ * @throws IllegalArgumentException if any of the parameters are null except for
{@code nameForClone} or {@code
+ * exactSegmentForClone}. Exactly one of {@code nameForClone} and {@code
exactSegmentForClone} must be null.
+ */
+ public CloneBranchRequest cloneBranch( Location from,
+ String fromWorkspace,
+ Location into,
+ String intoWorkspace,
+ Name nameForClone,
+ Path.Segment exactSegmentForClone,
+ boolean removeExisting ) {
+ return process(new CloneBranchRequest(from, fromWorkspace, into, intoWorkspace,
nameForClone, exactSegmentForClone,
+ removeExisting));
+ }
+
+ /**
* 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
Deleted:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UuidConflictBehavior.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UuidConflictBehavior.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UuidConflictBehavior.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -1,14 +0,0 @@
-package org.jboss.dna.graph.request;
-
-/**
- * An enumeration used by {@link CopyBranchRequest} for the choice of handling duplicate
UUIDs, such as when a node is to be
- * copied to another location where a node already exists.
- *
- * @author Randall Hauch
- */
-public enum UuidConflictBehavior {
-
- ALWAYS_CREATE_NEW_UUID,
- REPLACE_EXISTING_NODE,
- THROW_EXCEPTION;
-}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -26,6 +26,7 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -115,6 +116,18 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ logger.log(level, GraphI18n.executingRequest, request);
+ delegate.process(request);
+ logger.log(level, GraphI18n.executedRequest, request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -44,6 +44,7 @@
import org.jboss.dna.graph.property.ReferentialIntegrityException;
import org.jboss.dna.graph.request.CacheableRequest;
import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -253,6 +254,8 @@
process((GetWorkspacesRequest)request);
} else if (request instanceof CreateWorkspaceRequest) {
process((CreateWorkspaceRequest)request);
+ } else if (request instanceof CloneBranchRequest) {
+ process((CloneBranchRequest)request);
} else if (request instanceof CloneWorkspaceRequest) {
process((CloneWorkspaceRequest)request);
} else if (request instanceof DestroyWorkspaceRequest) {
@@ -358,6 +361,16 @@
public abstract void process( CreateWorkspaceRequest request );
/**
+ * Process a request to clone a branch into a new workspace.
+ * <p>
+ * This method does nothing if the request is null.
+ * </p>
+ *
+ * @param request the request
+ */
+ public abstract void process( CloneBranchRequest request );
+
+ /**
* Process a request to clone an existing workspace as a new workspace.
* <p>
* This method does nothing if the request is null.
@@ -786,7 +799,7 @@
public void close() {
// Publish any changes ...
if (observer != null && !this.changes.isEmpty()) {
- String userName = context.getSecurityContext() != null ?
context.getSecurityContext().getUserName() : null;
+ String userName = context.getSecurityContext() != null ?
context.getSecurityContext().getUserName() : null;
Changes changes = new Changes(userName, getSourceName(), getNowInUtc(),
this.changes);
observer.notify(changes);
}
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-06-27
02:54:06 UTC (rev 1065)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-06-28
00:33:32 UTC (rev 1066)
@@ -64,6 +64,8 @@
unableToCopyToTheRoot = Unable to copy node "{0}" to "{1}" since the
desired location is the root node
actualLocationIsNotSameAsInputLocation = The actual location of {0} is not the same as
the current location of {1}
actualLocationIsNotChildOfInputLocation = The actual location of {0} is not a child of
the specified location {1}
+actualLocationIsNotAtCorrectChildSegment = The last segment of the actual location of {0}
does not have the requested child segment {1}
+actualLocationDoesNotHaveCorrectChildName = The last segment of the actual location of
{0} does not have the requested child name {1}
actualLocationMustHavePath = The actual location of {0} must have a path
actualNewLocationIsNotSameAsInputLocation = The actual new location of {0} is not the
same as the input location of {1}
actualNewLocationMustHavePath = The actual new location of {0} must have a path
@@ -99,8 +101,9 @@
federatedSourceDoesNotSupportCloningWorkspaces = {0} is a source that does not allow
cloning workspaces
federatedSourceDoesNotSupportDestroyingWorkspaces = {0} is a source that does not allow
destroying workspaces
unableToProjectSourceInformationIntoWorkspace = Unable to project source information at
{0} in the "{1} federated repository source using projection {2}
-unableToCreateNodeUnderPlaceholder = Unable to create node "{0}" under {1} in
the "{2}" workspace of the "{3}" federarted repository because the
parent is a placeholder
-unableToUpdatePlaceholder = Unable to update node {0} in the "{1}" workspace of
the "{2}" federarted repository because the node is a placeholder
-unableToDeletePlaceholder = Unable to delete node {0} in the "{1}" workspace of
the "{2}" federarted repository because the node is a placeholder
-copyLimitedToBeWithinSingleSource = Unable to copy {0} in the "{1}" workspace
of the "{3}" federarted repository into {2} in the "{3}" workspace:
copy is only supported when the original and new locations are within the same source
-moveLimitedToBeWithinSingleSource = Unable to move {0} in the "{1}" workspace
of the "{3}" federarted repository into {2} in the "{3}" workspace:
move is only supported when the original and new locations are within the same source
+unableToCreateNodeUnderPlaceholder = Unable to create node "{0}" under {1} in
the "{2}" workspace of the "{3}" federated repository because the
parent is a placeholder
+unableToUpdatePlaceholder = Unable to update node {0} in the "{1}" workspace of
the "{2}" federated repository because the node is a placeholder
+unableToDeletePlaceholder = Unable to delete node {0} in the "{1}" workspace of
the "{2}" federated repository because the node is a placeholder
+copyLimitedToBeWithinSingleSource = Unable to copy {0} in the "{1}" workspace
of the "{3}" federated repository into {2} in the "{3}" workspace:
copy is only supported when the original and new locations are within the same source
+moveLimitedToBeWithinSingleSource = Unable to move {0} in the "{1}" workspace
of the "{3}" federated repository into {2} in the "{3}" workspace:
move is only supported when the original and new locations are within the same source
+cloneLimitedToBeWithinSingleSource = Unable to clone {0} in the "{1}" workspace
of the "{3}" federated repository into {2} in the "{3}" workspace:
clone is only supported when the original and new locations are within the same source
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 2009-06-27 02:54:06
UTC (rev 1065)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2009-06-28 00:33:32
UTC (rev 1066)
@@ -54,6 +54,7 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -72,7 +73,6 @@
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.SetPropertyRequest;
import org.jboss.dna.graph.request.UpdatePropertiesRequest;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
@@ -191,22 +191,14 @@
protected void assertNextRequestIsCopy( String fromWorkspace,
Location from,
Location to ) {
- assertNextRequestIsCopy(fromWorkspace, from, to,
UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID);
- }
-
- protected void assertNextRequestIsCopy( String fromWorkspace,
- Location from,
- Location to,
- UuidConflictBehavior uuidConflictBehavior) {
Request request = executedRequests.poll();
assertThat(request, is(instanceOf(CopyBranchRequest.class)));
CopyBranchRequest copy = (CopyBranchRequest)request;
assertThat(copy.fromWorkspace(), is(fromWorkspace));
assertThat(copy.from(), is(from));
assertThat(copy.into(), is(to));
- assertThat(copy.uuidConflictBehavior(), is(uuidConflictBehavior));
}
-
+
protected void assertNextRequestIsDelete( Location at ) {
Request request = executedRequests.poll();
assertThat(request, is(instanceOf(DeleteBranchRequest.class)));
@@ -1112,6 +1104,23 @@
}
@Override
+ public void process( CloneBranchRequest request ) {
+ // Create a child under the new parent ...
+ if (request.into().hasPath()) {
+ Name childName = request.desiredName();
+ if (childName == null) childName = request.desiredSegment().getName();
+
+ Path childPath =
context.getValueFactories().getPathFactory().create(request.into().getPath(), childName);
+ Location newChild = actualLocationOf(Location.create(childPath));
+ // Just update the actual location
+ request.setActualLocations(actualLocationOf(request.from()), newChild);
+ } else {
+ // Just update the actual location
+ request.setActualLocations(actualLocationOf(request.from()),
actualLocationOf(request.into()));
+ }
+ }
+
+ @Override
public void process( CreateNodeRequest request ) {
// Just update the actual location ...
Location parent = actualLocationOf(request.under()); // just make sure it has
a path ...
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -25,6 +25,7 @@
import java.util.Queue;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -136,6 +137,16 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ recordChange(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
*/
@Override
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -27,6 +27,7 @@
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
import static org.jboss.dna.graph.IsNodeWithChildren.hasChildren;
import static org.jboss.dna.graph.IsNodeWithChildren.isEmpty;
@@ -44,9 +45,9 @@
import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.connector.UuidAlreadyExistsException;
import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.property.Reference;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
/**
@@ -75,14 +76,6 @@
IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum3.txt"))};
}
- protected void tryCreatingAWorkspaceNamed( String workspaceName ) {
- try {
- graph.createWorkspace().named(workspaceName);
- } catch (Exception ex) {
- assumeNoException(ex);
- }
- }
-
/**
* These tests require that the source supports updates, since all of the tests do
some form of updates.
*/
@@ -308,7 +301,6 @@
createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, description);
}
- @Ignore
@Test
public void shouldCreateTreeWith10ChildrenAnd2LevelsDeepUsingIndividualRequests() {
String initialPath = "";
@@ -320,7 +312,6 @@
createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
}
- @Ignore
@Test
public void shouldCreateTreeWith10ChildrenAnd2LevelsDeepUsingOneBatch() {
String initialPath = "";
@@ -332,7 +323,6 @@
createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
}
- @Ignore
@Test
public void shouldCreateTreeWith10ChildrenAnd3LevelsDeepUsingOneBatch() {
String initialPath = "";
@@ -839,7 +829,7 @@
graph.useWorkspace(defaultWorkspaceName);
graph.create("/newUuids");
-
graph.copy("/node1").withNewUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+
graph.copy("/node1").fromWorkspace(workspaceName).to("/newUuids/node1");
/*
* Focus on testing node structure, since shouldCopyNodeWithChildren tests that
properties get copied
@@ -858,7 +848,7 @@
}
@Test
- public void shouldCopyChildrenBetweenWorkspacesPreservingUuids() throws Exception {
+ public void shouldNotCloneChildrenIfUuidConflictAndFailureBehavior() throws Exception
{
String defaultWorkspaceName = graph.getCurrentWorkspaceName();
String workspaceName = "copyChildrenSource";
@@ -873,31 +863,27 @@
boolean batch = true;
createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
- Subgraph source = graph.getSubgraphOfDepth(3).at("/node1");
-
graph.useWorkspace(defaultWorkspaceName);
graph.create("/newUuids");
-
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+ // Copy once to get the UUID into the default workspace
+
//graph.copy("/node1/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/node1");
+
graph.clone("/node1/node1/node1").fromWorkspace(workspaceName).as(name("node1")).into("/newUuids").failingIfAnyUuidsMatch();
- /*
- * Focus on testing node structure, since shouldCopyNodeWithChildren tests that
properties get copied
- */
- Subgraph target = graph.getSubgraphOfDepth(3).at("/newUuids/node1");
- assertThat(target, is(notNullValue()));
- assertThat(target.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
- assertThat(uuidFor(target.getNode(".")),
is(uuidFor(source.getNode("."))));
- assertThat(target.getNode("node1").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
- assertThat(uuidFor(target.getNode("node1")),
is(uuidFor(source.getNode("node1"))));
- assertThat(target.getNode("node2").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
- assertThat(uuidFor(target.getNode("node2")),
is(uuidFor(source.getNode("node2"))));
- assertThat(target.getNode("node3").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
- assertThat(uuidFor(target.getNode("node3")),
is(uuidFor(source.getNode("node3"))));
+ try {
+ // Copy again to get the exception since the UUID is already in the default
workspace
+ //
graph.copy("/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/shouldNotWork");
+
graph.clone("/node1/node1/node1").fromWorkspace(workspaceName).as(name("shouldNotWork")).into("/newUuids").failingIfAnyUuidsMatch();
+ fail("Should not be able to copy a node into a workspace if another node
with the "
+ + "same UUID already exists in the workspace and UUID behavior is
failingIfUuidsMatch");
+ } catch (UuidAlreadyExistsException ex) {
+ // Expected
+ }
}
@Test
- public void shouldNotCopyChildrenBetweenWorkspacesIfUuidConflictAndFailureBehavior()
throws Exception {
+ public void shouldCloneChildrenAndRemoveExistingNodesWithSameUuidIfSpecified() throws
Exception {
String defaultWorkspaceName = graph.getCurrentWorkspaceName();
String workspaceName = "copyChildrenSource";
@@ -912,25 +898,56 @@
boolean batch = true;
createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ Subgraph source = graph.getSubgraphOfDepth(3).at("/node1");
+
graph.useWorkspace(defaultWorkspaceName);
graph.create("/newUuids");
// Copy once to get the UUID into the default workspace
-
graph.copy("/node1/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/node1");
+ //
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(name("node1")).into("/newUuids").replacingExistingNodesWithSameUuids();
+
+ // Make sure that the node wasn't moved by the clone
+ graph.useWorkspace(workspaceName);
+ graph.getNodeAt("/node1");
+ graph.useWorkspace(defaultWorkspaceName);
+
+ // Create a new child node that in the target workspace that has no corresponding
node in the source workspace
+ graph.create("/newUuids/node1/shouldBeRemoved");
+
+ // Copy again to test the behavior now that the UUIDs are already in the default
workspace
+ // This should remove /newUuids/node1/shouldBeRemoved
+ //
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/otherNode");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(name("otherNode")).into("/newUuids").replacingExistingNodesWithSameUuids();
+ /*
+ * Focus on testing node structure, since shouldCopyNodeWithChildren tests that
properties get copied
+ */
+ // /newUuids/node1 should have been removed when the new node was added with the
same UUID
+ assertThat(graph.getNodeAt("/newUuids").getChildren(),
hasChildren(segment("otherNode")));
+
try {
- // Copy again to get the exception since the UUID is already in the default
workspace
-
graph.copy("/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/shouldNotWork");
- fail("Should not be able to copy a node into a workspace if another node
with the "
- + "same UUID already exists in the workspace and UUID behavior is
failingIfUuidsMatch");
- } catch (UuidAlreadyExistsException ex) {
+ graph.getNodeAt("/newUuids/node1/shouldBeRemoved");
+ fail("/newUuids/node1/shouldBeRemoved should no longer exist after the
copy-with-remove-conflicting-uuids operation");
+ }
+ catch (PathNotFoundException pnfe) {
// Expected
}
-
+
+ Subgraph target =
graph.getSubgraphOfDepth(3).at("/newUuids/otherNode");
+ assertThat(target, is(notNullValue()));
+ assertThat(target.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(uuidFor(target.getNode(".")),
is(uuidFor(source.getNode("."))));
+ assertThat(target.getNode("node1").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(uuidFor(target.getNode("node1")),
is(uuidFor(source.getNode("node1"))));
+ assertThat(target.getNode("node2").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(uuidFor(target.getNode("node2")),
is(uuidFor(source.getNode("node2"))));
+ assertThat(target.getNode("node3").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(uuidFor(target.getNode("node3")),
is(uuidFor(source.getNode("node3"))));
}
@Test
- public void
shouldCopyChildrenBetweenWorkspacesAndRemoveExistingNodesWithSameUuidIfSpecified() throws
Exception {
+ public void shouldCloneNodeIntoExactLocationIfSpecified() throws Exception {
String defaultWorkspaceName = graph.getCurrentWorkspaceName();
String workspaceName = "copyChildrenSource";
@@ -949,32 +966,27 @@
graph.useWorkspace(defaultWorkspaceName);
- graph.create("/newUuids");
+ graph.create("/segmentTestUuids");
// Copy once to get the UUID into the default workspace
-
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(name("node1")).into("/segmentTestUuids").failingIfAnyUuidsMatch();
// Create a new child node that in the target workspace that has no corresponding
node in the source workspace
- graph.create("/newUuids/node1/shouldBeRemoved");
-
+ PropertyFactory propFactory = context.getPropertyFactory();
+ graph.create("/segmentTestUuids/node1",
propFactory.create(name("identifier"), "backup copy"));
+
// Copy again to test the behavior now that the UUIDs are already in the default
workspace
// This should remove /newUuids/node1/shouldBeRemoved
-
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/otherNode");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(segment("node1[1]")).into("/segmentTestUuids").replacingExistingNodesWithSameUuids();
/*
* Focus on testing node structure, since shouldCopyNodeWithChildren tests that
properties get copied
*/
// /newUuids/node1 should have been removed when the new node was added with the
same UUID
- assertThat(graph.getNodeAt("/newUuids").getChildren(),
hasChildren(segment("otherNode")));
-
- try {
- graph.getNodeAt("/newUuids/node1/shouldBeRemoved");
- fail("/newUuids/node1/shouldBeRemoved should no longer exist after the
copy-with-remove-conflicting-uuids operation");
- }
- catch (PathNotFoundException pnfe) {
- // Expected
- }
+ assertThat(graph.getNodeAt("/segmentTestUuids").getChildren(),
hasChildren(segment("node1"), segment("node1[2]")));
+
assertThat(graph.getNodeAt("/segmentTestUuids/node1[1]").getProperty("identifier"),
is(nullValue()));
+
assertThat(graph.getNodeAt("/segmentTestUuids/node1[2]").getProperty("identifier").getFirstValue().toString(),
is("backup copy"));
- Subgraph target =
graph.getSubgraphOfDepth(3).at("/newUuids/otherNode");
+ Subgraph target =
graph.getSubgraphOfDepth(3).at("/segmentTestUuids/node1[1]");
assertThat(target, is(notNullValue()));
assertThat(target.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
assertThat(uuidFor(target.getNode(".")),
is(uuidFor(source.getNode("."))));
@@ -985,7 +997,15 @@
assertThat(target.getNode("node3").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
assertThat(uuidFor(target.getNode("node3")),
is(uuidFor(source.getNode("node3"))));
}
-
+
+ protected void tryCreatingAWorkspaceNamed( String workspaceName ) {
+ try {
+ graph.createWorkspace().named(workspaceName);
+ } catch (Exception ex) {
+ assumeNoException(ex);
+ }
+ }
+
private UUID uuidFor( Node node ) {
return (UUID)node.getProperty(DnaLexicon.UUID).getFirstValue();
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-06-27 02:54:06
UTC (rev 1065)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-06-28 00:33:32
UTC (rev 1066)
@@ -717,8 +717,7 @@
JcrNodeDefinition match =
this.cache.nodeTypes().findChildNodeDefinition(mixinCandidateType.getInternalName(),
Collections.<Name>emptyList(),
nodeName,
-
childNode.getPrimaryNodeType()
-
.getInternalName(),
+
childNode.getPrimaryNodeType().getInternalName(),
snsCount,
false);
@@ -1408,7 +1407,6 @@
*
* @see javax.jcr.Node#update(java.lang.String)
*/
- @SuppressWarnings( "unused" )
public final void update( String srcWorkspaceName ) throws NoSuchWorkspaceException,
RepositoryException {
CheckArg.isNotNull(srcWorkspaceName, "workspace name");
@@ -1424,7 +1422,10 @@
return;
}
- if (true) throw new UnsupportedOperationException();
+ // Need to force remove in case this node is not referenceable
+ this.session().workspace().clone(srcWorkspaceName, correspondingPath, path(),
true, true);
+
+ session().refresh(false);
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-06-27 02:54:06
UTC (rev 1065)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-06-28 00:33:32
UTC (rev 1066)
@@ -304,6 +304,17 @@
throw new AccessDeniedException(ace);
}
+ clone(srcWorkspace, srcPath, destPath, removeExisting, false);
+ }
+
+ void clone( String srcWorkspace,
+ Path srcPath,
+ Path destPath,
+ boolean removeExisting,
+ boolean destPathIncludesSegment )
+ throws ConstraintViolationException, VersionException, AccessDeniedException,
PathNotFoundException, ItemExistsException,
+ LockException, RepositoryException {
+
/*
* Make sure that the node has a definition at the new location
*/
@@ -332,6 +343,7 @@
primaryTypeProp = props.get(JcrLexicon.PRIMARY_TYPE);
uuidProp = props.get(DnaLexicon.UUID);
} catch (org.jboss.dna.graph.property.PathNotFoundException pnfe) {
+ String srcAbsPath = srcPath.getString(this.context.getNamespaceRegistry());
throw new PathNotFoundException(JcrI18n.itemNotFoundAtPath.text(srcAbsPath,
srcWorkspace));
} finally {
graph.useWorkspace(this.name);
@@ -391,12 +403,19 @@
}
}
- // Perform the copy operation, but use the "to" form (not the
"into", which takes the parent) ...
-
graph.copy(srcPath).replacingExistingNodesWithSameUuids().fromWorkspace(srcWorkspace).to(destPath);
+ if (destPathIncludesSegment) {
+
graph.clone(srcPath).fromWorkspace(srcWorkspace).as(destPath.getLastSegment()).into(destPath.getParent()).replacingExistingNodesWithSameUuids();
+ } else {
+
graph.clone(srcPath).fromWorkspace(srcWorkspace).as(newNodeName).into(destPath.getParent()).replacingExistingNodesWithSameUuids();
+ }
} else {
try {
- // Perform the copy operation, but use the "to" form (not the
"into", which takes the parent) ...
-
graph.copy(srcPath).failingIfUuidsMatch().fromWorkspace(srcWorkspace).to(destPath);
+ if (destPathIncludesSegment) {
+
graph.clone(srcPath).fromWorkspace(srcWorkspace).as(destPath.getLastSegment()).into(destPath.getParent()).replacingExistingNodesWithSameUuids();
+ } else {
+
graph.clone(srcPath).fromWorkspace(srcWorkspace).as(newNodeName).into(destPath.getParent()).failingIfAnyUuidsMatch();
+ }
+
} catch (UuidAlreadyExistsException uaee) {
throw new ItemExistsException(uaee.getMessage());
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-06-27 02:54:06 UTC
(rev 1065)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-06-28 00:33:32 UTC
(rev 1066)
@@ -37,6 +37,7 @@
import org.apache.jackrabbit.test.api.NodeItemIsNewTest;
import org.apache.jackrabbit.test.api.NodeOrderableChildNodesTest;
import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.NodeTest;
import org.apache.jackrabbit.test.api.PropertyItemIsModifiedTest;
import org.apache.jackrabbit.test.api.PropertyItemIsNewTest;
import org.apache.jackrabbit.test.api.PropertyTest;
@@ -178,7 +179,7 @@
// addTestSuite(ReferencesTest.class);
addTestSuite(SessionTest.class);
// addTestSuite(SessionUUIDTest.class);
- // addTestSuite(NodeTest.class);
+ addTestSuite(NodeTest.class);
// addTestSuite(NodeUUIDTest.class);
addTestSuite(NodeOrderableChildNodesTest.class);
addTestSuite(PropertyTest.class);
Modified:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -62,6 +62,7 @@
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -324,8 +325,8 @@
}
// Delete in the cache ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.at(),
workspace.getCacheProjection()
-
.getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.at(),
+
workspace.getCacheProjection().getWorkspaceName());
executeInCache(cacheRequest, workspace);
}
@@ -367,8 +368,7 @@
Location intoLocation = Location.create(intoProjection.pathInSource);
String workspaceName = fromProjection.projection.getWorkspaceName();
CopyBranchRequest sourceRequest = new CopyBranchRequest(fromLocation,
workspaceName, intoLocation, workspaceName,
- request.desiredName(),
request.nodeConflictBehavior(),
-
request.uuidConflictBehavior());
+ request.desiredName(),
request.nodeConflictBehavior());
execute(sourceRequest, fromProjection.projection);
// Copy/transform the results ...
@@ -380,14 +380,70 @@
}
// Delete from the cache the parent of the new location ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(),
fromWorkspace.getCacheProjection()
-
.getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(),
+
fromWorkspace.getCacheProjection().getWorkspaceName());
executeInCache(cacheRequest, fromWorkspace);
}
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ FederatedWorkspace fromWorkspace = getWorkspace(request,
request.fromWorkspace());
+ if (fromWorkspace == null) return;
+ FederatedWorkspace intoWorkspace = getWorkspace(request,
request.intoWorkspace());
+ if (intoWorkspace == null) return;
+ if (!fromWorkspace.equals(intoWorkspace)) {
+ // Otherwise there wasn't a single projection with a single path ...
+ String msg =
FederationI18n.unableToPerformOperationSpanningWorkspaces.text(fromWorkspace.getName(),
+
intoWorkspace.getName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ // Can push this down if and only if the entire request is within a single
federated source ...
+ SingleProjection fromProjection = asSingleProjection(fromWorkspace,
request.from(), request);
+ if (fromProjection == null) return;
+ SingleProjection intoProjection = asSingleProjection(intoWorkspace,
request.into(), request);
+ if (intoProjection == null) return;
+ if (!intoProjection.projection.equals(fromProjection.projection)) {
+ // Otherwise there wasn't a single projection with a single path ...
+ String msg =
FederationI18n.unableToPerformOperationUnlessLocationsAreFromSingleProjection.text(request.from(),
+
request.into(),
+
fromWorkspace.getName(),
+
fromProjection.projection.getRules(),
+
intoProjection.projection.getRules());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ // Push down the request ...
+ Location fromLocation = Location.create(fromProjection.pathInSource);
+ Location intoLocation = Location.create(intoProjection.pathInSource);
+ String workspaceName = fromProjection.projection.getWorkspaceName();
+ CloneBranchRequest sourceRequest = new CloneBranchRequest(fromLocation,
workspaceName, intoLocation, workspaceName,
+ request.desiredName(),
request.desiredSegment(),
+
request.removeExisting());
+ execute(sourceRequest, fromProjection.projection);
+
+ // Copy/transform the results ...
+ if (sourceRequest.hasError()) {
+ request.setError(sourceRequest.getError());
+ } else {
+
request.setActualLocations(fromProjection.convertToRepository(sourceRequest.getActualLocationBefore()),
+
intoProjection.convertToRepository(sourceRequest.getActualLocationAfter()));
+ }
+
+ // Delete from the cache the parent of the new location ...
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(),
+
fromWorkspace.getCacheProjection().getWorkspaceName());
+ executeInCache(cacheRequest, fromWorkspace);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
*/
@Override
@@ -411,14 +467,13 @@
}
SingleProjection beforeProjection = request.before() != null ?
asSingleProjection(workspace, request.before(), request) : null;
-
// Push down the request ...
Location fromLocation = Location.create(fromProjection.pathInSource);
Location intoLocation = Location.create(intoProjection.pathInSource);
Location beforeLocation = beforeProjection != null ?
Location.create(beforeProjection.pathInSource) : null;
String workspaceName = fromProjection.projection.getWorkspaceName();
- MoveBranchRequest sourceRequest = new MoveBranchRequest(fromLocation,
intoLocation, beforeLocation, workspaceName, request.desiredName(),
-
request.conflictBehavior());
+ MoveBranchRequest sourceRequest = new MoveBranchRequest(fromLocation,
intoLocation, beforeLocation, workspaceName,
+ request.desiredName(),
request.conflictBehavior());
execute(sourceRequest, fromProjection.projection);
// Copy/transform the results ...
@@ -429,8 +484,8 @@
intoProjection.convertToRepository(sourceRequest.getActualLocationAfter()));
}
// Delete from the cache ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.from(),
workspace.getCacheProjection()
-
.getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.from(),
+
workspace.getCacheProjection().getWorkspaceName());
executeInCache(cacheRequest, workspace);
// Mark the new parent node as being expired ...
cacheRequest = new DeleteBranchRequest(request.into(),
workspace.getCacheProjection().getWorkspaceName());
@@ -465,8 +520,8 @@
}
// Update the cache ...
- UpdatePropertiesRequest cacheRequest = new UpdatePropertiesRequest(request.on(),
workspace.getCacheProjection()
-
.getWorkspaceName(),
+ UpdatePropertiesRequest cacheRequest = new UpdatePropertiesRequest(request.on(),
+
workspace.getCacheProjection().getWorkspaceName(),
request.properties());
executeInCache(cacheRequest, workspace);
}
@@ -1151,9 +1206,9 @@
readable(registry, create.properties()));
} else if (request instanceof UpdatePropertiesRequest) {
UpdatePropertiesRequest update = (UpdatePropertiesRequest)request;
- logger.trace(" updating {0} with properties {1}",
update.on().getString(registry), readable(registry,
-
update.properties()
-
.values()));
+ logger.trace(" updating {0} with properties {1}",
+ update.on().getString(registry),
+ readable(registry, update.properties().values()));
} else {
logger.trace(" " + request.toString());
}
@@ -1172,9 +1227,9 @@
readable(registry, create.properties()));
} else if (request instanceof UpdatePropertiesRequest) {
UpdatePropertiesRequest update = (UpdatePropertiesRequest)request;
- logger.trace(" updating {0} with properties {1}",
update.on().getString(registry), readable(registry,
-
update.properties()
-
.values()));
+ logger.trace(" updating {0} with properties {1}",
+ update.on().getString(registry),
+ readable(registry, update.properties().values()));
} else {
logger.trace(" " + request.toString());
}
Modified:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -47,6 +47,7 @@
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -283,6 +284,16 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ updatesAllowed(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
*/
@Override
Modified:
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcRequestProcesor.java
===================================================================
---
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcRequestProcesor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcRequestProcesor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -28,6 +28,7 @@
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -100,6 +101,15 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
*/
@Override
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -82,6 +82,7 @@
import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -102,7 +103,6 @@
import org.jboss.dna.graph.request.ReadPropertyRequest;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.UpdatePropertiesRequest;
-import org.jboss.dna.graph.request.UuidConflictBehavior;
import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -132,6 +132,12 @@
protected final boolean enforceReferentialIntegrity;
private final Set<Long> workspaceIdsWithChangedReferences = new
HashSet<Long>();
+ private enum UuidConflictBehavior {
+ ALWAYS_CREATE_NEW_UUID,
+ REPLACE_EXISTING_NODE,
+ THROW_EXCEPTION
+ }
+
/**
* @param sourceName
* @param context
@@ -1215,7 +1221,7 @@
intoWorkspace,
original,
actualNewParent,
- request.uuidConflictBehavior(),
+
UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
desiredName,
originalToNewUuid,
addedLocations,
@@ -1236,7 +1242,7 @@
intoWorkspace,
original,
actualNewParent,
- request.uuidConflictBehavior(),
+ UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
desiredName,
originalToNewUuid,
addedLocations,
@@ -1336,8 +1342,202 @@
entities.persist(copy);
}
entities.flush();
+ } finally {
+ // Close and release the temporary data used for this operation ...
+ query.close();
+ }
- if (request.uuidConflictBehavior() ==
UuidConflictBehavior.REPLACE_EXISTING_NODE) {
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ request.setActualLocations(actualFromLocation, actualToLocation);
+ recordChange(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ logger.trace(request.toString());
+ Location actualFromLocation = null;
+ Location actualToLocation = null;
+ try {
+ // Find the workspaces ...
+ WorkspaceEntity fromWorkspace = getExistingWorkspace(request.fromWorkspace(),
request);
+ if (fromWorkspace == null) return;
+ WorkspaceEntity intoWorkspace = getExistingWorkspace(request.intoWorkspace(),
request);
+ if (intoWorkspace == null) return;
+ Long fromWorkspaceId = fromWorkspace.getId();
+ Long intoWorkspaceId = intoWorkspace.getId();
+ assert fromWorkspaceId != null;
+ assert intoWorkspaceId != null;
+
+ Location fromLocation = request.from();
+ ActualLocation actualFrom = getActualLocation(fromWorkspace, fromLocation);
+ actualFromLocation = actualFrom.location;
+ Path fromPath = actualFromLocation.getPath();
+
+ Location newParentLocation = request.into();
+ ActualLocation actualNewParent = getActualLocation(intoWorkspace,
newParentLocation);
+ assert actualNewParent != null;
+
+ // Create a map that we'll use to record the new UUID for each of the
original nodes ...
+ Map<String, String> originalToNewUuid = new HashMap<String,
String>();
+
+ // Compute the subgraph, including the top node in the subgraph ...
+ SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
+ entities,
+ fromWorkspaceId,
+ actualFromLocation.getUuid(),
+ fromPath,
+ 0);
+
+ UuidConflictBehavior conflictBehavior = request.removeExisting() ?
UuidConflictBehavior.REPLACE_EXISTING_NODE : UuidConflictBehavior.THROW_EXCEPTION;
+ try {
+ // Walk through the original nodes, creating new ChildEntity object
(i.e., copy) for each original ...
+ List<ChildEntity> originalNodes = query.getNodes(true, true);
+ Iterator<ChildEntity> originalIter = originalNodes.iterator();
+ Map<String, ChildEntity> addedLocations = new HashMap<String,
ChildEntity>();
+ Map<String, Location> deletedLocations = new HashMap<String,
Location>();
+
+ // Start with the original (top-level) node first, since we need to add
it to the list of children ...
+ if (originalIter.hasNext()) {
+ ChildEntity original = originalIter.next();
+
+ Name desiredName = request.desiredName();
+ if (desiredName == null) desiredName =
fromPath.getLastSegment().getName();
+ actualToLocation = this.copyNode(entities,
+ fromWorkspace,
+ intoWorkspace,
+ original,
+ actualNewParent,
+ conflictBehavior,
+ desiredName,
+ originalToNewUuid,
+ addedLocations,
+ deletedLocations).location;
+ }
+
+ // Now create copies of all children in the subgraph.
+ while (originalIter.hasNext()) {
+ ChildEntity original = originalIter.next();
+ String newParentUuidOfCopy =
originalToNewUuid.get(original.getParentUuidString());
+ assert newParentUuidOfCopy != null;
+
+ actualNewParent = getActualLocation(intoWorkspace,
Location.create(UUID.fromString(newParentUuidOfCopy)));
+
+ Name desiredName =
nameFactory.create(original.getChildNamespace().getUri(), original.getChildName());
+ this.copyNode(entities,
+ fromWorkspace,
+ intoWorkspace,
+ original,
+ actualNewParent,
+ conflictBehavior,
+ desiredName,
+ originalToNewUuid,
+ addedLocations,
+ deletedLocations);
+ }
+ entities.flush();
+
+ Set<String> newNodesWithReferenceProperties = new
HashSet<String>();
+ // Now create copies of all the intra-subgraph references, replacing the
UUIDs on both ends ...
+ for (ReferenceEntity reference : query.getInternalReferences()) {
+ String newFromUuid =
originalToNewUuid.get(reference.getId().getFromUuidString());
+ assert newFromUuid != null;
+ String newToUuid =
originalToNewUuid.get(reference.getId().getToUuidString());
+ assert newToUuid != null;
+ ReferenceEntity copy = new ReferenceEntity(new
ReferenceId(intoWorkspaceId, newFromUuid, newToUuid));
+ entities.persist(copy);
+ newNodesWithReferenceProperties.add(newFromUuid);
+ }
+
+ // Now create copies of all the references owned by the subgraph but
pointing to non-subgraph nodes,
+ // so we only replaced the 'from' UUID ...
+ for (ReferenceEntity reference : query.getOutwardReferences()) {
+ String oldToUuid = reference.getId().getToUuidString();
+ String newFromUuid =
originalToNewUuid.get(reference.getId().getFromUuidString());
+ assert newFromUuid != null;
+
+ ActualLocation refTargetLocation = getActualLocation(intoWorkspace,
+
Location.create(UUID.fromString(oldToUuid)));
+ if (refTargetLocation == null) {
+ // Some of the references that remain will be invalid, since they
point to nodes that
+ // have just been deleted. Build up the information necessary to
produce a useful exception ...
+ ValueFactory<Reference> refFactory =
getExecutionContext().getValueFactories().getReferenceFactory();
+ Map<Location, List<Reference>> invalidRefs = new
HashMap<Location, List<Reference>>();
+ UUID fromUuid =
UUID.fromString(reference.getId().getFromUuidString());
+ ActualLocation actualRefFromLocation =
getActualLocation(intoWorkspace, Location.create(fromUuid));
+ Location refFromLocation = actualRefFromLocation.location;
+ List<Reference> refs = invalidRefs.get(fromLocation);
+ if (refs == null) {
+ refs = new ArrayList<Reference>();
+ invalidRefs.put(refFromLocation, refs);
+ }
+ UUID toUuid = UUID.fromString(oldToUuid);
+ refs.add(refFactory.create(toUuid));
+
+ String msg =
JpaConnectorI18n.invalidReferences.text(reference.getId().getFromUuidString());
+ throw new ReferentialIntegrityException(invalidRefs, msg);
+ }
+
+ ReferenceEntity copy = new ReferenceEntity(new
ReferenceId(intoWorkspaceId, newFromUuid, oldToUuid));
+ entities.persist(copy);
+ newNodesWithReferenceProperties.add(newFromUuid);
+ }
+
+ Set<PropertiesEntity> addedProps = new
HashSet<PropertiesEntity>();
+ // Now process the properties, creating a copy (note references are not
changed) ...
+ for (PropertiesEntity original : query.getProperties(true, true)) {
+ // Find the UUID of the copy ...
+ String copyUuid =
originalToNewUuid.get(original.getId().getUuidString());
+ assert copyUuid != null;
+
+ // Create the copy ...
+ boolean compressed = original.isCompressed();
+ byte[] originalData = original.getData();
+ NodeId propertiesId = new NodeId(intoWorkspaceId, copyUuid);
+ PropertiesEntity copy = entities.find(PropertiesEntity.class,
propertiesId);
+
+ if (copy == null) {
+ copy = new PropertiesEntity(propertiesId);
+ }
+ copy.setCompressed(compressed);
+ if (newNodesWithReferenceProperties.contains(copyUuid)) {
+
+ // This node has internal or outward references that must be
adjusted ...
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = compressed ? new GZIPOutputStream(baos) :
baos;
+ ObjectOutputStream oos = new ObjectOutputStream(os);
+ ByteArrayInputStream bais = new
ByteArrayInputStream(originalData);
+ InputStream is = compressed ? new GZIPInputStream(bais) : bais;
+ ObjectInputStream ois = new ObjectInputStream(is);
+ try {
+ serializer.adjustReferenceProperties(ois, oos,
originalToNewUuid);
+ } finally {
+ try {
+ ois.close();
+ } finally {
+ oos.close();
+ }
+ }
+ copy.setData(baos.toByteArray());
+ } else {
+ // No references to adjust, so just copy the original data ...
+ copy.setData(originalData);
+ }
+ copy.setPropertyCount(original.getPropertyCount());
+
copy.setReferentialIntegrityEnforced(original.isReferentialIntegrityEnforced());
+ addedProps.add(copy);
+ entities.persist(copy);
+ }
+ entities.flush();
+
+ if (request.removeExisting()) {
/*
* We may have deleted some old copies of nodes and replaced them with
new copies.
* Now we need to clean up any nodes that were descendants of the old
copies of the
Modified:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -92,7 +92,8 @@
graph.create("/newUuids");
// Copy once to get the UUID into the default workspace
-
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+
//graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(name("node1")).into("/newUuids").replacingExistingNodesWithSameUuids();
// Create a new child node that in the target workspace that has no corresponding
node in the source workspace
graph.create("/newUuids/node1/shouldBeRemoved");
@@ -104,6 +105,7 @@
// Copy again to test the behavior now that the UUIDs are already in the default
workspace
// This should fail because /newUuids/node1/shouldBeRemoved must be removed by
the copy, but can't be removed
// because there is a reference to it.
-
graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/otherNode");
+
//graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/otherNode");
+
graph.clone("/node1").fromWorkspace(workspaceName).as(name("otherNode")).into("/newUuids").replacingExistingNodesWithSameUuids();
}
}
Modified:
trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java 2009-06-27
02:54:06 UTC (rev 1065)
+++
trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java 2009-06-28
00:33:32 UTC (rev 1066)
@@ -50,6 +50,7 @@
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
@@ -346,6 +347,16 @@
/**
* {@inheritDoc}
*
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
+ */
+ @Override
+ public void process( CloneBranchRequest request ) {
+ updatesAllowed(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
*/
@Override