[dna-commits] DNA SVN: r1107 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/session and 4 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Mon Jul 13 16:37:54 EDT 2009


Author: rhauch
Date: 2009-07-13 16:37:53 -0400 (Mon, 13 Jul 2009)
New Revision: 1107

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/session/GraphSession.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/session/GraphSessionTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-479 Workspace import does not work

Resolved a number of issues, including how the SessionCache forwards down desired UUIDs or identification properties to the GraphSession when creating new nodes.  There still are 4 TCK import test cases that fail, so the DocumentViewImportTest and SerializationTest are still commented out.

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-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -88,7 +88,7 @@
         MapWorkspace workspace = getWorkspace(request, request.inWorkspace());
         MapNode node = getTargetNode(workspace, request, request.of());
         if (node == null) return;
-        Location actualLocation = getActualLocation(request.of().getPath(), node);
+        Location actualLocation = getActualLocation(request.of(), node);
         Path path = actualLocation.getPath();
         // Get the names of the children ...
         List<MapNode> children = node.getChildren();
@@ -107,7 +107,7 @@
         MapNode node = getTargetNode(workspace, request, request.at());
         if (node == null) return;
         // Get the properties of the node ...
-        Location actualLocation = getActualLocation(request.at().getPath(), node);
+        Location actualLocation = getActualLocation(request.at(), node);
         request.addProperty(propertyFactory.create(DnaLexicon.UUID, node.getUuid()));
         for (Property property : node.getProperties().values()) {
             request.addProperty(property);
@@ -140,7 +140,7 @@
                                               request.desiredSegment(),
                                               request.removeExisting());
         Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, newNode.getName());
-        Location oldLocation = getActualLocation(request.from().getPath(), node);
+        Location oldLocation = getActualLocation(request.from(), node);
         Location newLocation = Location.create(newPath, newNode.getUuid());
         request.setActualLocations(oldLocation, newLocation);
         recordChange(request);
@@ -165,7 +165,7 @@
         MapNode newParent = newWorkspace.getNode(newParentPath);
         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 oldLocation = getActualLocation(request.from(), node);
         Location newLocation = Location.create(newPath, newNode.getUuid());
         request.setActualLocations(oldLocation, newLocation);
         recordChange(request);
@@ -243,7 +243,7 @@
                 node.getProperties().put(propName, property);
             }
         }
-        Location actualLocation = getActualLocation(path, node);
+        Location actualLocation = getActualLocation(Location.create(path), node);
         request.setActualLocationOfNode(actualLocation);
         recordChange(request);
     }
@@ -260,7 +260,7 @@
         MapNode node = getTargetNode(workspace, request, request.at());
         if (node == null) return;
         workspace.removeNode(getExecutionContext(), node);
-        Location actualLocation = getActualLocation(request.at().getPath(), node);
+        Location actualLocation = getActualLocation(request.at(), node);
         request.setActualLocationOfNode(actualLocation);
         recordChange(request);
     }
@@ -301,7 +301,7 @@
         workspace.moveNode(getExecutionContext(), node, request.desiredName(), workspace, newParent, beforeNode);
         assert node.getParent() == newParent;
         Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName());
-        Location oldLocation = getActualLocation(request.from().getPath(), node);
+        Location oldLocation = getActualLocation(request.from(), node);
         Location newLocation = Location.create(newPath, node.getUuid());
         request.setActualLocations(oldLocation, newLocation);
         recordChange(request);
@@ -329,7 +329,7 @@
                 node.getProperties().put(propName, property);
             }
         }
-        Location actualLocation = getActualLocation(request.on().getPath(), node);
+        Location actualLocation = getActualLocation(request.on(), node);
         request.setActualLocationOfNode(actualLocation);
         recordChange(request);
     }
@@ -452,8 +452,9 @@
         }
     }
 
-    protected Location getActualLocation( Path path,
+    protected Location getActualLocation( Location location,
                                           MapNode node ) {
+        Path path = location.getPath();
         if (path == null) {
             // Find the path on the node ...
             LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
@@ -465,6 +466,11 @@
             }
             path = pathFactory.createAbsolutePath(segments);
         }
+        // If there is a UUID in the location, it should match the node's.
+        assert location.getUuid() == null || location.getUuid().equals(node.getUuid());
+        if (location.hasIdProperties()) {
+            return location.with(path);
+        }
         return Location.create(path, node.getUuid());
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -275,7 +275,8 @@
             CheckArg.isNotNull(id, "id");
             CheckArg.isNotNull(path, "path");
         }
-        Node<Payload, PropertyPayload> result = nodes.get(id); // if found, the user should have read privilege since it was
+        Node<Payload, PropertyPayload> result = id != null ? nodes.get(id) : null; // if found, the user should have read
+        // privilege since it was
         // already in the cache
         if (result == null || result.isStale()) {
             assert path != null;
@@ -667,6 +668,7 @@
             root.childrenByName = null;
             root.expirationTime = Long.MAX_VALUE;
             root.changedBelow = false;
+            root.payload = null;
         }
     }
 
@@ -781,13 +783,11 @@
         // LinkedList<Request> oldRequests = this.requests;
         this.requests = new LinkedList<Request>();
         this.requestBuilder = new BatchRequestBuilder(this.requests);
