You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2014/12/18 11:43:32 UTC

svn commit: r1646435 [2/3] - in /jackrabbit/trunk: jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/ jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/server/remoting/davex/ jackrabbit-jcr-server/src/main/java/org/apache/jackrab...

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/security/authorization/jackrabbit/acl/AccessControlManagerImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/security/authorization/jackrabbit/acl/AccessControlManagerImplTest.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/security/authorization/jackrabbit/acl/AccessControlManagerImplTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/security/authorization/jackrabbit/acl/AccessControlManagerImplTest.java Thu Dec 18 10:43:31 2014
@@ -17,35 +17,54 @@
 package org.apache.jackrabbit.jcr2spi.security.authorization.jackrabbit.acl;
 
 import java.security.Principal;
+
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.AccessControlPolicyIterator;
 import javax.jcr.security.Privilege;
 
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
 import org.apache.jackrabbit.test.NotExecutableException;
 import org.apache.jackrabbit.test.api.security.AbstractAccessControlTest;
 
-public class AccessControlManagerImplTest extends AbstractAccessControlTest {
-        
+public class AccessControlManagerImplTest extends AbstractAccessControlTest {    
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();        
+    }
+    
     private Principal getUnknownPrincipal() throws NotExecutableException, RepositoryException {
         return getHelper().getUnknownPrincipal(superuser);
     }
     
+    public void testGetAndHasPrivileges() throws Exception {
+        Privilege[] privileges = acMgr.getPrivileges(testRoot);
+        assertNotNull(privileges);
+        assertTrue(acMgr.hasPrivileges(testRoot, privileges));
+    }
+    
     /**
      * Tests the binding state of a policy.
      * @throws Exception
      */
     public void testGetPolicesAfterSetPoliciesCall() throws Exception {
-        AccessControlPolicyIterator policies = acMgr.getApplicablePolicies(testRoot);
-        AccessControlPolicy policy = null;
-        while (policies.hasNext()) {
-            policy = policies.nextAccessControlPolicy();
-            acMgr.setPolicy(testRoot, policy);
-            AccessControlPolicy[] acl = acMgr.getPolicies(testRoot);
-            assertNotNull(acl);
-        }
+        try {
+            AccessControlPolicyIterator policies = acMgr.getApplicablePolicies(testRoot);
+            AccessControlPolicy policy = null;
+            while (policies.hasNext()) {
+                policy = policies.nextAccessControlPolicy();
+                acMgr.setPolicy(testRoot, policy);
+                AccessControlPolicy[] acl = acMgr.getPolicies(testRoot);
+                assertNotNull(acl);
+            }
+        } finally {
+            superuser.refresh(false);
+        }        
     }
 
     /**
@@ -57,19 +76,20 @@ public class AccessControlManagerImplTes
     public void testRemovePolicyAfterASetPoliciesCall() throws Exception {
         try {
             AccessControlPolicyIterator policies = acMgr.getApplicablePolicies(testRoot);
-            AccessControlPolicy policy = policies.nextAccessControlPolicy();
-            
-            AccessControlListImpl acl = (AccessControlListImpl) policy;
-            
-            // GRANT read privilege
-            acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
-            
-            acMgr.setPolicy(testRoot, policy);
-            
-            AccessControlPolicy[] unsavePolicy = acMgr.getPolicies(testRoot);
-            
-            // MUST be able to get policies that are not ineffect for the node at 'testRoot'
-            assertFalse(unsavePolicy.length == 0);
+            while (policies.hasNext()) {
+                AccessControlList acl = (AccessControlListImpl) policies.nextAccessControlPolicy();
+                
+                // GRANT read privilege
+                acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
+                
+                acMgr.setPolicy(testRoot, acl);
+                
+                AccessControlPolicy[] transientPolicy = acMgr.getPolicies(testRoot);
+                
+                acMgr.removePolicy(testRoot, transientPolicy[0]);
+                
+                assertEquals(0, acMgr.getPolicies(testRoot).length);
+            }
 
         } finally {
             superuser.refresh(false);
@@ -77,61 +97,119 @@ public class AccessControlManagerImplTes
     }
     
     /**
-     * test removing an effective policy.
+     * Test removing an effective policy.
      */
-    public void testRemovePolicyAfterASaveCall() {
-        // TODO
+    public void testRemovePolicyAfterASaveCall() throws Exception {
+        try {
+            AccessControlList[] acl = (AccessControlList[]) acMgr.getPolicies(testRoot);
+            if (acl.length > 0) {
+                acMgr.removePolicy(testRoot, acl[0]);
+            } else {                
+                AccessControlPolicy policy = acMgr.getApplicablePolicies(testRoot).nextAccessControlPolicy();
+                acMgr.setPolicy(testRoot, policy);
+                acMgr.removePolicy(testRoot, policy);
+            }
+
+            // transient removal           
+            AccessControlPolicy[] noPolicies = acMgr.getPolicies(testRoot);            
+            assertEquals(0, noPolicies.length);
+
+            // save changes -> removal of protected items on jcr-server
+            superuser.save();
+        } catch (Exception e) {
+            throw new RepositoryException(e.getMessage());
+        } finally {
+            superuser.refresh(false);
+        }
     }
     
-    public void testGetPrivilegesOnNonExistingNode() {
-        // TODO
+    /**
+     * JCR mandates that the path specified for getPrivileges method must
+     * be absolute and points to an existing node.
+     * @throws Exception
+     */
+    public void testGetPrivilegesOnNonExistingNode() throws Exception {
+        try {
+            acMgr.getPrivileges(getPathToNonExistingNode());
+            fail("Must throw a PathNotFoundException");
+        } catch (PathNotFoundException e) {
+            // success
+        }        
     }
 
     /**
+     * Add an AccessControlList with four entries. This will result in having the result in:
+     * Transient-space: An ACL node that has four child-nodes.
+     * Persistent-state: An ACL node that has one child-node.
+     * NOTE: That Jackrabbit-core tries to internally merge the entries that belongs to the same 
+     * principal, which is not the case for the client-side ACM implementation.
+     */
+    public void testAddingFourAccessControlEntries() throws Exception {
+        try {
+            AccessControlList acl = (AccessControlList) getACL(testRoot);        
+            
+            acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
+            acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
+            acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
+            acl.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
+            
+            acMgr.setPolicy(testRoot, acl);
+
+            // Transient-space: Must contain FOUR ace nodes.
+            assertEquals(4, testRootNode.getNode("rep:policy").getNodes().getSize());
+            
+            superuser.save();
+            
+            // Persistent-state: Must contain a single ace node -> entries were merged
+            assertEquals(1, testRootNode.getNode("rep:policy").getNodes().getSize());
+        } finally {
+            superuser.refresh(false);
+        }
+    }
+    
+    /**
      * Test retrieving a policy after a save call.
      * @throws Exception
      */
     public void testGetPoliciesAfterASaveCall() throws Exception {
-        AccessControlPolicyIterator pi = acMgr.getApplicablePolicies(testRoot);
-        assertTrue(pi.hasNext());
-        AccessControlListImpl policy = (AccessControlListImpl) pi.nextAccessControlPolicy();
-
-        String aclPath = policy.getPath();
-        assertEquals(aclPath, testRoot);
-
-        // GRANT 'read' privilege to principal
-        policy.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
-
-        // GRANT 'add_child_nodes' privilege
-        policy.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_ADD_CHILD_NODES));
-
-        assertEquals(1, policy.getAccessControlEntries().length);
-
-        // bind the policy and save changes
-        acMgr.setPolicy(testRoot, policy);
-        superuser.save();
-
-        Node aclNode = testRootNode.getNode("rep:policy");
-        assertNotNull(aclNode);
-
-        // only a single ACE node should be created by the manager
-        NodeIterator nit = aclNode.getNodes();
-        assertEquals(2, nit.getSize());
+        try {
+            JackrabbitAccessControlList policy = (JackrabbitAccessControlList) getACL(testRoot);
 
-        AccessControlPolicy[] policies = acMgr.getPolicies(testRoot);
+            String aclPath = policy.getPath();
+            assertEquals(aclPath, testRoot);
 
-        // A single policy node at 'testRoot'
-        assertEquals(1, policies.length);
+            // GRANT 'read' privilege to principal
+            policy.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ));
 
-        policy = (AccessControlListImpl) policies[0];
+            // GRANT 'add_child_nodes' privilege
+            policy.addAccessControlEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_ADD_CHILD_NODES));
 
