Author: bcarothers
Date: 2009-06-20 20:45:14 -0400 (Sat, 20 Jun 2009)
New Revision: 1058
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/DnaTckTest.java
Log:
DNA-370 JcrWorkspace.clone Is Not Implemented
Committed patch to add constraint checking to clone with removeExisting = true.
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-06-21
00:16:03 UTC (rev 1057)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-06-21
00:45:14 UTC (rev 1058)
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.graph.request;
+import java.util.Arrays;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
@@ -209,6 +210,6 @@
@Override
public String toString() {
return "set property " + property().getName() + " on " + on()
+ " in the \"" + workspaceName + "\" workspace to "
- + property().getValuesAsArray();
+ + Arrays.asList(property().getValuesAsArray()).toString();
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-06-21 00:16:03 UTC
(rev 1057)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-06-21 00:45:14 UTC
(rev 1058)
@@ -89,6 +89,7 @@
public static I18n unableToSetSingleValuedPropertyUsingMultipleValues;
public static I18n
unableToRefreshBranchSinceAtLeastOneNodeMovedToParentOutsideOfBranch;
public static I18n allPropertyValuesMustHaveSameType;
+ public static I18n cannotRemoveNodeFromClone;
public static I18n unableToRemoveRootNode;
public static I18n unableToMoveNodeToBeChildOfDecendent;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-06-21 00:16:03 UTC
(rev 1057)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-06-21 00:45:14 UTC
(rev 1058)
@@ -24,6 +24,7 @@
package org.jboss.dna.jcr;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -113,7 +114,7 @@
boolean mixin,
boolean orderableChildNodes ) {
assert context != null;
-
+
this.context = context;
this.nodeTypeManager = nodeTypeManager;
this.name = name;
@@ -140,12 +141,12 @@
JcrNodeType superType = thisAndAllSupertypes.get(i);
for (NodeType superSuperType : superType.getDeclaredSupertypes()) {
JcrNodeType jcrSuperSuperType = (JcrNodeType)superSuperType;
-
+
if (jcrSuperSuperType == null) {
assert JcrNtLexicon.BASE.equals(name);
continue;
}
-
+
if (typeNames.add(jcrSuperSuperType.getInternalName())) {
thisAndAllSupertypes.add(jcrSuperSuperType);
}
@@ -208,6 +209,17 @@
return allDefinitions.allChildNodeDefinitions(childName);
}
+ JcrNodeDefinition childNodeDefinition( NodeDefinitionId nodeDefnId ) {
+ List<Name> requiredPrimaryTypeNames =
Arrays.asList(nodeDefnId.getRequiredPrimaryTypes());
+ for (JcrNodeDefinition nodeDefn :
allChildNodeDefinitions(nodeDefnId.getChildDefinitionName())) {
+ if (nodeDefn.getRequiredPrimaryTypeNames().size() ==
requiredPrimaryTypeNames.size()
+ &&
nodeDefn.getRequiredPrimaryTypeNames().containsAll(requiredPrimaryTypeNames)) {
+ return nodeDefn;
+ }
+ }
+ return null;
+ }
+
/**
* {@inheritDoc}
*
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-21 00:16:03
UTC (rev 1057)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-06-21 00:45:14
UTC (rev 1058)
@@ -26,12 +26,15 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.AccessControlException;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.jcr.AccessDeniedException;
import javax.jcr.InvalidSerializedDataException;
import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.PathNotFoundException;
@@ -50,6 +53,8 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.connector.RepositorySourceException;
@@ -60,8 +65,10 @@
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.UuidFactory;
import org.jboss.dna.graph.property.ValueFormatException;
import org.jboss.dna.graph.property.basic.GraphNamespaceRegistry;
+import org.jboss.dna.graph.request.ReadBranchRequest;
import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
import org.jboss.dna.jcr.JcrRepository.Option;
@@ -308,12 +315,20 @@
Name newNodeName = destPath.getLastSegment().getName();
String parentPath =
destPath.getParent().getString(this.context.getNamespaceRegistry());
+ Subgraph sourceTree = null;
// This will check for a definition and throw a ConstraintViolationException or
ItemExistsException if none is found
graph.useWorkspace(srcWorkspace);
Property primaryTypeProp;
Property uuidProp;
try {
- Map<Name, Property> props =
graph.getNodeAt(srcPath).getPropertiesByName();
+ Map<Name, Property> props;
+
+ if (removeExisting) {
+ sourceTree =
graph.getSubgraphOfDepth(ReadBranchRequest.NO_MAXIMUM_DEPTH).at(srcPath);
+ props = sourceTree.getRoot().getPropertiesByName();
+ } else {
+ props = graph.getNodeAt(srcPath).getPropertiesByName();
+ }
primaryTypeProp = props.get(JcrLexicon.PRIMARY_TYPE);
uuidProp = props.get(DnaLexicon.UUID);
} catch (org.jboss.dna.graph.property.PathNotFoundException pnfe) {
@@ -331,12 +346,56 @@
newNodeName,
nameFactory.create(primaryTypeProp.getFirstValue()));
- // Perform the copy operation, but use the "to" form (not the
"into", which takes the parent) ...
- // graph.copy(srcPath).fromWorkspace(srcWorkspace).to(destPath);
if (removeExisting) {
+ assert sourceTree != null;
+
+ // Find all of the UUIDS in the source tree
+ UuidFactory uuidFactory = context().getValueFactories().getUuidFactory();
+ PathFactory pathFactory = context().getValueFactories().getPathFactory();
+
+ Set<UUID> sourceUuids = new HashSet<UUID>();
+ LinkedList<SubgraphNode> nodesToVisit = new
LinkedList<SubgraphNode>();
+ nodesToVisit.push(sourceTree.getRoot());
+
+ while (!nodesToVisit.isEmpty()) {
+ SubgraphNode node = nodesToVisit.pop();
+
+ UUID uuid =
uuidFactory.create(node.getProperty(DnaLexicon.UUID).getFirstValue().toString());
+ if (uuid != null) {
+ sourceUuids.add(uuid);
+ }
+ for (Path.Segment childSegment : node.getChildrenSegments()) {
+
nodesToVisit.add(node.getNode(pathFactory.createRelativePath(childSegment)));
+ }
+ }
+
+ // See if the nodes exist in the current workspace outside of the current
tree
+ for (UUID uuid : sourceUuids) {
+ NodeInfo nodeInfo = null;
+ try {
+ nodeInfo = session.cache().findNodeInfo(uuid);
+ } catch (ItemNotFoundException infe) {
+ continue;
+ }
+ assert nodeInfo != null;
+
+ NodeDefinitionId nodeDefnId = nodeInfo.getDefinitionId();
+
+ JcrNodeType nodeType =
nodeTypeManager.getNodeType(nodeDefnId.getNodeTypeName());
+ JcrNodeDefinition nodeDefn = nodeType.childNodeDefinition(nodeDefnId);
+ if (nodeDefn.isMandatory()) {
+ // We can't just remove a mandatory node... unless its parent
will be removed too!
+ String path =
session.cache().getPathFor(nodeInfo).getString(context().getNamespaceRegistry());
+ throw new
ConstraintViolationException(JcrI18n.cannotRemoveNodeFromClone.text(path, uuid));
+
+ }
+ }
+
+ // Perform the copy operation, but use the "to" form (not the
"into", which takes the parent) ...
graph.copy(srcPath).replacingExistingNodesWithSameUuids().fromWorkspace(srcWorkspace).to(destPath);
} 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);
} catch (UuidAlreadyExistsException uaee) {
throw new ItemExistsException(uaee.getMessage());
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-06-21
00:16:03 UTC (rev 1057)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-06-21
00:45:14 UTC (rev 1058)
@@ -79,6 +79,7 @@
unableToSetSingleValuedPropertyUsingMultipleValues = Unable to set existing single-valued
property "{0}" on node "{1}" in workspace "{2}" using
multi-value setter methods
unableToRefreshBranchSinceAtLeastOneNodeMovedToParentOutsideOfBranch = Unable to refresh
"{0}" in workspace "{2}" because at least one of its decendants was
moved to another node outside of the branch that is not being refreshed
allPropertyValuesMustHaveSameType = All values of property "{0}" on node
"{3}" in workspace "{4}" must all be {2} values (values were: {1})
+cannotRemoveNodeFromClone = The node at "{0}" with UUID "{1}" exists
in the current workspace but cannot be removed because it is a mandatory child node
unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}"
cannot be moved under a decendant node ("{1}")
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/DnaTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/DnaTckTest.java 2009-06-21 00:16:03 UTC
(rev 1057)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/DnaTckTest.java 2009-06-21 00:45:14 UTC
(rev 1058)
@@ -1,5 +1,7 @@
package org.jboss.dna.jcr;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.Node;
@@ -8,6 +10,7 @@
import javax.jcr.Property;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
+import javax.jcr.nodetype.ConstraintViolationException;
import org.apache.jackrabbit.test.AbstractJCRTest;
/**
@@ -197,4 +200,74 @@
assertNotNull(node4node1node2);
}
+
+ /**
+ * A clone operation with removeExisting = true should fail if it would require
removing an existing node that is a mandatory
+ * child node of some other parent (and not replacing it as part of the clone
operation).
+ *
+ * @throws Exception if an error occurs
+ */
+ public void testShouldNotCloneIfItWouldViolateTypeSemantics() throws Exception {
+ session = helper.getSuperuserSession("otherWorkspace");
+ assertThat(session.getWorkspace().getName(), is("otherWorkspace"));
+
+ String nodetype1 = this.getProperty("nodetype");
+ Node node1 = session.getRootNode().addNode("cloneSource", nodetype1);
+ // This node is not a mandatory child of nodetype1
(dnatest:referenceableUnstructured)
+ node1.addNode("dnatest:mandatoryChild", nodetype1);
+
+ session.save();
+ session.logout();
+
+ // /node4 in the default workspace is type dna:referenceableUnstructured
+ superuser.getRootNode().addNode("cloneTarget", nodetype1);
+
+ // /node3 in the default workspace is type dna:referenceableUnstructured
+ superuser.getRootNode().addNode(nodeName3, nodetype1);
+ superuser.save();
+
+ // Throw the cloned items under node4
+ superuser.getWorkspace().clone("otherWorkspace",
"/cloneSource", "/cloneTarget/cloneSource", false);
+
+ superuser.refresh(false);
+ Node node3 = (Node)superuser.getItem("/node3");
+ assertThat(node3.getNodes().getSize(), is(0L));
+
+ Node node4node1 = (Node)superuser.getItem("/cloneTarget/cloneSource");
+ assertThat(node4node1.getNodes().getSize(), is(1L));
+
+ // Now clone from the same source under node3 and remove the existing records
+ superuser.getWorkspace().clone("otherWorkspace",
"/cloneSource", "/" + nodeName3 + "/cloneSource", true);
+ superuser.refresh(false);
+
+ Node node3node1 = (Node)superuser.getItem("/node3/cloneSource");
+ assertThat(node3node1.getNodes().getSize(), is(1L));
+
+ // Check that the nodes were indeed removed
+ Node node4 = (Node)superuser.getItem("/cloneTarget");
+
+ assertThat(node4.getNodes().getSize(), is(0L));
+
+ superuser.getRootNode().addNode("nodeWithMandatoryChild",
"dnatest:nodeWithMandatoryChild");
+ try {
+ superuser.save();
+ fail("A node with type dnatest:nodeWithMandatoryChild should not be
savable until the child is added");
+ } catch (ConstraintViolationException cve) {
+ // Expected
+ }
+
+ superuser.move("/node3/cloneSource/dnatest:mandatoryChild",
"/nodeWithMandatoryChild/dnatest:mandatoryChild");
+ superuser.save();
+ superuser.refresh(false);
+
+ // Now clone from the same source under node3 and remove the existing records
+ try {
+ superuser.getWorkspace().clone("otherWorkspace",
"/cloneSource", "/" + nodeName3 + "/cloneSource", true);
+ fail("Should not be able to use clone to remove the mandatory child node
at /nodeWithMandatoryChild/dnatest:mandatoryChild");
+ } catch (ConstraintViolationException cve) {
+ // expected
+ }
+
+ }
+
}