-        operations = store.batch(this.requestBuilder);
+        this.operations = store.batch(this.requestBuilder);
 
-        // Remove all the cached items that have been changed or deleted ...
+        // Remove all the cached items ...
         this.root.clearChanges();
-
-        // Remove all the changed and deleted infos ...
-        changeDependencies.clear();
+        this.root.unload();
     }
 
     /**
@@ -874,6 +874,7 @@
 
         // Remove all the cached, changed or deleted items that were just saved ...
         node.clearChanges();
+        node.unload();
     }
 
     protected Node<Payload, PropertyPayload> createNode( Node<Payload, PropertyPayload> parent,
@@ -925,7 +926,7 @@
     protected void recordUnloaded( final Node<Payload, PropertyPayload> node ) {
         if (node.isLoaded() && node.getChildrenCount() > 0) {
             // Walk the branch and remove all nodes from the map of all nodes ...
-            node.onLoadedNodes(new NodeVisitor<Payload, PropertyPayload>() {
+            node.onCachedNodes(new NodeVisitor<Payload, PropertyPayload>() {
                 @SuppressWarnings( "synthetic-access" )
                 @Override
                 public boolean visit( Node<Payload, PropertyPayload> unloaded ) {
@@ -2204,44 +2205,63 @@
         }
 
         /**
-         * Create a new child node with the supplied name and one initial property. The same-name-sibling index will be determined
-         * based upon the existing children.
+         * Create a new child node with the supplied name and multiple initial properties. The same-name-sibling index will be
+         * determined based upon the existing children.
          * 
          * @param name the name of the new child node
-         * @param property a property for the new node
+         * @param properties the (non-identification) properties for the new node
          * @return the new child node
-         * @throws IllegalArgumentException if the name or identification property is null
+         * @throws IllegalArgumentException if the name or properties are null
+         * @throws ValidationException if the new node is not valid as a child
          * @throws RepositorySourceException if this node must be loaded but doing so results in a problem
          */
         public Node<Payload, PropertyPayload> createChild( Name name,
-                                                           Property property ) {
+                                                           Property... properties ) {
             CheckArg.isNotNull(name, "name");
-            CheckArg.isNotNull(property, "property");
-            return doCreateChild(name, property, null);
+            CheckArg.isNotNull(properties, "properties");
+            return doCreateChild(name, null, properties);
         }
 
         /**
+         * Create a new child node with the supplied name and multiple initial identification properties. The same-name-sibling
+         * index will be determined based upon the existing children.
+         * 
+         * @param name the name of the new child node
+         * @param idProperties the identification properties for the new node
+         * @return the new child node
+         * @throws IllegalArgumentException if the name or properties are null
+         * @throws ValidationException if the new node is not valid as a child
+         * @throws RepositorySourceException if this node must be loaded but doing so results in a problem
+         */
+        public Node<Payload, PropertyPayload> createChild( Name name,
+                                                           Collection<Property> idProperties ) {
+            CheckArg.isNotNull(name, "name");
+            CheckArg.isNotEmpty(idProperties, "idProperties");
+            return doCreateChild(name, idProperties, null);
+        }
+
+        /**
          * Create a new child node with the supplied name and multiple initial properties. The same-name-sibling index will be
          * determined based upon the existing children.
          * 
          * @param name the name of the new child node
-         * @param firstProperty the first identification property for the new node
-         * @param remainingProperties the remaining identification properties for the new node
+         * @param idProperties the identification properties for the new node
+         * @param remainingProperties the remaining (non-identification) properties for the new node
          * @return the new child node
          * @throws IllegalArgumentException if the name or properties are null
          * @throws ValidationException if the new node is not valid as a child
          * @throws RepositorySourceException if this node must be loaded but doing so results in a problem
          */
         public Node<Payload, PropertyPayload> createChild( Name name,
-                                                           Property firstProperty,
+                                                           Collection<Property> idProperties,
                                                            Property... remainingProperties ) {
             CheckArg.isNotNull(name, "name");
-            CheckArg.isNotNull(firstProperty, "firstProperty");
-            return doCreateChild(name, firstProperty, remainingProperties);
+            CheckArg.isNotEmpty(idProperties, "idProperties");
+            return doCreateChild(name, idProperties, remainingProperties);
         }
 
         private Node<Payload, PropertyPayload> doCreateChild( Name name,
-                                                              Property firstProperty,
+                                                              Collection<Property> idProperties,
                                                               Property[] remainingProperties ) throws ValidationException {
             assert !isStale();
 
@@ -2255,22 +2275,24 @@
             // Figure out the name and SNS of the new copy ...
             List<Node<Payload, PropertyPayload>> currentChildren = childrenByName.get(name);
             Path newPath = cache.pathFactory.create(path, name, currentChildren.size() + 1);
-            Location newChild = Location.create(newPath);
+            Location newChild = idProperties != null && !idProperties.isEmpty() ? Location.create(newPath, idProperties) : Location.create(newPath);
 
             // Create the properties ...
             Map<Name, PropertyInfo<PropertyPayload>> newProperties = new HashMap<Name, PropertyInfo<PropertyPayload>>();
-            if (firstProperty != null) {
-                PropertyInfo<PropertyPayload> info = new PropertyInfo<PropertyPayload>(firstProperty, firstProperty.isMultiple(),
-                                                                                       Status.NEW, null);
-                newProperties.put(info.getName(), info);
-                if (remainingProperties != null) {
-                    for (Property property : remainingProperties) {
-                        PropertyInfo<PropertyPayload> info2 = new PropertyInfo<PropertyPayload>(property, property.isMultiple(),
-                                                                                                Status.NEW, null);
-                        newProperties.put(info2.getName(), info2);
-                    }
+            if (idProperties != null) {
+                for (Property idProp : idProperties) {
+                    PropertyInfo<PropertyPayload> info = new PropertyInfo<PropertyPayload>(idProp, idProp.isMultiple(),
+                                                                                           Status.NEW, null);
+                    newProperties.put(info.getName(), info);
                 }
             }
+            if (remainingProperties != null) {
+                for (Property property : remainingProperties) {
+                    PropertyInfo<PropertyPayload> info2 = new PropertyInfo<PropertyPayload>(property, property.isMultiple(),
+                                                                                            Status.NEW, null);
+                    newProperties.put(info2.getName(), info2);
+                }
+            }
 
             // Notify before the addition ...
             cache.nodeOperations.preCreateChild(this, newPath.getLastSegment(), newProperties);
@@ -2671,6 +2693,10 @@
             queue.add(this);
             while (!queue.isEmpty()) {
                 Node<Payload, PropertyPayload> node = queue.poll();
+                if (!node.isLoaded()) {
+                    visitor.visit(node);
+                    continue;
+                }
                 // Get an iterator over the children *before* we visit the node
                 Iterator<Node<Payload, PropertyPayload>> iter = node.getChildren().iterator();
                 // Visit this node ...

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-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -113,9 +113,20 @@
 
     @Test
     public void shouldAddChildrenAndSettingProperties() {
-        graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB", "valueB").and("propC", "valueC").and().create("/b").with("propD",
-                                                                                                                                                  "valueD").and("propE",
-                                                                                                                                                                "valueE").and().execute();
+        graph.batch()
+             .set("propA")
+             .to("valueA")
+             .on("/")
+             .and()
+             .create("/a")
+             .with("propB", "valueB")
+             .and("propC", "valueC")
+             .and()
+             .create("/b")
+             .with("propD", "valueD")
+             .and("propE", "valueE")
+             .and()
+             .execute();
         // Now look up the root node ...
         Node root = graph.getNodeAt("/");
         assertThat(root, is(notNullValue()));
@@ -867,13 +878,21 @@
 
         graph.create("/newUuids").and();
         // 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();
+        // graph.copy("/node1/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/node1");
+        graph.clone("/node1/node1/node1")
+             .fromWorkspace(workspaceName)
+             .as(name("node1"))
+             .into("/newUuids")
+             .failingIfAnyUuidsMatch();
 
         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();
+            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) {
@@ -905,20 +924,28 @@
         graph.create("/newUuids").and();
         // 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("/newUuids").replacingExistingNodesWithSameUuids();
-        
+        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();
+        graph.clone("/node1")
+             .fromWorkspace(workspaceName)
+             .as(name("otherNode"))
+             .into("/newUuids")
+             .replacingExistingNodesWithSameUuids();
 
         /*
          * Focus on testing node structure, since shouldCopyNodeWithChildren tests that properties get copied
@@ -929,11 +956,10 @@
         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) {
+        } 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")));
@@ -969,14 +995,18 @@
         graph.create("/segmentTestUuids").and();
         // Copy once to get the UUID into the default workspace
         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
         PropertyFactory propFactory = context.getPropertyFactory();
         graph.create("/segmentTestUuids/node1", propFactory.create(name("identifier"), "backup copy")).and();
 
         // Copy again to test the behavior now that the UUIDs are already in the default workspace
         // This should remove /newUuids/node1/shouldBeRemoved
-        graph.clone("/node1").fromWorkspace(workspaceName).as(segment("node1[1]")).into("/segmentTestUuids").replacingExistingNodesWithSameUuids();
+        graph.clone("/node1")
+             .fromWorkspace(workspaceName)
+             .as(segment("node1[1]"))
+             .into("/segmentTestUuids")
+             .replacingExistingNodesWithSameUuids();
 
         /*
          * Focus on testing node structure, since shouldCopyNodeWithChildren tests that properties get copied
@@ -984,8 +1014,9 @@
         // /newUuids/node1 should have been removed when the new node was added with the same UUID
         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"));
-        
+        assertThat(graph.getNodeAt("/segmentTestUuids/node1[2]").getProperty("identifier").getFirstValue().toString(),
+                   is("backup copy"));
+
         Subgraph target = graph.getSubgraphOfDepth(3).at("/segmentTestUuids/node1[1]");
         assertThat(target, is(notNullValue()));
         assertThat(target.getNode(".").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
@@ -1019,7 +1050,7 @@
             Object value = values[i];
             assertThat(value, is(instanceOf(Reference.class)));
             Reference ref = (Reference)value;
-            assertThat(graph.resolve(ref), is(graph.getNodeAt(toNodePath[i])));
+            assertThat(graph.resolve(ref).getLocation().getPath(), is(graph.getNodeAt(toNodePath[i]).getLocation().getPath()));
         }
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/session/GraphSessionTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/session/GraphSessionTest.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/session/GraphSessionTest.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -446,7 +446,7 @@
         // The affected nodes should now be stale ...
         assertThat(sports.isStale(), is(true));
         assertThat(utility.isStale(), is(true));
-        assertThat(cars.isStale(), is(false)); // not stale because it was unloaded
+        assertThat(cars.isStale(), is(true));
         assertThat(cars.isLoaded(), is(false));
         assertThat(root.isStale(), is(false)); // not touched during saves
 
@@ -454,6 +454,7 @@
         sports = cache.findNodeWith(path("/Cars/Utility/Sports"));
         assertConnectionsUsed(1);
         utility = cache.findNodeWith(path("/Cars/Utility"));
+        cars = cache.findNodeWith(path("/Cars"));
         assertNoMoreConnectionsUsed();
 
         assertChildren(cars, "Hybrid", "Luxury", "Utility");
@@ -567,11 +568,12 @@
         // The affected nodes should now be stale ...
         assertThat(sports.isStale(), is(true));
         assertThat(utility.isStale(), is(true));
-        assertThat(cars.isStale(), is(false)); // not stale because it was unloaded
+        assertThat(cars.isStale(), is(true));
         assertThat(cars.isLoaded(), is(false));
         assertThat(root.isStale(), is(false)); // not touched during saves
 
         // Now the state should reflect our changes ...
+        cars = cache.findNodeWith(path("/Cars"));
         assertChildren(cars, "Hybrid", "Utility", "Sports", "Luxury");
 
         // Now there should be no changes ...
@@ -607,6 +609,7 @@
         assertConnectionsUsed(2); // 1 to load children required by validation, 1 to perform save
 
         // Now the state should reflect our changes ...
+        cars = cache.findNodeWith(path("/Cars"));
         assertChildren(cars, "Hybrid", "Experimental", "Sports", "Luxury", "Utility", "Experimental[2]", "Experimental[3]");
 
         // Now there should be no changes ...

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-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -243,18 +243,7 @@
      * @throws RepositoryException if there is an exception
      */
     public final boolean isNodeType( Name nodeTypeName ) throws RepositoryException {
-        JcrNodeType nodeType = getPrimaryNodeType();
-        if (nodeType.isNodeType(nodeTypeName)) {
-            return true;
-        }
-        JcrNodeTypeManager nodeTypes = session().nodeTypeManager();
-        for (Name mixinTypeName : getMixinTypeNames()) {
-            JcrNodeType mixinType = nodeTypes.getNodeType(mixinTypeName);
-            if (mixinType.isNodeType(nodeTypeName)) {
-                return true;
-            }
-        }
-        return false;
+        return cache.isNodeType(nodeInfo(), nodeTypeName);
     }
 
     /**
@@ -505,7 +494,10 @@
      * @throws RepositoryException if there is an error finding the property with the supplied name
      */
     public final Property getProperty( Name propertyName ) throws RepositoryException {
-        return cache.findJcrProperty(nodeId, location.getPath(), propertyName);
+        Property property = cache.findJcrProperty(nodeId, location.getPath(), propertyName);
+        // Must be referenceable in order to return this property ...
+        if (property != null && JcrLexicon.UUID.equals(propertyName) && !isReferenceable()) return null;
+        return property;
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -92,10 +92,16 @@
     }
 
     final org.jboss.dna.graph.property.Property property() throws RepositoryException {
+        if (propertyInfo() == null) {
+            int x = 0;
+        }
         return propertyInfo().getProperty();
     }
 
     JcrValue createValue( Object value ) throws RepositoryException {
+        if (value == null) {
+            int x = 0;
+        }
         return new JcrValue(context().getValueFactories(), this.cache, payload().getPropertyType(), value);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -125,7 +125,11 @@
         }
         assert cache != null;
 
-        this.currentNode = cache.findJcrNode(null, parentPath);
+        try {
+            this.currentNode = cache.findJcrNode(null, parentPath);
+        } catch (ItemNotFoundException e) {
+            throw new PathNotFoundException(e.getLocalizedMessage(), e);
+        }
         this.jcrValueFactory = session.getValueFactory();
         this.nodeTypes = session.nodeTypeManager();
         this.jcrNamespaceRegistry = session.workspace().getNamespaceRegistry();
@@ -342,6 +346,7 @@
 
                     String typeName = currentProps.get(primaryTypeName).get(0).getString();
                     AbstractJcrNode newNode = parentNode.editor().createChild(nameFor(currentNodeName), uuid, nameFor(typeName));
+                    SessionCache.NodeEditor newNodeEditor = newNode.editor();
 
                     for (Map.Entry<String, List<Value>> entry : currentProps.entrySet()) {
                         if (entry.getKey().equals(primaryTypeName)) {
@@ -351,7 +356,7 @@
                         if (entry.getKey().equals(mixinTypesName)) {
                             for (Value value : entry.getValue()) {
                                 JcrNodeType mixinType = nodeTypeFor(value.getString());
-                                newNode.editor().addMixin(mixinType);
+                                newNodeEditor.addMixin(mixinType);
                             }
                             continue;
                         }
@@ -363,11 +368,11 @@
                         List<Value> values = entry.getValue();
 
                         if (values.size() == 1) {
-                            newNode.editor().setProperty(nameFor(entry.getKey()), (JcrValue)values.get(0));
+                            newNodeEditor.setProperty(nameFor(entry.getKey()), (JcrValue)values.get(0));
                         } else {
-                            newNode.editor().setProperty(nameFor(entry.getKey()),
-                                                         values.toArray(new Value[values.size()]),
-                                                         PropertyType.UNDEFINED);
+                            newNodeEditor.setProperty(nameFor(entry.getKey()),
+                                                      values.toArray(new Value[values.size()]),
+                                                      PropertyType.UNDEFINED);
                         }
                     }
 
@@ -448,10 +453,12 @@
 
                 if (uuid != null) {
                     try {
+                        // Deal with any existing node ...
                         AbstractJcrNode existingNodeWithUuid = cache().findJcrNode(Location.create(uuid));
                         switch (uuidBehavior) {
                             case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
-                                existingNodeWithUuid.editor().destroy();
+                                parentNode = existingNodeWithUuid.getParent();
+                                existingNodeWithUuid.remove();
                                 break;
                             case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
                                 uuid = UUID.randomUUID();
@@ -460,18 +467,19 @@
                                 if (existingNodeWithUuid.path().isAtOrAbove(parentStack.firstElement().path())) {
                                     throw new ConstraintViolationException();
                                 }
-                                existingNodeWithUuid.editor().destroy();
+                                existingNodeWithUuid.remove();
                                 break;
                             case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
                                 throw new ItemExistsException();
                         }
                     } catch (ItemNotFoundException e) {
-                        // don't care
+                        // there wasn't an existing item, so just continue
                     }
                 }
 
                 name = DOCUMENT_VIEW_NAME_DECODER.decode(name);
                 AbstractJcrNode currentNode = parentNode.editor().createChild(nameFor(name), uuid, nameFor(primaryTypeName));
+                SessionCache.NodeEditor currentNodeEditor = currentNode.editor();
 
                 for (int i = 0; i < atts.getLength(); i++) {
                     if (JcrContentHandler.this.primaryTypeName.equals(atts.getQName(i))) {
@@ -480,7 +488,7 @@
 
                     if (mixinTypesName.equals(atts.getQName(i))) {
                         JcrNodeType mixinType = nodeTypeFor(atts.getValue(i));
-                        currentNode.editor().addMixin(mixinType);
+                        currentNodeEditor.addMixin(mixinType);
                         continue;
                     }
 
@@ -493,7 +501,7 @@
                     // String value = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getValue(i));
                     String value = atts.getValue(i);
                     String propertyName = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getQName(i));
-                    currentNode.editor().setProperty(nameFor(propertyName), (JcrValue)valueFor(value, PropertyType.STRING));
+                    currentNodeEditor.setProperty(nameFor(propertyName), (JcrValue)valueFor(value, PropertyType.STRING));
                 }
 
                 parentStack.push(currentNode);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -94,6 +94,6 @@
      * @see javax.jcr.Item#remove()
      */
     public void remove() throws RepositoryException {
-        editorForParent().destroyChild(nodeInfo());
+        editor().destroy();
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -49,6 +49,7 @@
 import javax.jcr.ValueFormatException;
 import javax.jcr.Workspace;
 import javax.jcr.nodetype.ConstraintViolationException;
+import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
@@ -62,9 +63,11 @@
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
+import org.jboss.dna.graph.session.GraphSession;
 import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
 import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
+import org.jboss.dna.jcr.SessionCache.JcrPropertyPayload;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -812,4 +815,43 @@
     public void save() throws RepositoryException {
         cache.save();
     }
+
+    /**
+     * Get a snapshot of the current session state. This snapshot is immutable and will not reflect any future state changes in
+     * the session.
+     * 
+     * @return the snapshot; never null
+     */
+    public Snapshot getSnapshot() {
+        return new Snapshot(cache.graphSession().getRoot().getSnapshot(false));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getSnapshot().toString();
+    }
+
+    @Immutable
+    public class Snapshot {
+        private final GraphSession.StructureSnapshot<JcrPropertyPayload> rootSnapshot;
+
+        protected Snapshot( GraphSession.StructureSnapshot<JcrPropertyPayload> snapshot ) {
+            this.rootSnapshot = snapshot;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return rootSnapshot.toString();
+        }
+    }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -362,13 +362,13 @@
         try {
             graphSession.save();
         } catch (ValidationException e) {
-            throw new ConstraintViolationException(e.getLocalizedMessage(), e.getCause());
+            throw new ConstraintViolationException(e.getLocalizedMessage(), e);
         } catch (InvalidStateException e) {
-            throw new InvalidItemStateException(e.getLocalizedMessage(), e.getCause());
+            throw new InvalidItemStateException(e.getLocalizedMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getLocalizedMessage(), e.getCause());
+            throw new RepositoryException(e.getLocalizedMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -393,13 +393,13 @@
             assert node != null;
             graphSession.save(node);
         } catch (ValidationException e) {
-            throw new ConstraintViolationException(e.getLocalizedMessage(), e.getCause());
+            throw new ConstraintViolationException(e.getLocalizedMessage(), e);
         } catch (InvalidStateException e) {
-            throw new InvalidItemStateException(e.getLocalizedMessage(), e.getCause());
+            throw new InvalidItemStateException(e.getLocalizedMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getLocalizedMessage(), e.getCause());
+            throw new RepositoryException(e.getLocalizedMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -420,11 +420,11 @@
         try {
             return graphSession.findNodeWith(id, absolutePath);
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage(), e.getCause());
+            throw new ItemNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -451,11 +451,11 @@
         try {
             return graphSession.findNodeRelativeTo(referenceNode, relativePath);
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new PathNotFoundException(e.getMessage(), e.getCause());
+            throw new PathNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -484,11 +484,11 @@
         try {
             return graphSession.findNodeWith(location).getPayload().getJcrNode();
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage(), e.getCause());
+            throw new ItemNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -535,11 +535,11 @@
             // Find the reference node ...
             return graphSession.findNodeRelativeTo(referenceNode, relativePath).getPayload().getJcrNode();
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new PathNotFoundException(e.getMessage(), e.getCause());
+            throw new PathNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -562,6 +562,7 @@
         Node<JcrNodePayload, JcrPropertyPayload> node = findNode(id, absolutePath);
         PropertyInfo<JcrPropertyPayload> propertyInfo = node.getProperty(propertyName);
         if (propertyInfo != null) {
+            if (propertyName.equals(JcrLexicon.UUID) && !isReferenceable(node)) return null;
             return propertyInfo.getPayload().getJcrProperty();
         }
         return null;
@@ -585,17 +586,18 @@
             Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(node.getPropertyCount());
             for (org.jboss.dna.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property : node.getProperties()) {
                 Name propertyName = property.getName();
+                if (propertyName.equals(JcrLexicon.UUID) && !isReferenceable(node)) continue;
                 if (!propertyName.getNamespaceUri().equals(DnaIntLexicon.Namespace.URI)) {
                     result.add(property.getPayload().getJcrProperty());
                 }
             }
             return result;
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new PathNotFoundException(e.getMessage(), e.getCause());
+            throw new PathNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
     }
 
@@ -655,11 +657,11 @@
                 parent = graphSession.findNodeRelativeTo(fromNode, relativePath.getParent());
             }
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage(), e.getCause());
+            throw new ItemNotFoundException(e.getMessage(), e);
         } catch (RepositorySourceException e) {
-            throw new RepositoryException(e.getMessage(), e.getCause());
+            throw new RepositoryException(e.getMessage(), e);
         } catch (AccessControlException e) {
-            throw new AccessDeniedException(e.getMessage(), e.getCause());
+            throw new AccessDeniedException(e.getMessage(), e);
         }
 
         // JSR-170 doesn't allow children and proeprties to have the same name, but this is relaxed in JSR-283.
@@ -695,6 +697,37 @@
     }
 
     /**
+     * Determine whether the node's primary type or any of the mixins are or extend the node type with the supplied name. This
+     * method is semantically equivalent to but slightly more efficient than the {@link javax.jcr.Node#isNodeType(String)
+     * equivalent in the JCR API}.
+     * 
+     * @param node the node to be evaluated
+     * @param nodeType the name of the node type
+     * @return true if this node is of the node type given by the supplied name, or false otherwise
+     * @throws RepositoryException if there is an exception
+     */
+    public final boolean isNodeType( Node<JcrNodePayload, JcrPropertyPayload> node,
+                                     Name nodeType ) throws RepositoryException {
+        Name primaryTypeName = node.getPayload().getPrimaryTypeName();
+        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        if (primaryType.isNodeType(nodeType)) {
+            return true;
+        }
+        JcrNodeTypeManager nodeTypes = session().nodeTypeManager();
+        for (Name mixinTypeName : node.getPayload().getMixinTypeNames()) {
+            JcrNodeType mixinType = nodeTypes.getNodeType(mixinTypeName);
+            if (mixinType.isNodeType(nodeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isReferenceable( Node<JcrNodePayload, JcrPropertyPayload> node ) throws RepositoryException {
+        return isNodeType(node, JcrMixLexicon.REFERENCEABLE);
+    }
+
+    /**
      * Obtain an {@link NodeEditor editor} that can be used to manipulate the properties or children on the node identified by the
      * supplied identifier and path. The node must exist prior to this call, either as a node that exists in the workspace or as a
      * node that was created within this session but not yet persisted to the workspace. This method returns an editor that
@@ -939,11 +972,11 @@
                 node.setProperty(dnaProp, definition.isMultiple(), propPayload);
                 return jcrProp;
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1118,11 +1151,11 @@
                 node.setProperty(dnaProp, definition.isMultiple(), propPayload);
                 return jcrProp;
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1138,11 +1171,11 @@
             try {
                 return node.removeProperty(name) != null;
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1161,11 +1194,11 @@
             try {
                 node.orderChildBefore(childToBeMoved, before);
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1222,11 +1255,11 @@
                 }
                 return existingChild;
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1292,9 +1325,9 @@
                     setProperty(JcrLexicon.UUID, value, false);
                 }
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1386,12 +1419,16 @@
 
                 // Now add the "jcr:uuid" property if and only if referenceable ...
                 Node<JcrNodePayload, JcrPropertyPayload> result = null;
-                if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) {
+                boolean isReferenceable = primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE);
+                Property uuidProperty = null;
+                if (desiredUuid != null || isReferenceable) {
                     if (desiredUuid == null) {
                         desiredUuid = UUID.randomUUID();
                     }
-                    Property uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
-                    result = node.createChild(name, uuidProperty, primaryTypeProp, nodeDefinitionProp);
+                    uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
+                }
+                if (uuidProperty != null) {
+                    result = node.createChild(name, Collections.singleton(uuidProperty), primaryTypeProp, nodeDefinitionProp);
                 } else {
                     result = node.createChild(name, primaryTypeProp, nodeDefinitionProp);
                 }
@@ -1400,11 +1437,11 @@
                 // Finally, return the jcr node ...
                 return (JcrNode)result.getPayload().getJcrNode();
             } catch (ValidationException e) {
-                throw new ConstraintViolationException(e.getMessage(), e.getCause());
+                throw new ConstraintViolationException(e.getMessage(), e);
             } catch (RepositorySourceException e) {
-                throw new RepositoryException(e.getMessage(), e.getCause());
+                throw new RepositoryException(e.getMessage(), e);
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
 
@@ -1423,7 +1460,7 @@
             try {
                 child.destroy();
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
             return true;
         }
@@ -1439,7 +1476,7 @@
             try {
                 node.destroy();
             } catch (AccessControlException e) {
-                throw new AccessDeniedException(e.getMessage(), e.getCause());
+                throw new AccessDeniedException(e.getMessage(), e);
             }
             return true;
         }
@@ -2337,7 +2374,15 @@
             Set<JcrNodeDefinition> satisfiedChildNodes = new HashSet<JcrNodeDefinition>();
             Set<JcrPropertyDefinition> satisfiedProperties = new HashSet<JcrPropertyDefinition>();
 
+            // Is this node referenceable ...
+            boolean referenceable = false;
+            try {
+                referenceable = isReferenceable(node);
+            } catch (RepositoryException e) {
+                throw new ValidationException(e.getLocalizedMessage());
+            }
             for (org.jboss.dna.graph.session.GraphSession.PropertyInfo<JcrPropertyPayload> property : node.getProperties()) {
+                if (property.getName().equals(JcrLexicon.UUID) && !referenceable) continue;
                 JcrPropertyPayload propPayload = property.getPayload();
                 JcrPropertyDefinition definition = findBestPropertyDefintion(primaryTypeName,
                                                                              mixinTypeNames,
@@ -2447,13 +2492,13 @@
             // Now update the property infos for the two mandatory properties ...
             JcrNodeType ntBase = nodeTypes().getNodeType(JcrNtLexicon.BASE);
             assert ntBase != null;
-            primaryTypeInfo = createPropertyInfo(parent.getPayload(),
+            primaryTypeInfo = createPropertyInfo(child.getPayload(),
                                                  primaryTypeInfo.getProperty(),
                                                  ntBase.allPropertyDefinitions(JcrLexicon.PRIMARY_TYPE).iterator().next(),
                                                  PropertyType.NAME,
                                                  primaryTypeInfo);
             properties.put(primaryTypeInfo.getName(), primaryTypeInfo);
-            nodeDefnInfo = createPropertyInfo(parent.getPayload(),
+            nodeDefnInfo = createPropertyInfo(child.getPayload(),
                                               nodeDefnInfo.getProperty(),
                                               ntBase.allPropertyDefinitions(DnaIntLexicon.NODE_DEFINITON).iterator().next(),
                                               PropertyType.STRING,
@@ -2465,7 +2510,7 @@
             if (uuidInfo != null) {
                 JcrNodeType mixRef = nodeTypes().getNodeType(JcrMixLexicon.REFERENCEABLE);
                 assert mixRef != null;
-                uuidInfo = createPropertyInfo(parent.getPayload(),
+                uuidInfo = createPropertyInfo(child.getPayload(),
                                               uuidInfo.getProperty(),
                                               mixRef.allPropertyDefinitions(JcrLexicon.UUID).iterator().next(),
                                               PropertyType.STRING,

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -114,7 +114,8 @@
                                    String targetPath,
                                    ExportType useSystemView,
                                    boolean skipBinary,
-                                   boolean noRecurse ) throws Exception {
+                                   boolean noRecurse,
+                                   boolean useWorkspace ) throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
         if (useSystemView == ExportType.SYSTEM) {
@@ -125,11 +126,17 @@
 
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 
-        session.importXML(targetPath, bais, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
+        if (useWorkspace) {
+            // import via workspace ...
+            session.getWorkspace().importXML(targetPath, bais, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
+        } else {
+            // import via session ...
+            session.importXML(targetPath, bais, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
+        }
     }
 
     @Test
-    public void shouldImportExportEscapedXmlCharactersInSystemView() throws Exception {
+    public void shouldImportExportEscapedXmlCharactersInSystemViewUsingSession() throws Exception {
         String testName = "importExportEscapedXmlCharacters";
         Node rootNode = session.getRootNode();
         Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured");
@@ -140,15 +147,34 @@
         assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));
         sourceNode.addNode(BAD_CHARACTER_STRING);
 
-        testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false);
+        testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false, false);
         Node newSourceNode = targetNode.getNode(testName + "Source");
         newSourceNode.getNode(BAD_CHARACTER_STRING);
         assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));
     }
 
+    @Test
+    public void shouldImportExportEscapedXmlCharactersInSystemViewUsingWorkspace() throws Exception {
+        String testName = "importExportEscapedXmlCharacters";
+        Node rootNode = session.getRootNode();
+        Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured");
+        Node targetNode = rootNode.addNode(testName + "Target", "nt:unstructured");
+
+        // Test data
+        sourceNode.setProperty("badcharacters", BAD_CHARACTER_STRING);
+        assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));
+        sourceNode.addNode(BAD_CHARACTER_STRING);
+        session.save();
+
+        testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false, true);
+        Node newSourceNode = targetNode.getNode(testName + "Source");
+        newSourceNode.getNode(BAD_CHARACTER_STRING);
+        assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));
+    }
+
     @Ignore( "JR TCK is broken" )
     @Test
-    public void shouldImportExportEscapedXmlCharactersInDocumentView() throws Exception {
+    public void shouldImportExportEscapedXmlCharactersInDocumentViewUsingSession() throws Exception {
         String testName = "importExportEscapedXmlCharacters";
         Node rootNode = session.getRootNode();
         Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured");
@@ -159,7 +185,7 @@
         assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));
         sourceNode.addNode(BAD_CHARACTER_STRING);
 
-        testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.DOCUMENT, false, false);
+        testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.DOCUMENT, false, false, false);
         Node newSourceNode = targetNode.getNode(testName + "Source");
         newSourceNode.getNode(BAD_CHARACTER_STRING);
         assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-07-13 15:07:52 UTC (rev 1106)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-07-13 20:37:53 UTC (rev 1107)
@@ -26,6 +26,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
@@ -40,6 +41,7 @@
 import java.util.UUID;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import org.jboss.dna.graph.Location;
@@ -255,6 +257,56 @@
         assertThat(hybrid.hasNode("child"), is(true));
     }
 
+    @Test
+    public void shouldNotExposeUuidPropertyOnNonReferenceableNodes() throws Exception {
+        AbstractJcrNode highlander = cache.findJcrNode(null, path("/Cars/Hybrid/Toyota Highlander"));
+        assertNotReferenceable(highlander);
+    }
+
+    @Test
+    public void shouldExposeUuidPropertyOnReferenceableNodes() throws Exception {
+        AbstractJcrNode hybrid = cache.findJcrNode(null, path("/Cars/Hybrid"));
+        hybrid.addMixin("mix:referenceable"); // we don't have any referenceable nodes in our tests
+        assertReferenceable(hybrid);
+    }
+
+    @Test
+    public void shouldExposeUuidPropertyOnlyWhenNonReferenceableNodeTransitionsToReferenceable() throws Exception {
+        AbstractJcrNode highlander = cache.findJcrNode(null, path("/Cars/Hybrid/Toyota Highlander"));
+        assertNotReferenceable(highlander);
+        highlander.addMixin("mix:referenceable");
+        assertReferenceable(highlander);
+    }
+
+    protected void assertNotReferenceable( AbstractJcrNode node ) throws RepositoryException {
+        assertThat(node.isReferenceable(), is(false));
+        try {
+            node.getUUID();
+            fail("should not return UUID if the node is not referenceable");
+        } catch (UnsupportedRepositoryOperationException e) {
+            // expected
+        }
+        // Should not have a "jcr:uuid" property ...
+        assertThat(node.hasProperty(JcrLexicon.UUID), is(false));
+        assertThat(node.getProperty(JcrLexicon.UUID), is(nullValue()));
+    }
+
+    protected void assertReferenceable( AbstractJcrNode node ) throws RepositoryException {
+        assertThat(node.isReferenceable(), is(true));
+        String uuidValue = null;
+        try {
+            uuidValue = node.getUUID();
+            assertThat(uuidValue, is(notNullValue()));
+        } catch (UnsupportedOperationException e) {
+            fail("should return UUID if the node is referenceable");
+        }
+        // Should have a "jcr:uuid" property ...
+        assertThat(node.hasProperty(JcrLexicon.UUID), is(true));
+        javax.jcr.Property uuidProp = node.getProperty(JcrLexicon.UUID);
+        assertThat(uuidProp, is(notNullValue()));
+        assertThat(uuidProp.getString(), is(uuidValue));
+    }
+
     protected void assertProperty( AbstractJcrProperty property,
                                    javax.jcr.Node node,
                                    String name,




More information about the dna-commits mailing list