-        //... and the policy contains 2 entries.
-        assertEquals(2, policy.getAccessControlEntries().length);
+            assertEquals(2, policy.getAccessControlEntries().length);
 
-        // revoke the read privilege
-        policy.addEntry(getUnknownPrincipal(), privilegesFromName(Privilege.JCR_READ), false);
+            // bind the policy and save changes
+            acMgr.setPolicy(testRoot, policy);
+            superuser.save();
 
-        assertEquals(3, policy.getAccessControlEntries().length);
+            Node aclNode = testRootNode.getNode("rep:policy");
+            assertNotNull(aclNode);
+            
+            NodeIterator nit = aclNode.getNodes();
+            
+            // Jackrabbit-core will match the two entries -> only a single aceNode will be created.
+            assertEquals(1, nit.getSize());            
+        } finally {
+            superuser.refresh(false);
+        }
+    }
+    
+    private AccessControlPolicy getACL(String absPath) throws RepositoryException {
+        AccessControlList acl = null;
+        if (acMgr.getPolicies(absPath).length > 0) {
+            acl = (AccessControlList) acMgr.getPolicies(absPath)[0];
+        } else {
+            acl = (AccessControlList) acMgr.getApplicablePolicies(absPath).nextAccessControlPolicy();
+        }
+        return acl;
     }
 
 }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java Thu Dec 18 10:43:31 2014
@@ -72,6 +72,7 @@ import org.apache.jackrabbit.spi.QueryIn
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
 import org.apache.jackrabbit.spi.Subscription;
+import org.apache.jackrabbit.spi.Tree;
 import org.apache.jackrabbit.spi.commons.identifier.IdFactoryImpl;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
@@ -514,6 +515,14 @@ public abstract class AbstractRepository
         throw new UnsupportedRepositoryOperationException();
     }
 
+    /**
+     * @throws UnsupportedRepositoryOperationException always.
+     */
+    @Override
+    public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
     /**
      * @throws UnsupportedRepositoryOperationException always.
      */

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java Thu Dec 18 10:43:31 2014
@@ -22,6 +22,7 @@ import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Tree;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.ValueFormatException;
@@ -131,6 +132,10 @@ public class SerializableBatch implement
         recording.add(new Move(srcNodeId, destParentNodeId, destName));
     }
 
+    public void setTree(NodeId parentId, Tree contentTree)
+            throws RepositoryException {
+        recording.add(new SetTree(parentId, contentTree));
+    }
     //----------------------------< internal >----------------------------------
 
     public interface Operation extends Serializable {
@@ -170,6 +175,25 @@ public class SerializableBatch implement
         }
     }
 
+    private static class SetTree implements Operation {
+
+        private final NodeId parentId;
+
+        private final Tree contentTree;
+
+        SetTree(NodeId parentId, Tree contentTree) {
+            this.parentId = parentId;
+            this.contentTree = contentTree;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void replay(Batch batch) throws RepositoryException {
+            batch.setTree(parentId, contentTree);
+        }
+    }
+
     private static class AddProperty implements Operation {
 
         private final NodeId parentId;

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java Thu Dec 18 10:43:31 2014
@@ -25,6 +25,7 @@ import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
 
 /**
  * This {@link ChangeLog} implementation simply keeps back all calls to its {@link Batch} methods as
@@ -80,5 +81,9 @@ public class ChangeLogImpl extends Abstr
         addOperation(Operations.setValue(propertyId, values));
     }
 
+    @Override
+    public void setTree(NodeId parentId, Tree contentTree) throws RepositoryException {
+        addOperation(Operations.setTree(parentId, contentTree));
+    }
 }
 

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java Thu Dec 18 10:43:31 2014
@@ -29,6 +29,7 @@ import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
 import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 
 /**
@@ -125,6 +126,11 @@ public class ConsolidatingChangeLog exte
         addOperation(CancelableOperations.setValue(propertyId, values));
     }
 
+    @Override
+    public void setTree(NodeId parentId, Tree contentTree) throws RepositoryException {
+        addOperation(CancelableOperations.setTree(parentId, contentTree));
+    }
+
     /**
      * Determines the cancellation behavior from the list of {@link ChangeLogImpl#operations operations}
      * and the current operation <code>op</code>:
@@ -772,6 +778,47 @@ public class ConsolidatingChangeLog exte
             return new SetValue(propertyId, values);
         }
 
+
+        //--------------------------------------------------------< SetTree >---
+        public static class SetTree extends Operations.SetTree implements CancelableOperation {
+
+            public SetTree(NodeId parentId, Tree contentTree) {
+                super(parentId, contentTree);
+            }
+
+            /**
+             * The cancellation only considers canceling the parent node, which corresponds
+             * to the policy node.
+             */
+            public int cancel(CancelableOperation other) throws RepositoryException {
+                if (other instanceof Remove) {
+                    Path thisPath = ConsolidatingChangeLog.getPath(parentId, tree.getName());
+                    Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+                    if (thisPath == null || otherPath == null) {
+                        return CANCEL_NONE;
+                    }
+                    if (thisPath.equals(otherPath)) {
+                        return CANCEL_BOTH;
+                    }
+                    return (thisPath.isDescendantOf(otherPath))
+                            ? CANCEL_THIS
+                            : CANCEL_NONE;
+                }
+                return CANCEL_NONE;
+            }
+        }
+
+        /**
+         * Factory method for creating an {@link SetTree} operation.
+         * @see Batch#setTree(NodeId, Tree)
+         *
+         * @param parentId
+         * @param tree
+         * @return
+         */
+        public static CancelableOperation setTree(NodeId parentId, Tree tree) {
+            return new SetTree(parentId, tree);
+        }
     }
 
 }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java Thu Dec 18 10:43:31 2014
@@ -26,6 +26,7 @@ import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
 
 /**
  * Factory for creating {@link Operation}s. The inner classes of this class
@@ -784,4 +785,68 @@ public final class Operations {
             : o.hashCode();
     }
 
+    //--------------------------------------------------------------< SetTree >---
+    public static class SetTree implements Operation {
+        protected final NodeId parentId;
+        protected final Tree tree;
+
+        public SetTree(NodeId parentId, Tree tree) {
+            super();
+            this.parentId = parentId;
+            this.tree = tree;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void apply(Batch batch) throws RepositoryException {
+            batch.setTree(parentId, tree);
+        }
+
+        //----------------------------< Object >---
+        @Override
+        public String toString() {
+            return "SetTree[" + parentId + ", " + tree+"]";
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (null == other) {
+                return false;
+            }
+            if (this == other) {
+                return true;
+            }
+            if (other instanceof SetTree) {
+                return equals((SetTree) other);
+            }
+            return false;
+        }
+
+        public boolean equals(SetTree other) {
+            return Operations.equals(parentId, other.parentId)
+                && Operations.equals(tree, other.tree);
+        }
+
+        @Override
+        public int hashCode() {
+            return 41 * (
+                          41 + Operations.hashCode(parentId))
+                       + Operations.hashCode(tree);
+        }
+    }
+
+    /**
+     * Factory method for creating an {@link SetTree} operation.
+     * @see Batch#addNode(NodeId, AddItem)
+     *
+     * @param parentId
+     * @param addItem
+     * @param nodetypeName
+     * @param uuid
+     * @return
+     */
+    public static Operation setTree(NodeId parentId, Tree contentTree) {
+        return new SetTree(parentId, contentTree);
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/BatchLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/BatchLogger.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/BatchLogger.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/BatchLogger.java Thu Dec 18 10:43:31 2014
@@ -24,6 +24,7 @@ import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
 
 /**
  * Log wrapper for a {@link Batch}.
@@ -141,5 +142,13 @@ public class BatchLogger extends Abstrac
             }}, "move(NodeId, NodeId, Name)", new Object[]{srcNodeId, destParentNodeId, destName});
     }
 
-
+    @Override
+    public void  setTree(final NodeId parentId, final Tree contentTree)
+            throws RepositoryException {
+        execute(new Callable() {
+            public Object call() throws RepositoryException {
+                batch.setTree(parentId, contentTree);
+                return null;
+            }}, "setTree(NodeId, Tree)", new Object[]{parentId, contentTree});
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java Thu Dec 18 10:43:31 2014
@@ -54,6 +54,7 @@ import org.apache.jackrabbit.spi.QueryIn
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
 import org.apache.jackrabbit.spi.Subscription;
+import org.apache.jackrabbit.spi.Tree;
 
 /**
  * Log wrapper for a {@link RepositoryService}.
@@ -201,6 +202,14 @@ public class RepositoryServiceLogger ext
         }, "getSupportedPrivileges(SessionInfo, NodeId)", new Object[]{unwrap(sessionInfo), nodeId});
     }
 
+    public PrivilegeDefinition[] getPrivileges(final SessionInfo sessionInfo, final NodeId nodeId) throws RepositoryException {
+        return (PrivilegeDefinition[]) execute(new Callable() {
+            public Object call() throws RepositoryException {
+                return service.getPrivileges(unwrap(sessionInfo), nodeId);
+            }
+        }, "getPrivileges(SessionInfo, NodeId)", new Object[]{unwrap(sessionInfo), nodeId});
+    }
+
     public QNodeDefinition getNodeDefinition(final SessionInfo sessionInfo, final NodeId nodeId)
             throws RepositoryException {
 
@@ -287,6 +296,14 @@ public class RepositoryServiceLogger ext
         }, "submit(Batch)", new Object[]{unwrap(batch)});
     }
 
+    @Override
+    public Tree createTree(final SessionInfo sessionInfo, final Batch batch, final Name nodeName, final Name primaryTypeName, final String uniqueId) throws RepositoryException {
+            return (Tree) execute(new Callable() {
+                public Object call() throws RepositoryException {
+                    return service.createTree(sessionInfo, batch, nodeName, primaryTypeName, uniqueId);
+                }}, "createTree(SessionInfo, Batch, Name, Name, String)", new Object[]{sessionInfo, batch, nodeName, primaryTypeName, uniqueId});
+    }
+
     public void importXml(final SessionInfo sessionInfo, final NodeId parentId, final InputStream xmlStream,
             final int uuidBehaviour) throws RepositoryException {
 

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/tree/AbstractTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/tree/AbstractTree.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/tree/AbstractTree.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/tree/AbstractTree.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Tree;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+
+public abstract class AbstractTree implements Tree {
+
+    private final Name nodeName;
+    private final Name ntName;
+    private final String uniqueId;
+
+    private final NamePathResolver resolver;
+
+    private List<Tree> children;
+
+    protected AbstractTree(Name nodeName, Name ntName, String uniqueId, NamePathResolver resolver) {
+        this.nodeName = nodeName;
+        this.ntName = ntName;
+        this.uniqueId = uniqueId;
+
+        this.resolver = resolver;
+    }
+
+    protected NamePathResolver getResolver() {
+        return resolver;
+    }
+    
+    protected List<Tree> getChildren() {
+        return children;
+    }
+
+    protected abstract Tree createChild(Name name, Name primaryTypeName, String uniqueId);
+
+    //---------------------------------------------------------------< Tree >---
+    @Override
+    public Name getName() {
+        return nodeName;
+    }
+
+    @Override
+    public Name getPrimaryTypeName() {
+        return ntName;
+    }
+
+    @Override
+    public String getUniqueId() {
+        return uniqueId;
+    }
+
+    @Override
+    public Tree addChild(Name childName, Name primaryTypeName, String uniqueId) {
+        Tree child = createChild(childName, primaryTypeName, uniqueId);
+        if (children == null) {
+            children = new ArrayList<Tree>();
+        }
+        children.add(child);
+        return child;
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Batch.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Batch.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Batch.java (original)
+++ jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Batch.java Thu Dec 18 10:43:31 2014
@@ -281,4 +281,13 @@ public interface Batch {
      * @see javax.jcr.Session#move(String, String)
      */
     public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException;
+
+    /**
+     * Add a new content tree to the persistent layer.
+     *
+     * @param parentId
+     * @param contentTree
+     * @throws RepositoryException
+     */
+    public void setTree(NodeId parentId, Tree contentTree) throws RepositoryException;
 }

Modified: jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java (original)
+++ jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java Thu Dec 18 10:43:31 2014
@@ -230,6 +230,16 @@ public interface RepositoryService {
 
     /**
      * TODO
+     * 
+     * @param sessionInfo
+     * @param id
+     * @return
+     * @throws RepositoryException
+     */
+    public PrivilegeDefinition[] getPrivileges(SessionInfo sessionInfo, NodeId id) throws RepositoryException;
+    
+    /**
+     * TODO
      *
      * @param sessionInfo
      * @param nodeId
@@ -412,6 +422,18 @@ public interface RepositoryService {
      */
     public void submit(Batch batch) throws PathNotFoundException, ItemNotFoundException, NoSuchNodeTypeException, ValueFormatException, VersionException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
 
+    /**
+     * Creates a new {@code Tree} that can be populated and later on be applied
+     * to the specified {@code Batch} by calling {@code #setTree}.
+     *
+     * @param nodeName
+     * @param primaryTypeName
+     * @param uniqueId
+     * @return a new {@code Tree} instance.
+     * @throws RepositoryException
+     */
+    public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException;
+
     //-------------------------------------------------------------< Import >---
     /**
      * Imports the data present in the given <code>InputStream</code> into the

Added: jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Tree.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Tree.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Tree.java (added)
+++ jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Tree.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Interface for building a hierarchy of JCR items on
+ * the SPI layer.
+ */
+public interface Tree {
+    
+    public Name getName();
+
+    public Name getPrimaryTypeName();
+
+    public String getUniqueId();
+
+    public void addProperty(Name propertyName, int propertyType, QValue value) throws RepositoryException;
+
+    public void addProperty(Name propertyName, int propertyType, QValue[] values) throws RepositoryException;
+
+    public Tree addChild(Name childName, Name primaryTypeName, String uniqueId);
+}

Added: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/BatchUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/BatchUtils.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/BatchUtils.java (added)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/BatchUtils.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi2dav;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.xml.Namespace;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+final class BatchUtils {
+
+    /**
+     * The XML elements and attributes used in serialization
+     */
+    private static final Namespace SV_NAMESPACE = Namespace.getNamespace(Name.NS_SV_PREFIX, Name.NS_SV_URI);
+    private static final String NODE_ELEMENT = "node";
+    private static final String PROPERTY_ELEMENT = "property";
+    private static final String VALUE_ELEMENT = "value";
+    private static final String NAME_ATTRIBUTE = "name";
+    private static final String TYPE_ATTRIBUTE = "type";
+
+    private BatchUtils() {};
+
+    static Element createNodeElement(Node parent, Name nodeName, Name primaryTypeName, String uniqueId, NamePathResolver resolver) throws NamespaceException {
+        Element nodeElement = DomUtil.addChildElement(parent, NODE_ELEMENT, SV_NAMESPACE);
+        String nameAttr = resolver.getJCRName(nodeName);
+        DomUtil.setAttribute(nodeElement, NAME_ATTRIBUTE, SV_NAMESPACE, nameAttr);
+
+        // nodetype must never be null
+        Element propElement = DomUtil.addChildElement(nodeElement, PROPERTY_ELEMENT, SV_NAMESPACE);
+        String name = resolver.getJCRName(NameConstants.JCR_PRIMARYTYPE);
+        DomUtil.setAttribute(propElement, NAME_ATTRIBUTE, SV_NAMESPACE, name);
+        DomUtil.setAttribute(propElement, TYPE_ATTRIBUTE, SV_NAMESPACE, PropertyType.nameFromValue(PropertyType.NAME));
+        name = resolver.getJCRName(primaryTypeName);
+        DomUtil.addChildElement(propElement, VALUE_ELEMENT, SV_NAMESPACE, name);
+        // optional uuid
+        if (uniqueId != null) {
+            propElement = DomUtil.addChildElement(nodeElement, PROPERTY_ELEMENT, SV_NAMESPACE);
+            name = resolver.getJCRName(NameConstants.JCR_UUID);
+            DomUtil.setAttribute(propElement, NAME_ATTRIBUTE, SV_NAMESPACE, name);
+            DomUtil.setAttribute(propElement, TYPE_ATTRIBUTE, SV_NAMESPACE, PropertyType.nameFromValue(PropertyType.STRING));
+            DomUtil.addChildElement(propElement, VALUE_ELEMENT, SV_NAMESPACE, uniqueId);
+        }
+        return nodeElement;
+    }
+
+    static void importProperty(Element nodeElement, Name propertyName, int type, QValue[] values, NamePathResolver resolver) throws RepositoryException {
+        Element propElement = DomUtil.addChildElement(nodeElement, PROPERTY_ELEMENT, SV_NAMESPACE);
+        DomUtil.setAttribute(propElement, NAME_ATTRIBUTE, SV_NAMESPACE, resolver.getJCRName(propertyName));
+        DomUtil.setAttribute(propElement, TYPE_ATTRIBUTE, SV_NAMESPACE, PropertyType.nameFromValue(type));
+
+        // build all the values.
+        for (QValue value : values) {
+            DomUtil.addChildElement(propElement, VALUE_ELEMENT, SV_NAMESPACE, value.getString());
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/DocumentTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/DocumentTree.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/DocumentTree.java (added)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/DocumentTree.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi2dav;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.jcr.RepositoryException;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.tree.AbstractTree;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+class DocumentTree extends AbstractTree {
+
+    private final List<Property> properties = new ArrayList<Property>();
+
+    protected DocumentTree(Name nodeName, Name ntName, String uniqueId, NamePathResolver resolver) {
+        super(nodeName, ntName, uniqueId, resolver);
+    }
+
+    //-------------------------------------------------------< AbstractTree >---
+    @Override
+    protected Tree createChild(Name name, Name primaryTypeName, String uniqueId) {
+        return new DocumentTree(name, primaryTypeName, uniqueId, getResolver());
+    }
+
+    //---------------------------------------------------------------< Tree >---
+    @Override
+    public void addProperty(Name propertyName, int propertyType, QValue value) throws RepositoryException {
+        addProperty(propertyName, propertyType, new QValue[]{value});
+    }
+
+    @Override
+    public void addProperty(Name propertyName, int propertyType, QValue[] values) throws RepositoryException {
+        properties.add(new Property(propertyName, propertyType, values));
+    }
+
+    //--------------------------------------------------------------------------
+    Document toDocument() throws RepositoryException {
+        try {
+            Document body = DomUtil.createDocument();
+            buildNodeInfo(body, this);
+            return body;
+        } catch (ParserConfigurationException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    private void buildNodeInfo(Node parent, DocumentTree tree) throws RepositoryException {
+        Element node = BatchUtils.createNodeElement(parent, tree.getName(), tree.getPrimaryTypeName(), tree.getUniqueId(), getResolver());
+        for (Property prop : properties) {
+            BatchUtils.importProperty(node, prop.name, prop.type, prop.values, getResolver());
+        }
+        for (Tree child : tree.getChildren()) {
+            buildNodeInfo(node, (DocumentTree) child);
+        }
+    }
+
+    private final static class Property {
+
+        private final Name name;
+        private final int type;
+        private final QValue[] values;
+
+        private Property(Name name, int type, QValue[] values) {
+            this.name = name;
+            this.type = type;
+            this.values = values;
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java Thu Dec 18 10:43:31 2014
@@ -113,6 +113,7 @@ import org.apache.jackrabbit.spi.QueryIn
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
 import org.apache.jackrabbit.spi.Subscription;
+import org.apache.jackrabbit.spi.Tree;
 import org.apache.jackrabbit.spi.commons.ChildInfoImpl;
 import org.apache.jackrabbit.spi.commons.EventBundleImpl;
 import org.apache.jackrabbit.spi.commons.EventFilterImpl;
@@ -201,7 +202,6 @@ import org.apache.jackrabbit.webdav.vers
 import org.apache.jackrabbit.webdav.version.report.ReportInfo;
 import org.apache.jackrabbit.webdav.xml.DomUtil;
 import org.apache.jackrabbit.webdav.xml.ElementIterator;
-import org.apache.jackrabbit.webdav.xml.Namespace;
 import org.apache.jackrabbit.webdav.xml.XmlSerializable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -925,6 +925,21 @@ public class RepositoryServiceImpl imple
         return internalGetPrivilegeDefinitions(sessionInfo, uri);
     }
 
+    @Override
+    public PrivilegeDefinition[] getPrivileges(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException {
+        String uri = (nodeId == null) ? uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()) : getItemUri(nodeId, sessionInfo);
+        return internalGetUserPrivilegeDefinitions(sessionInfo, uri);
+    }
+    
+    private PrivilegeDefinition[] internalGetUserPrivilegeDefinitions(SessionInfo sessionInfo, String uri) throws RepositoryException {
+        DavPropertyNameSet nameSet = new DavPropertyNameSet();
+        nameSet.add(SecurityConstants.CURRENT_USER_PRIVILEGE_SET);
+        DavMethodBase method = null;
+        
+        // TODO
+        return new PrivilegeDefinition[0];
+    }
+    
     private PrivilegeDefinition[] internalGetPrivilegeDefinitions(SessionInfo sessionInfo, String uri) throws RepositoryException {
         DavPropertyNameSet nameSet = new DavPropertyNameSet();
         nameSet.add(SecurityConstants.SUPPORTED_PRIVILEGE_SET);
@@ -944,7 +959,6 @@ public class RepositoryServiceImpl imple
                 return new PrivilegeDefinition[0];
             } else {
                 // build PrivilegeDefinition(s) from the supported-privileges dav property
-                NamePathResolver npResolver = getNamePathResolver(sessionInfo);
                 Map<Name, SupportedPrivilege> spMap = new HashMap<Name, SupportedPrivilege>();
                 fillSupportedPrivilegeMap(new SupportedPrivilegeSetProperty(p).getValue(), spMap, getNameFactory());
 
@@ -956,7 +970,9 @@ public class RepositoryServiceImpl imple
                     if (aggregates != null && aggregates.length > 0) {
                         aggrnames = new HashSet<Name>();
                         for (SupportedPrivilege aggregate : aggregates) {
-                            aggrnames.add(npResolver.getQName(aggregate.getPrivilege().getName()));
+                            Name aggregateName = nameFactory.create(aggregate.getPrivilege().getNamespace().getURI(), 
+                                                                    aggregate.getPrivilege().getName());
+                            aggrnames.add(aggregateName);
                         }
                     }
                     PrivilegeDefinition def = new PrivilegeDefinitionImpl(privilegeName, sp.isAbstract(), aggrnames);
@@ -1522,6 +1538,11 @@ public class RepositoryServiceImpl imple
         }
     }
 
+    @Override
+    public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException {
+        return new DocumentTree(nodeName, primaryTypeName, uniqueId, getNamePathResolver(sessionInfo));
+    }
+
     /**
      * @see RepositoryService#importXml(SessionInfo, NodeId, InputStream, int)
      */
@@ -2948,16 +2969,6 @@ public class RepositoryServiceImpl imple
         }
     }
 
-    /**
-     * The XML elements and attributes used in serialization
-     */
-    private static final Namespace SV_NAMESPACE = Namespace.getNamespace(Name.NS_SV_PREFIX, Name.NS_SV_URI);
-    private static final String NODE_ELEMENT = "node";
-    private static final String PROPERTY_ELEMENT = "property";
-    private static final String VALUE_ELEMENT = "value";
-    private static final String NAME_ATTRIBUTE = "name";
-    private static final String TYPE_ATTRIBUTE = "type";
-
     //------------------------------------------------< Inner Class 'Batch' >---
     private class BatchImpl implements Batch {
 
@@ -3056,9 +3067,7 @@ public class RepositoryServiceImpl imple
         }
 
         //----------------------------------------------------------< Batch >---
-        /**
-         * @see Batch#addNode(NodeId, Name, Name, String)
-         */
+        @Override
         public void addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) throws RepositoryException {
             checkConsumed();
             try {
@@ -3070,25 +3079,7 @@ public class RepositoryServiceImpl imple
 
                 // build 'sys-view' for the node to create and append it as request body
                 Document body = DomUtil.createDocument();
-                Element nodeElement = DomUtil.addChildElement(body, NODE_ELEMENT, SV_NAMESPACE);
-                String nameAttr = resolver.getJCRName(nodeName);
-                DomUtil.setAttribute(nodeElement, NAME_ATTRIBUTE, SV_NAMESPACE, nameAttr);
-
-                // nodetype must never be null
-                Element propElement = DomUtil.addChildElement(nodeElement, PROPERTY_ELEMENT, SV_NAMESPACE);
-                String name = resolver.getJCRName(NameConstants.JCR_PRIMARYTYPE);
-                DomUtil.setAttribute(propElement, NAME_ATTRIBUTE, SV_NAMESPACE, name);
-                DomUtil.setAttribute(propElement, TYPE_ATTRIBUTE, SV_NAMESPACE, PropertyType.nameFromValue(PropertyType.NAME));
-                name = resolver.getJCRName(nodetypeName);
-                DomUtil.addChildElement(propElement, VALUE_ELEMENT, SV_NAMESPACE, name);
-                // optional uuid
-                if (uuid != null) {
-                    propElement = DomUtil.addChildElement(nodeElement, PROPERTY_ELEMENT, SV_NAMESPACE);
-                    name = resolver.getJCRName(NameConstants.JCR_UUID);
-                    DomUtil.setAttribute(propElement, NAME_ATTRIBUTE, SV_NAMESPACE, name);
-                    DomUtil.setAttribute(propElement, TYPE_ATTRIBUTE, SV_NAMESPACE, PropertyType.nameFromValue(PropertyType.STRING));
-                    DomUtil.addChildElement(propElement, VALUE_ELEMENT, SV_NAMESPACE, uuid);
-                }
+                BatchUtils.createNodeElement(body, nodeName, nodetypeName, uuid, resolver);
                 method.setRequestBody(body);
 
                 methods.add(method);
@@ -3099,9 +3090,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#addProperty(NodeId, Name, QValue)
-         */
+        @Override
         public void addProperty(NodeId parentId, Name propertyName, QValue value) throws RepositoryException {
             checkConsumed();
             String uri = getItemUri(parentId, propertyName, sessionInfo);
@@ -3112,9 +3101,7 @@ public class RepositoryServiceImpl imple
             methods.add(method);
         }
 
-        /**
-         * @see Batch#addProperty(NodeId, Name, QValue[])
-         */
+        @Override
         public void addProperty(NodeId parentId, Name propertyName, QValue[] values) throws RepositoryException {
             checkConsumed();
             // TODO: avoid usage of the ValuesProperty. specially for binary props.
@@ -3135,9 +3122,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#setValue(PropertyId, QValue)
-         */
+        @Override
         public void setValue(PropertyId propertyId, QValue value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
             checkConsumed();
             if (value == null) {
@@ -3155,9 +3140,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#setValue(PropertyId, QValue[])
-         */
+        @Override
         public void setValue(PropertyId propertyId, QValue[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
             checkConsumed();
             if (values == null) {
@@ -3211,9 +3194,7 @@ public class RepositoryServiceImpl imple
             return ent;
         }
 
-        /**
-         * @see Batch#remove(ItemId)
-         */
+        @Override
         public void remove(ItemId itemId) throws RepositoryException {
             checkConsumed();
             String uri = getItemUri(itemId, sessionInfo);
@@ -3225,9 +3206,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#reorderNodes(NodeId, NodeId, NodeId)
-         */
+        @Override
         public void reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) throws RepositoryException {
             checkConsumed();
             try {
@@ -3251,9 +3230,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#setMixins(NodeId, Name[])
-         */
+        @Override
         public void setMixins(NodeId nodeId, Name[] mixinNodeTypeIds) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
             checkConsumed();
             try {
@@ -3282,9 +3259,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#setPrimaryType(NodeId, Name)
-         */
+        @Override
         public void setPrimaryType(NodeId nodeId, Name primaryNodeTypeName) throws RepositoryException {
             checkConsumed();
             try {
@@ -3300,9 +3275,7 @@ public class RepositoryServiceImpl imple
             }
         }
 
-        /**
-         * @see Batch#move(NodeId, NodeId, Name)
-         */
+        @Override
         public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException {
             checkConsumed();
             String uri = getItemUri(srcNodeId, sessionInfo);
@@ -3315,6 +3288,28 @@ public class RepositoryServiceImpl imple
             methods.add(method);
             clear = true;
         }
+
+        @Override
+        public void setTree(NodeId parentId, Tree tree) throws RepositoryException {
+            checkConsumed();
+
+            if (!(tree instanceof DocumentTree)) {
+                throw new RepositoryException("Invalid tree implementation " + tree.getClass().getName());
+            }
+            try {
+                // TODO: TOBEFIXED. WebDAV does not allow MKCOL for existing resource -> problem with SNS
+                // use fake name instead (see also #importXML)
+                Name fakeName = getNameFactory().create(Name.NS_DEFAULT_URI, UUID.randomUUID().toString());
+                String uri = getItemUri(parentId, fakeName, sessionInfo);
+                MkColMethod method = new MkColMethod(uri);
+
+                method.setRequestBody(((DocumentTree) tree).toDocument());
+
+                methods.add(method);
+            } catch (IOException e) {
+                throw new RepositoryException(e);
+            }
+        }
     }
 
     //----------------------------------------------< NamespaceResolverImpl >---

Added: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/JsonTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/JsonTree.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/JsonTree.java (added)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/JsonTree.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi2davex;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.json.JsonUtil;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Tree;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.tree.AbstractTree;
+
+class JsonTree extends AbstractTree {
+
+    private final StringBuilder properties = new StringBuilder();
+    private final List<Part> parts = new ArrayList<Part>();
+
+    JsonTree(Name nodeName, Name ntName, String uniqueId, NamePathResolver resolver) {
+        super(nodeName, ntName, uniqueId, resolver);
+    }
+
+    //-------------------------------------------------------< AbstractTree >---
+    @Override
+    protected Tree createChild(Name name, Name primaryTypeName, String uniqueId) {
+        return new JsonTree(name, primaryTypeName, uniqueId, getResolver());
+    }
+
+    //---------------------------------------------------------------< Tree >---
+    @Override
+    public void addProperty(Name propertyName, int propertyType, QValue value) throws RepositoryException {
+        properties.append(Utils.getJsonKey(getResolver().getJCRName(propertyName)));
+        properties.append(Utils.getJsonString(value));
+    }
+
+    @Override
+    public void addProperty(Name propertyName, int propertyType, QValue[] values) throws RepositoryException {
+        String name = getResolver().getJCRName(propertyName);
+        properties.append(',');
+        properties.append(Utils.getJsonKey(name));
+        int index = 0;
+        properties.append('[');
+        for (QValue value : values) {
+            String valueStr = Utils.getJsonString(value);
+            if (valueStr == null) {
+                Utils.addPart(name, value, getResolver(), parts);
+            } else {
+                String delim = (index++ == 0) ? "" : ",";
+                properties.append(delim).append('"').append(valueStr).append('"');
+            }
+        }
+        properties.append(']');
+    }
+
+    //--------------------------------------------------------------------------
+    String toJsonString(List<Part> batchParts) throws RepositoryException {
+        batchParts.addAll(this.parts);
+
+        StringBuilder json = new StringBuilder();
+        createJsonNodeFragment(json, this, true);
+        return json.toString();
+    }
+
+    //--------------------------------------------------------------------------
+    private String createJsonNodeFragment(StringBuilder json, JsonTree tree, boolean start) throws RepositoryException {
+        if (!start) {
+            json.append(',');
+            json.append(Utils.getJsonKey(getResolver().getJCRName(tree.getName())));
+        }
+        json.append('{');
+        json.append(Utils.getJsonKey(JcrConstants.JCR_PRIMARYTYPE));
+        json.append(JsonUtil.getJsonString(getResolver().getJCRName(tree.getPrimaryTypeName())));
+        String uuid = tree.getUniqueId();
+        if (uuid != null) {
+            json.append(',');
+            json.append(Utils.getJsonKey(JcrConstants.JCR_UUID));
+            json.append(JsonUtil.getJsonString(uuid));
+        }
+        // write all the properties.
+        json.append(properties);
+        for (Tree child : tree.getChildren()) {
+            createJsonNodeFragment(json, (JsonTree) child, false);
+        }
+        json.append('}');
+        return json.toString();
+    }
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java Thu Dec 18 10:43:31 2014
@@ -36,12 +36,10 @@ import org.apache.commons.httpclient.URI
 import org.apache.commons.httpclient.methods.RequestEntity;
 import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
 import org.apache.commons.httpclient.methods.multipart.Part;
-import org.apache.commons.httpclient.methods.multipart.PartBase;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.commons.json.JsonParser;
 import org.apache.jackrabbit.commons.json.JsonUtil;
 import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants;
-import org.apache.jackrabbit.commons.webdav.JcrValueType;
 import org.apache.jackrabbit.commons.webdav.ValueUtil;
 import org.apache.jackrabbit.spi.Batch;
 import org.apache.jackrabbit.spi.ItemId;
@@ -54,6 +52,7 @@ import org.apache.jackrabbit.spi.Propert
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.Tree;
 import org.apache.jackrabbit.spi.commons.ItemInfoCacheImpl;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
@@ -99,8 +98,6 @@ public class RepositoryServiceImpl exten
     private static final String ORDER_POSITION_LAST = "#last";
     private static final String ORDER_POSITION_BEFORE = "#before";
 
-    private static final String DEFAULT_CHARSET = "UTF-8";
-
     private static final DavPropertyName JCR_TYPE =
             DavPropertyName.create(ItemResourceConstants.JCR_TYPE_LN, ItemResourceConstants.NAMESPACE);
 
@@ -465,6 +462,11 @@ public class RepositoryServiceImpl exten
     }
 
     @Override
+    public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException {
+        return new JsonTree(nodeName, primaryTypeName, uniqueId, getNamePathResolver(sessionInfo));
+    }
+
+    @Override
     public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException {
         if (srcWorkspaceName.equals(sessionInfo.getWorkspaceName())) {
             super.copy(sessionInfo, srcWorkspaceName, srcNodeId, destParentNodeId, destName);
@@ -596,7 +598,7 @@ public class RepositoryServiceImpl exten
 
             // add the diff part - always do multipart in case the receiving servlet
             // engine has a form-size restriction (JCR-3726)
-            addPart(PARAM_DIFF, buf.toString());
+            Utils.addPart(PARAM_DIFF, buf.toString(), parts);
             Part[] partArr = parts.toArray(new Part[parts.size()]);
             RequestEntity entity = new MultipartRequestEntity(partArr, method.getParams());
             method.setRequestEntity(entity);
@@ -653,9 +655,7 @@ public class RepositoryServiceImpl exten
         }
 
         //----------------------------------------------------------< Batch >---
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void addNode(NodeId parentId, Name nodeName, Name nodetypeName,
                             String uuid) throws RepositoryException {
             assertMethod();
@@ -666,56 +666,46 @@ public class RepositoryServiceImpl exten
 
             StringWriter wr = new StringWriter();
             wr.write('{');
-            wr.write(getJsonKey(JcrConstants.JCR_PRIMARYTYPE));
+            wr.write(Utils.getJsonKey(JcrConstants.JCR_PRIMARYTYPE));
             wr.write(JsonUtil.getJsonString(getNamePathResolver(sessionInfo).getJCRName(nodetypeName)));
             if (uuid != null) {
                 wr.write(',');
-                wr.write(getJsonKey(JcrConstants.JCR_UUID));
+                wr.write(Utils.getJsonKey(JcrConstants.JCR_UUID));
                 wr.write(JsonUtil.getJsonString(uuid));
             }
             wr.write('}');
             appendDiff(SYMBOL_ADD_NODE, jcrPath, wr.toString());
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void addProperty(NodeId parentId, Name propertyName, QValue value) throws RepositoryException {
             assertMethod();
             Path p = getPathFactory().create(getPath(parentId, sessionInfo), propertyName, true);
             setProperty(p, value, false);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void addProperty(NodeId parentId, Name propertyName, QValue[] values) throws RepositoryException {
             assertMethod();
             Path p = getPathFactory().create(getPath(parentId, sessionInfo), propertyName, true);
             setProperty(p, values, false);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void setValue(PropertyId propertyId, QValue value) throws RepositoryException {
             assertMethod();
             Path p = getPath(propertyId, sessionInfo);
             setProperty(p, value, true);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void setValue(PropertyId propertyId, QValue[] values) throws RepositoryException {
             assertMethod();
             Path p = getPath(propertyId, sessionInfo);
             setProperty(p, values, true);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void remove(ItemId itemId) throws RepositoryException {
             assertMethod();
 
@@ -732,9 +722,7 @@ public class RepositoryServiceImpl exten
             }
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) throws RepositoryException {
             assertMethod();
 
@@ -758,9 +746,7 @@ public class RepositoryServiceImpl exten
             }
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void setMixins(NodeId nodeId, Name[] mixinNodeTypeNames) throws RepositoryException {
             assertMethod();
 
@@ -774,9 +760,7 @@ public class RepositoryServiceImpl exten
             setProperty(p, vs, true);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void setPrimaryType(NodeId nodeId, Name primaryNodeTypeName) throws RepositoryException {
             assertMethod();
 
@@ -787,9 +771,7 @@ public class RepositoryServiceImpl exten
             setProperty(p, v, true);
         }
 
-        /**
-         * @inheritDoc
-         */
+        @Override
         public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException {
             assertMethod();
 
@@ -802,6 +784,19 @@ public class RepositoryServiceImpl exten
             clear = true;
         }
 
+        @Override
+        public void setTree(NodeId parentId, Tree contentTree) throws RepositoryException {
+            assertMethod();
+            if (!(contentTree instanceof JsonTree)) {
+                throw new RepositoryException("Invalid Tree implementation : " + contentTree.getClass().getName());
+            }
+
+            Path normalizedPath = getPathFactory().create(getPath(parentId, sessionInfo), contentTree.getName(), true);
+            String jcrPath = getNamePathResolver(sessionInfo).getJCRPath(normalizedPath);
+            appendDiff(SYMBOL_ADD_NODE, jcrPath, ((JsonTree) contentTree).toJsonString(parts));
+        }
+
+        //----------------------------------------------------------------------
         /**
          *
          * @param symbol
@@ -830,10 +825,10 @@ public class RepositoryServiceImpl exten
                 clearPreviousSetProperty(jcrPropPath);
             }
 
-            String strValue = getJsonString(value);
+            String strValue = Utils.getJsonString(value);
             appendDiff(SYMBOL_SET_PROPERTY, jcrPropPath, strValue);
             if (strValue == null) {
-                addPart(jcrPropPath, value, resolver);
+                Utils.addPart(jcrPropPath, value, resolver, parts);
             }
         }
 
@@ -846,9 +841,9 @@ public class RepositoryServiceImpl exten
 
             StringBuilder strVal = new StringBuilder("[");
             for (int i = 0; i < values.length; i++) {
-                String str = getJsonString(values[i]);
+                String str = Utils.getJsonString(values[i]);
                 if (str == null) {
-                    addPart(jcrPropPath, values[i], resolver);
+                    Utils.addPart(jcrPropPath, values[i], resolver, parts);
                 } else {
                     String delim = (i == 0) ? "" : ",";
                     strVal.append(delim).append(str);
@@ -868,91 +863,12 @@ public class RepositoryServiceImpl exten
                 String entry = it.next();
                 if (entry.startsWith(key)) {
                     it.remove();
-                    removeParts(jcrPropPath);
+                    Utils.removeParts(jcrPropPath, parts);
                     return;
                 }
             }
         }
 
-        /**
-         *
-         * @param paramName
-         * @param value
-         */
-        private void addPart(String paramName, String value) {
-            parts.add(new StringPart(paramName, value, DEFAULT_CHARSET));
-        }
-
-        /**
-         *
-         * @param paramName
-         * @param value
-         * @param resolver
-         * @throws RepositoryException
-         */
-        private void addPart(String paramName, QValue value, NamePathResolver resolver) throws RepositoryException {
-            Part part;
-            switch (value.getType()) {
-                case PropertyType.BINARY:
-                    part = new BinaryPart(paramName, new BinaryPartSource(value), JcrValueType.contentTypeFromType(PropertyType.BINARY), DEFAULT_CHARSET);
-                    break;
-                case PropertyType.NAME:
-                    part = new StringPart(paramName, resolver.getJCRName(value.getName()), DEFAULT_CHARSET);
-                    break;
-                case PropertyType.PATH:
-                    part = new StringPart(paramName, resolver.getJCRPath(value.getPath()), DEFAULT_CHARSET);
-                    break;
-                default:
-                    part = new StringPart(paramName, value.getString(), DEFAULT_CHARSET);
-            }
-            String ctype = JcrValueType.contentTypeFromType(value.getType());
-            ((PartBase) part).setContentType(ctype);
-
-            parts.add(part);
-        }
-
-        private void removeParts(String paramName) {
-            for (Iterator<Part> it = parts.iterator(); it.hasNext();) {
-                Part part = it.next();
-                if (part.getName().equals(paramName)) {
-                    it.remove();
-                }
-            }
-        }
-
-        private String getJsonKey(String str) {
-            return JsonUtil.getJsonString(str) + ":";
-        }
-
-        private String getJsonString(QValue value) throws RepositoryException {
-            String str;
-            switch (value.getType()) {
-                case PropertyType.STRING:
-                    str = JsonUtil.getJsonString(value.getString());
-                    break;
-                case PropertyType.BOOLEAN:
-                case PropertyType.LONG:
-                    str = value.getString();
-                    break;
-                case PropertyType.DOUBLE:
-                    double d = value.getDouble();
-                    if (Double.isNaN(d) || Double.isInfinite(d)) {
-                    // JSON cannot specifically handle this property type...
-                        str = null;
-                    } else {
-                        str = value.getString();
-                        if (str.indexOf('.') == -1) {
-                            str += ".0";
-                        }
-                    }
-                    break;
-                default:
-                    // JSON cannot specifically handle this property type...
-                    str = null;
-            }
-            return str;
-        }
-
         private Path calcRemovePath(Path removedNodePath) throws RepositoryException {
             removed.put(removedNodePath, removedNodePath);
             Name name = removedNodePath.getName();

Added: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java?rev=1646435&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java (added)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java Thu Dec 18 10:43:31 2014
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi2davex;
+
+import java.util.Iterator;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.PartBase;
+import org.apache.jackrabbit.commons.webdav.JcrValueType;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.commons.json.JsonUtil;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+
+final class Utils {
+
+    private static final String DEFAULT_CHARSET = "UTF-8";
+
+    private Utils() {};
+
+    static String getJsonKey(String str) {
+        return JsonUtil.getJsonString(str) + ":";
+    }
+
+    static String getJsonString(QValue value) throws RepositoryException {
+        String str;
+        switch (value.getType()) {
+            case PropertyType.STRING:
+                str = JsonUtil.getJsonString(value.getString());
+                break;
+            case PropertyType.BOOLEAN:
+            case PropertyType.LONG:
+                str = value.getString();
+                break;
+            case PropertyType.DOUBLE:
+                double d = value.getDouble();
+                if (Double.isNaN(d) || Double.isInfinite(d)) {
+                    // JSON cannot specifically handle this property type...
+                    str = null;
+                } else {
+                    str = value.getString();
+                    if (str.indexOf('.') == -1) {
+                        str += ".0";
+                    }
+                }
+                break;
+            default:
+                // JSON cannot specifically handle this property type...
+                str = null;
+        }
+        return str;
+    }
+
+    /**
+     *
+     * @param paramName
+     * @param value
+     */
+    static void addPart(String paramName, String value, List<Part> parts) {
+        parts.add(new StringPart(paramName, value, DEFAULT_CHARSET));
+    }
+
+    /**
+     *
+     * @param paramName
+     * @param value
+     * @param resolver
+     * @throws RepositoryException
+     */
+    static void addPart(String paramName, QValue value, NamePathResolver resolver, List<Part> parts) throws RepositoryException {
+        Part part;
+        switch (value.getType()) {
+            case PropertyType.BINARY:
+                part = new BinaryPart(paramName, new BinaryPartSource(value), JcrValueType.contentTypeFromType(PropertyType.BINARY), DEFAULT_CHARSET);
+                break;
+            case PropertyType.NAME:
+                part = new StringPart(paramName, resolver.getJCRName(value.getName()), DEFAULT_CHARSET);
+                break;
+            case PropertyType.PATH:
+                part = new StringPart(paramName, resolver.getJCRPath(value.getPath()), DEFAULT_CHARSET);
+                break;
+            default:
+                part = new StringPart(paramName, value.getString(), DEFAULT_CHARSET);
+        }
+        String ctype = JcrValueType.contentTypeFromType(value.getType());
+        ((PartBase) part).setContentType(ctype);
+
+        parts.add(part);
+    }
+
+    static void removeParts(String paramName, List<Part> parts) {
+        for (Iterator<Part> it = parts.iterator(); it.hasNext();) {
+            Part part = it.next();
+            if (part.getName().equals(paramName)) {
+                it.remove();
+            }
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java?rev=1646435&r1=1646434&r2=1646435&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java Thu Dec 18 10:43:31 2014
@@ -45,6 +45,7 @@ import org.apache.jackrabbit.spi.QNodeTy
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.ItemInfo;
 import org.apache.jackrabbit.spi.ChildInfo;
+import org.apache.jackrabbit.spi.Tree;
 import org.apache.jackrabbit.spi.commons.EventFilterImpl;
 import org.apache.jackrabbit.spi.commons.EventBundleImpl;
 import org.apache.jackrabbit.spi.commons.ItemInfoCacheImpl;
@@ -373,6 +374,29 @@ public class RepositoryServiceImpl imple
         return pDefs;
     }
 
+    @Override
+    public PrivilegeDefinition[] getPrivileges(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException {
+        SessionInfoImpl sInfo = getSessionInfoImpl(sessionInfo);
+        String path = (nodeId == null) ? null : pathForId(nodeId, sInfo);
+        NamePathResolver npResolver = sInfo.getNamePathResolver();
+        
+        Privilege[] privs = sInfo.getSession().getAccessControlManager().getPrivileges(path);
+        List<PrivilegeDefinition> pDefs = new ArrayList<PrivilegeDefinition>(privs.length);
+        for (Privilege priv : privs) {
+            Name privName = npResolver.getQName(priv.getName());
+            Set<Name> aggrNames = null;
+            if (priv.isAggregate()) {
+                aggrNames = new HashSet<Name>();
+                for (Privilege dap : priv.getDeclaredAggregatePrivileges()) {
+                    aggrNames.add(npResolver.getQName(dap.getName()));
+                }
+            }
+            PrivilegeDefinition def = new PrivilegeDefinitionImpl(privName, priv.isAbstract(), aggrNames);
+            pDefs.add(def);
+        }
+        return pDefs.toArray(new PrivilegeDefinition[pDefs.size()]);
+    }
+    
     public PrivilegeDefinition[] getSupportedPrivileges(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException {
         SessionInfoImpl sInfo = getSessionInfoImpl(sessionInfo);
         String path = (nodeId == null) ? null : pathForId(nodeId, sInfo);
@@ -579,6 +603,11 @@ public class RepositoryServiceImpl imple
         }
     }
 
+    @Override
+    public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException {
+        return new XmlTree(nodeName, primaryTypeName, uniqueId, getSessionInfoImpl(sessionInfo).getNamePathResolver());
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -1453,6 +1482,8 @@ public class RepositoryServiceImpl imple
             this.sInfo = sInfo;
         }
 
+        //----------------------------------------------------------< Batch >---
+        @Override
         public void addNode(final NodeId parentId,
                             final Name nodeName,
                             final Name nodetypeName,
@@ -1484,6 +1515,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void addProperty(final NodeId parentId,
                                 final Name propertyName,
                                 final QValue value)
@@ -1500,6 +1532,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void addProperty(final NodeId parentId,
                                 final Name propertyName,
                                 final QValue[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, PathNotFoundException, ItemExistsException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
@@ -1518,6 +1551,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void setValue(final PropertyId propertyId, final QValue value)
                 throws RepositoryException {
             executeGuarded(new Callable() {
@@ -1531,6 +1565,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void setValue(final PropertyId propertyId, final QValue[] values)
                 throws RepositoryException {
             executeGuarded(new Callable() {
@@ -1547,6 +1582,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void remove(final ItemId itemId) throws RepositoryException {
             executeGuarded(new Callable() {
                 public Object run() throws RepositoryException {
@@ -1594,6 +1630,7 @@ public class RepositoryServiceImpl imple
             return nodeId;
         }
 
+        @Override
         public void reorderNodes(final NodeId parentId,
                                  final NodeId srcNodeId,
                                  final NodeId beforeNodeId)
@@ -1623,6 +1660,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void setMixins(final NodeId nodeId,
                               final Name[] mixinNodeTypeIds)
                 throws RepositoryException {
@@ -1651,6 +1689,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void setPrimaryType(final NodeId nodeId, final Name primaryNodeTypeName) throws RepositoryException {
             executeGuarded(new Callable() {
                 public Object run() throws RepositoryException {
@@ -1661,6 +1700,7 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
         public void move(final NodeId srcNodeId,
                          final NodeId destParentNodeId,
                          final Name destName) throws RepositoryException {
@@ -1678,6 +1718,36 @@ public class RepositoryServiceImpl imple
             });
         }
 
+        @Override
+        public void setTree(final NodeId parentId, Tree tree) throws RepositoryException {
+            if (!(tree instanceof XmlTree)) {
+                throw new RepositoryException("Unknown Tree implementation: " + tree.getClass().getName());
+            }
+
+            final XmlTree xmlTree = (XmlTree) tree;
+            executeGuarded(new Callable() {
+                public Object run() throws RepositoryException {
+                    Session s = sInfo.getSession();
+                    Node parent = getParent(parentId, sInfo);
+                    String xml = xmlTree.toXML();;
+                    InputStream in = new ByteArrayInputStream(xml.getBytes());
+                    try {
+                        s.importXML(parent.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+                    } catch (IOException e) {
+                        throw new RepositoryException(e.getMessage(), e);
+                    } finally {
+                        try {
+                            in.close();
+                        } catch (IOException e) {
+                            throw new RepositoryException(e.getMessage(), e);
+                        }
+                    }
+                    return null;
+                }
+            });
+        }
+
+        //----------------------------------------------------------------------
         private void executeGuarded(Callable call) throws RepositoryException {
             if (failed) {
                 return;