SLING-6723 : Make dependency to javax.jcr, jcr.contentloader and jcr.api optional

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1792881 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupport.java b/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupport.java
index bba35da..45330e1 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupport.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupport.java
@@ -188,6 +188,13 @@
         return null;
     }
 
+    public Object getItem(final Resource rsrc) {
+        if ( supportImpl != null ) {
+            return ((JCRSupportImpl)supportImpl).getItem(rsrc);
+        }
+        return null;
+    }
+
     public void setPrimaryNodeType(final Object node, final String type)
     throws PersistenceException {
         if ( node != null && supportImpl != null ) {
@@ -196,4 +203,16 @@
             throw new PersistenceException("Node type should be set but JCR support is not available");
         }
     }
+
+    public String copy(Object src, Object dstParent, String name)
+    throws PersistenceException {
+        // the caller already got an item and a node, so supportImpl is available
+        return ((JCRSupportImpl)supportImpl).copy(src, dstParent, name);
+    }
+
+    public void move(Object src, Object dstParent, String name)
+    throws PersistenceException {
+        // the caller already got an item and a node, so supportImpl is available
+        ((JCRSupportImpl)supportImpl).move(src, dstParent, name);
+    }
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupportImpl.java b/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupportImpl.java
index 9aa9cd0..ed641b0 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupportImpl.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/helper/JCRSupportImpl.java
@@ -20,10 +20,12 @@
 
 import java.util.List;
 
+import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -395,6 +397,10 @@
         return rsrc.adaptTo(Node.class);
     }
 
+    public Object getItem(final Resource rsrc) {
+        return rsrc.adaptTo(Item.class);
+    }
+
     public void setPrimaryNodeType(final Object node, final String type)
     throws PersistenceException {
         try {
@@ -403,4 +409,148 @@
             throw new PersistenceException(re.getMessage(), re);
         }
     }
+
+    public void move(Object src, Object dstParent, String name)
+    throws PersistenceException {
+        try {
+            final Session session = ((Item)src).getSession();
+            session.move(((Item)src).getPath(), ((Node)dstParent).getPath() + '/' + name);
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
+        }
+    }
+
+    /**
+     * Copy the <code>src</code> item into the <code>dstParent</code> node.
+     * The name of the newly created item is set to <code>name</code>.
+     *
+     * @param src The item to copy to the new location
+     * @param dstParent The node into which the <code>src</code> node is to be
+     *            copied
+     * @param name The name of the newly created item. If this is
+     *            <code>null</code> the new item gets the same name as the
+     *            <code>src</code> item.
+     * @throws PersistenceException May be thrown in case of any problem copying
+     *             the content.
+     * @see #copy(Node, Node, String)
+     * @see #copy(Property, Node, String)
+     */
+    public String copy(Object src, Object dstParent, String name)
+    throws PersistenceException {
+        try {
+            final Item result;
+            if (((Item)src).isNode()) {
+                result = copy((Node) src, (Node)dstParent, name);
+            } else {
+                result = copy((Property) src, (Node)dstParent, name);
+            }
+            return result.getPath();
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
+        }
+    }
+
+    /**
+     * Copy the <code>src</code> node into the <code>dstParent</code> node.
+     * The name of the newly created node is set to <code>name</code>.
+     * <p>
+     * This method does a recursive (deep) copy of the subtree rooted at the
+     * source node to the destination. Any protected child nodes and and
+     * properties are not copied.
+     *
+     * @param src The node to copy to the new location
+     * @param dstParent The node into which the <code>src</code> node is to be
+     *            copied
+     * @param name The name of the newly created node. If this is
+     *            <code>null</code> the new node gets the same name as the
+     *            <code>src</code> node.
+     * @throws RepositoryException May be thrown in case of any problem copying
+     *             the content.
+     */
+    private Item copy(Node src, Node dstParent, String name)
+            throws RepositoryException {
+
+        if(isAncestorOrSameNode(src, dstParent)) {
+            throw new RepositoryException(
+                    "Cannot copy ancestor " + src.getPath() + " to descendant " + dstParent.getPath());
+        }
+
+        // ensure destination name
+        if (name == null) {
+            name = src.getName();
+        }
+
+        // ensure new node creation
+        if (dstParent.hasNode(name)) {
+            dstParent.getNode(name).remove();
+        }
+
+        // create new node
+        Node dst = dstParent.addNode(name, src.getPrimaryNodeType().getName());
+        for (NodeType mix : src.getMixinNodeTypes()) {
+            dst.addMixin(mix.getName());
+        }
+
+        // copy the properties
+        for (PropertyIterator iter = src.getProperties(); iter.hasNext();) {
+            copy(iter.nextProperty(), dst, null);
+        }
+
+        // copy the child nodes
+        for (NodeIterator iter = src.getNodes(); iter.hasNext();) {
+            Node n = iter.nextNode();
+            if (!n.getDefinition().isProtected()) {
+                copy(n, dst, null);
+            }
+        }
+        return dst;
+    }
+
+    /** @return true if src is an ancestor node of dest, or if
+     *  both are the same node */
+    public static boolean isAncestorOrSameNode(Node src, Node dest) throws RepositoryException {
+        if(src.getPath().equals("/")) {
+            return true;
+        } else if(src.getPath().equals(dest.getPath())) {
+            return true;
+        } else if(dest.getPath().startsWith(src.getPath() + "/")) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Copy the <code>src</code> property into the <code>dstParent</code>
+     * node. The name of the newly created property is set to <code>name</code>.
+     * <p>
+     * If the source property is protected, this method does nothing.
+     *
+     * @param src The property to copy to the new location
+     * @param dstParent The node into which the <code>src</code> property is
+     *            to be copied
+     * @param name The name of the newly created property. If this is
+     *            <code>null</code> the new property gets the same name as the
+     *            <code>src</code> property.
+     * @throws RepositoryException May be thrown in case of any problem copying
+     *             the content.
+     */
+    private Item copy(Property src, Node dstParent, String name)
+            throws RepositoryException {
+        if (!src.getDefinition().isProtected()) {
+            if (name == null) {
+                name = src.getName();
+            }
+
+            // ensure new property creation
+            if (dstParent.hasProperty(name)) {
+                dstParent.getProperty(name).remove();
+            }
+
+            if (src.getDefinition().isMultiple()) {
+                return dstParent.setProperty(name, src.getValues());
+            }
+            return dstParent.setProperty(name, src.getValue());
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
index fd918f5..340bf57 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
@@ -18,16 +18,9 @@
 
 import java.util.List;
 
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.VersioningConfiguration;
 
@@ -50,149 +43,33 @@
             final String destName,
             final VersioningConfiguration versioningConfiguration)
     throws PersistenceException {
-        try {
-            // ensure we have an item underlying the request's resource
-            Item item = source.adaptTo(Item.class);
-            if (item == null) {
-                return null;
-            }
-
-            Item destItem = copy(item, (Node) item.getSession().getItem(destParent), destName);
-
-            String dest = destParent + "/" + destName;
+        final Resource parentRsrc = source.getResourceResolver().getResource(destParent);
+        // check if the item is backed by JCR
+        final Object item = this.jcrSsupport.getItem(source);
+        final Object parentItem = this.jcrSsupport.getNode(parentRsrc);
+        if ( item == null || parentItem == null ) {
+            // no JCR, copy via resources
+            final Resource result = copy(source, parentRsrc);
+            changes.add(Modification.onCopied(source.getPath(), result.getPath()));
+            return result;
+        } else {
+            final String dest = this.jcrSsupport.copy(item, parentItem, destName);
             changes.add(Modification.onCopied(source.getPath(), dest));
             log.debug("copy {} to {}", source, dest);
-            return source.getResourceResolver().getResource(destItem.getPath());
-        } catch ( final RepositoryException re) {
-            throw new PersistenceException(re.getMessage(), re, source.getPath(), null);
+            return source.getResourceResolver().getResource(dest);
         }
     }
 
     /**
-     * Copy the <code>src</code> item into the <code>dstParent</code> node.
-     * The name of the newly created item is set to <code>name</code>.
-     *
-     * @param src The item to copy to the new location
-     * @param dstParent The node into which the <code>src</code> node is to be
-     *            copied
-     * @param name The name of the newly created item. If this is
-     *            <code>null</code> the new item gets the same name as the
-     *            <code>src</code> item.
-     * @throws RepositoryException May be thrown in case of any problem copying
-     *             the content.
-     * @see #copy(Node, Node, String)
-     * @see #copy(Property, Node, String)
+     * Copy the source as a child resource to the parent
      */
-    static Item copy(Item src, Node dstParent, String name)
-            throws RepositoryException {
-        if (src.isNode()) {
-            return copy((Node) src, dstParent, name);
+    private Resource copy(final Resource source, final Resource dest)
+    throws PersistenceException {
+        final ValueMap vm = source.getValueMap();
+        final Resource result = source.getResourceResolver().create(dest, source.getName(), vm);
+        for(final Resource c : source.getChildren()) {
+            copy(c, result);
         }
-        return copy((Property) src, dstParent, name);
+        return result;
     }
-
-    /**
-     * Copy the <code>src</code> node into the <code>dstParent</code> node.
-     * The name of the newly created node is set to <code>name</code>.
-     * <p>
-     * This method does a recursive (deep) copy of the subtree rooted at the
-     * source node to the destination. Any protected child nodes and and
-     * properties are not copied.
-     *
-     * @param src The node to copy to the new location
-     * @param dstParent The node into which the <code>src</code> node is to be
-     *            copied
-     * @param name The name of the newly created node. If this is
-     *            <code>null</code> the new node gets the same name as the
-     *            <code>src</code> node.
-     * @throws RepositoryException May be thrown in case of any problem copying
-     *             the content.
-     */
-    static Item copy(Node src, Node dstParent, String name)
-            throws RepositoryException {
-
-        if(isAncestorOrSameNode(src, dstParent)) {
-            throw new RepositoryException(
-                    "Cannot copy ancestor " + src.getPath() + " to descendant " + dstParent.getPath());
-        }
-
-        // ensure destination name
-        if (name == null) {
-            name = src.getName();
-        }
-
-        // ensure new node creation
-        if (dstParent.hasNode(name)) {
-            dstParent.getNode(name).remove();
-        }
-
-        // create new node
-        Node dst = dstParent.addNode(name, src.getPrimaryNodeType().getName());
-        for (NodeType mix : src.getMixinNodeTypes()) {
-            dst.addMixin(mix.getName());
-        }
-
-        // copy the properties
-        for (PropertyIterator iter = src.getProperties(); iter.hasNext();) {
-            copy(iter.nextProperty(), dst, null);
-        }
-
-        // copy the child nodes
-        for (NodeIterator iter = src.getNodes(); iter.hasNext();) {
-            Node n = iter.nextNode();
-            if (!n.getDefinition().isProtected()) {
-                copy(n, dst, null);
-            }
-        }
-        return dst;
-    }
-
-    /** @return true if src is an ancestor node of dest, or if
-     *  both are the same node */
-    static boolean isAncestorOrSameNode(Node src, Node dest) throws RepositoryException {
-        if(src.getPath().equals("/")) {
-            return true;
-        } else if(src.getPath().equals(dest.getPath())) {
-            return true;
-        } else if(dest.getPath().startsWith(src.getPath() + "/")) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Copy the <code>src</code> property into the <code>dstParent</code>
-     * node. The name of the newly created property is set to <code>name</code>.
-     * <p>
-     * If the source property is protected, this method does nothing.
-     *
-     * @param src The property to copy to the new location
-     * @param dstParent The node into which the <code>src</code> property is
-     *            to be copied
-     * @param name The name of the newly created property. If this is
-     *            <code>null</code> the new property gets the same name as the
-     *            <code>src</code> property.
-     * @throws RepositoryException May be thrown in case of any problem copying
-     *             the content.
-     */
-    static Item copy(Property src, Node dstParent, String name)
-            throws RepositoryException {
-        if (!src.getDefinition().isProtected()) {
-            if (name == null) {
-                name = src.getName();
-            }
-
-            // ensure new property creation
-            if (dstParent.hasProperty(name)) {
-                dstParent.getProperty(name).remove();
-            }
-
-            if (src.getDefinition().isMultiple()) {
-                return dstParent.setProperty(name, src.getValues());
-            }
-            return dstParent.setProperty(name, src.getValue());
-        }
-        return null;
-    }
-
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
index 1ded8c7..158f8ef 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
@@ -21,14 +21,9 @@
 import java.util.List;
 import java.util.Map;
 
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
 import javax.servlet.ServletContext;
 
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.sling.api.SlingException;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.ModifiableValueMap;
@@ -78,33 +73,28 @@
                     final PostResponse response,
                     final List<Modification> changes)
     throws PersistenceException {
+        final Map<String, RequestProperty> reqProperties = collectContent(request, response);
 
-        try {
-            final Map<String, RequestProperty> reqProperties = collectContent(request, response);
+        final VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
 
-            final VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
+        // do not change order unless you have a very good reason.
 
-            // do not change order unless you have a very good reason.
+        // ensure root of new content
+        processCreate(request.getResourceResolver(), reqProperties, response, changes, versioningConfiguration);
 
-            // ensure root of new content
-            processCreate(request.getResourceResolver(), reqProperties, response, changes, versioningConfiguration);
+        // write content from existing content (@Move/CopyFrom parameters)
+        processMoves(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
+        processCopies(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
 
-            // write content from existing content (@Move/CopyFrom parameters)
-            processMoves(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
-            processCopies(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
+        // cleanup any old content (@Delete parameters)
+        processDeletes(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
 
-            // cleanup any old content (@Delete parameters)
-            processDeletes(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
+        // write content from form
+        writeContent(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
 
-            // write content from form
-            writeContent(request.getResourceResolver(), reqProperties, changes, versioningConfiguration);
-
-            // order content
-            final Resource newResource = request.getResourceResolver().getResource(response.getPath());
-            this.jcrSsupport.orderNode(request, newResource, changes);
-        } catch ( final RepositoryException pe) {
-            throw new PersistenceException(pe.getMessage(), pe);
-        }
+        // order content
+        final Resource newResource = request.getResourceResolver().getResource(response.getPath());
+        this.jcrSsupport.orderNode(request, newResource, changes);
     }
 
     @Override
@@ -180,7 +170,7 @@
     private void processMoves(final ResourceResolver resolver,
             Map<String, RequestProperty> reqProperties, List<Modification> changes,
             VersioningConfiguration versioningConfiguration)
-            throws RepositoryException, PersistenceException {
+            throws PersistenceException {
 
         for (RequestProperty property : reqProperties.values()) {
             if (property.hasRepositoryMoveSource()) {
@@ -198,7 +188,7 @@
     private void processCopies(final ResourceResolver resolver,
             Map<String, RequestProperty> reqProperties, List<Modification> changes,
             VersioningConfiguration versioningConfiguration)
-            throws RepositoryException, PersistenceException {
+            throws PersistenceException {
 
         for (RequestProperty property : reqProperties.values()) {
             if (property.hasRepositoryCopySource()) {
@@ -210,8 +200,8 @@
 
     /**
      * Internal implementation of the
-     * {@link #processCopies(Session, Map, HtmlResponse)} and
-     * {@link #processMoves(Session, Map, HtmlResponse)} methods taking into
+     * {@link #processCopies(ResourceResolver, Map, HtmlResponse)} and
+     * {@link #processMoves(ResourceResolver, Map, HtmlResponse)} methods taking into
      * account whether the source is actually a property or a node.
      * <p>
      * Any intermediary nodes to the destination as indicated by the
@@ -222,35 +212,34 @@
      *            content of the operation.
      * @param isMove <code>true</code> if the source item is to be moved.
      *            Otherwise the source item is just copied.
-     * @param session The repository session to use to access the content
+     * @param resolver The resource resolver to use to access the content
      * @param reqProperties All accepted request properties. This is used to
      *            create intermediary nodes along the property path.
-     * @param response The <code>HtmlResponse</code> into which successfull
+     * @param response The <code>HtmlResponse</code> into which successful
      *            copies and moves as well as intermediary node creations are
      *            recorded.
-     * @throws RepositoryException May be thrown if an error occurrs.
+     * @throws PersistenceException May be thrown if an error occurs.
      */
     private void processMovesCopiesInternal(
                     RequestProperty property,
             boolean isMove, final ResourceResolver resolver,
             Map<String, RequestProperty> reqProperties, List<Modification> changes,
             VersioningConfiguration versioningConfiguration)
-            throws RepositoryException, PersistenceException {
+            throws PersistenceException {
 
-        final Session session = resolver.adaptTo(Session.class);
         String propPath = property.getPath();
         String source = property.getRepositorySource();
 
         // only continue here, if the source really exists
-        if (session.itemExists(source)) {
+        if (resolver.getResource(source) != null ) {
 
             // if the destination item already exists, remove it
             // first, otherwise ensure the parent location
-            if (session.itemExists(propPath)) {
-                Node parent = session.getItem(propPath).getParent();
-                this.jcrSsupport.checkoutIfNecessary(resolver.getResource(parent.getPath()), changes, versioningConfiguration);
+            if (resolver.getResource(propPath) != null) {
+                final Resource parent = resolver.getResource(propPath).getParent();
+                this.jcrSsupport.checkoutIfNecessary(parent, changes, versioningConfiguration);
 
-                session.getItem(propPath).remove();
+                resolver.delete(resolver.getResource(propPath));
                 changes.add(Modification.onDeleted(propPath));
             } else {
                 Resource parent = deepGetOrCreateResource(resolver, property.getParentPath(),
@@ -259,35 +248,30 @@
             }
 
             // move through the session and record operation
-            Item sourceItem = session.getItem(source);
-            if (sourceItem.isNode()) {
-
-                // node move/copy through session
-                if (isMove) {
-                    this.jcrSsupport.checkoutIfNecessary(resolver.getResource(sourceItem.getParent().getPath()), changes, versioningConfiguration);
-                    session.move(source, propPath);
+            // check if the item is backed by JCR
+            Resource sourceRsrc = resolver.getResource(source);
+            final Object sourceItem = this.jcrSsupport.getItem(sourceRsrc);
+            final Object destItem = this.jcrSsupport.getItem(resolver.getResource(property.getParentPath()));
+            if ( sourceItem != null && destItem != null ) {
+                if ( this.jcrSsupport.isNode(sourceRsrc) ) {
+                    if ( isMove ) {
+                        this.jcrSsupport.checkoutIfNecessary(sourceRsrc.getParent(), changes, versioningConfiguration);
+                        this.jcrSsupport.move(sourceItem, destItem, ResourceUtil.getName(propPath));
+                    } else {
+                        this.jcrSsupport.checkoutIfNecessary(resolver.getResource(property.getParentPath()), changes, versioningConfiguration);
+                        this.jcrSsupport.copy(sourceItem, destItem, property.getName());
+                    }
                 } else {
-                    Node sourceNode = (Node) sourceItem;
-                    Node destParent = (Node) session.getItem(property.getParentPath());
-                    this.jcrSsupport.checkoutIfNecessary(resolver.getResource(destParent.getPath()), changes, versioningConfiguration);
-                    CopyOperation.copy(sourceNode, destParent,
-                        property.getName());
-                }
+                    // property: move manually
+                    this.jcrSsupport.checkoutIfNecessary(resolver.getResource(property.getParentPath()), changes, versioningConfiguration);
+                    // create destination property
+                    this.jcrSsupport.copy(sourceItem, destItem, ResourceUtil.getName(source));
 
-            } else {
-
-                // property move manually
-                Property sourceProperty = (Property) sourceItem;
-
-                // create destination property
-                Node destParent = (Node) session.getItem(property.getParentPath());
-                this.jcrSsupport.checkoutIfNecessary(resolver.getResource(destParent.getPath()), changes, versioningConfiguration);
-                CopyOperation.copy(sourceProperty, destParent, null);
-
-                // remove source property (if not just copying)
-                if (isMove) {
-                    this.jcrSsupport.checkoutIfNecessary(resolver.getResource(sourceProperty.getParent().getPath()), changes, versioningConfiguration);
-                    sourceProperty.remove();
+                    // remove source property (if not just copying)
+                    if ( isMove ) {
+                        this.jcrSsupport.checkoutIfNecessary(sourceRsrc.getParent(), changes, versioningConfiguration);
+                        resolver.delete(sourceRsrc);
+                    }
                 }
             }
 
@@ -314,14 +298,14 @@
      *            properties to be removed.
      * @param response The <code>HtmlResponse</code> to be updated with
      *            information on deleted properties.
-     * @throws RepositoryException Is thrown if an error occurrs checking or
+     * @throws PersistenceException Is thrown if an error occurs checking or
      *             removing properties.
      */
     private void processDeletes(final ResourceResolver resolver,
             final Map<String, RequestProperty> reqProperties,
             final List<Modification> changes,
             final VersioningConfiguration versioningConfiguration)
-    throws RepositoryException, PersistenceException {
+    throws PersistenceException {
 
         for (final RequestProperty property : reqProperties.values()) {
 
@@ -330,32 +314,19 @@
                 if ( parent == null ) {
                     continue;
                 }
-                final Node parentNode = parent.adaptTo(Node.class);
+                this.jcrSsupport.checkoutIfNecessary(parent, changes, versioningConfiguration);
 
-                if ( parentNode != null ) {
-                    this.jcrSsupport.checkoutIfNecessary(parent, changes, versioningConfiguration);
-
-                    if (property.getName().equals("jcr:mixinTypes")) {
-
-                        // clear all mixins
-                        for (NodeType mixin : parentNode.getMixinNodeTypes()) {
-                            parentNode.removeMixin(mixin.getName());
-                        }
-
-                    } else {
-                        if ( parentNode.hasProperty(property.getName())) {
-                            parentNode.getProperty(property.getName()).remove();
-                        } else if ( parentNode.hasNode(property.getName())) {
-                            parentNode.getNode(property.getName()).remove();
-                        }
-                    }
-
-                } else {
-                    final ValueMap vm = parent.adaptTo(ModifiableValueMap.class);
-                    if ( vm == null ) {
-                        throw new PersistenceException("Resource '" + parent.getPath() + "' is not modifiable.");
-                    }
+                final ValueMap vm = parent.adaptTo(ModifiableValueMap.class);
+                if ( vm == null ) {
+                    throw new PersistenceException("Resource '" + parent.getPath() + "' is not modifiable.");
+                }
+                if ( vm.containsKey(property.getName()) ) {
                     vm.remove(property.getName());
+                } else {
+                    final Resource childRsrc = resolver.getResource(parent.getPath() + '/' + property.getName());
+                    if ( childRsrc != null ) {
+                        resolver.delete(childRsrc);
+                    }
                 }
 
                 changes.add(Modification.onDeleted(property.getPath()));
@@ -367,14 +338,13 @@
     /**
      * Writes back the content
      *
-     * @throws RepositoryException if a repository error occurs
      * @throws PersistenceException if a persistence error occurs
      */
     private void writeContent(final ResourceResolver resolver,
             final Map<String, RequestProperty> reqProperties,
             final List<Modification> changes,
             final VersioningConfiguration versioningConfiguration)
-    throws RepositoryException, PersistenceException {
+    throws PersistenceException {
 
         final SlingPropertyValueHandler propHandler = new SlingPropertyValueHandler(
             dateParser, this.jcrSsupport, changes);
@@ -387,8 +357,8 @@
                 this.jcrSsupport.checkoutIfNecessary(parent, changes, versioningConfiguration);
 
                 // skip jcr special properties
-                if (prop.getName().equals("jcr:primaryType")
-                    || prop.getName().equals("jcr:mixinTypes")) {
+                if (prop.getName().equals(JcrConstants.JCR_PRIMARYTYPE)
+                    || prop.getName().equals(JcrConstants.JCR_MIXINTYPES)) {
                     continue;
                 }
 
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/MoveOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/MoveOperation.java
index eda2acb..a7a804b 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/MoveOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/MoveOperation.java
@@ -18,12 +18,9 @@
 
 import java.util.List;
 
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.VersioningConfiguration;
 
@@ -46,37 +43,46 @@
             String destName,
             final VersioningConfiguration versioningConfiguration)
     throws PersistenceException {
-        try {
-            // ensure we have an item underlying the request's resource
-            Item item = source.adaptTo(Item.class);
-            if (item == null) {
-                return null;
-            }
-
-            if (destName == null) {
-                destName = source.getName();
-            }
-
-            String sourcePath = source.getPath();
-            if (destParent.equals("/")) {
-                destParent = "";
-            }
-            String destPath = destParent + "/" + destName;
-            Session session = item.getSession();
-
-            this.jcrSsupport.checkoutIfNecessary(source.getParent(), changes, versioningConfiguration);
-
-            final Resource dest = source.getResourceResolver().getResource(destPath);
-            if (dest != null ) {
-                source.getResourceResolver().delete(dest);
-            }
-
-            session.move(sourcePath, destPath);
-            changes.add(Modification.onMoved(sourcePath, destPath));
-            return source.getResourceResolver().getResource(destPath);
-        } catch ( final RepositoryException re) {
-            throw new PersistenceException(re.getMessage(), re);
+        if (destName == null) {
+            destName = source.getName();
         }
+
+        if (destParent.equals("/")) {
+            destParent = "";
+        }
+        final String destPath = destParent + "/" + destName;
+
+        final Resource destParentRsrc = source.getResourceResolver().getResource(destParent);
+        final Resource dest = destParentRsrc.getChild(destName);
+        if (dest != null ) {
+            source.getResourceResolver().delete(dest);
+        }
+
+        // ensure we have an item underlying the request's resource
+        final Object item = this.jcrSsupport.getItem(source);
+        final Object target = this.jcrSsupport.getNode(destParentRsrc);
+        if (item == null || target == null ) {
+            move(source, destParentRsrc);
+        } else {
+            this.jcrSsupport.checkoutIfNecessary(source.getParent(), changes, versioningConfiguration);
+            this.jcrSsupport.move(item, target, destName);
+        }
+        changes.add(Modification.onMoved(source.getPath(), destPath));
+        return source.getResourceResolver().getResource(destPath);
     }
 
+    /**
+     * Move the source as a child resource to the parent
+     */
+    private void move(final Resource source, final Resource dest)
+    throws PersistenceException {
+        // first copy
+        final ValueMap vm = source.getValueMap();
+        final Resource result = source.getResourceResolver().create(dest, source.getName(), vm);
+        for(final Resource c : source.getChildren()) {
+            move(c, result);
+        }
+        // then delete
+        source.getResourceResolver().delete(source);
+    }
 }
diff --git a/src/test/java/org/apache/sling/servlets/post/impl/operations/CopyOperationTest.java b/src/test/java/org/apache/sling/servlets/post/impl/operations/CopyOperationTest.java
index 4e3fa9d..dca13aa 100644
--- a/src/test/java/org/apache/sling/servlets/post/impl/operations/CopyOperationTest.java
+++ b/src/test/java/org/apache/sling/servlets/post/impl/operations/CopyOperationTest.java
@@ -21,8 +21,7 @@
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 
-import junit.framework.TestCase;
-
+import org.apache.sling.servlets.post.impl.helper.JCRSupportImpl;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
 import org.jmock.integration.junit4.JMock;
@@ -30,16 +29,18 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import junit.framework.TestCase;
+
 @RunWith(JMock.class)
 public class CopyOperationTest extends TestCase {
     private Mockery context = new JUnit4Mockery();
     private int counter;
-    
+
     private void assertResult(final String srcPath, final String destPath, Boolean expectedResult) throws RepositoryException {
         counter++;
         final Node src = context.mock(Node.class, "src" + counter);
         final Node dest = context.mock(Node.class, "dest" + counter);
-        
+
         context.checking(new Expectations() {
             {
                 allowing(src).getPath();
@@ -49,12 +50,12 @@
             }
         });
 
-        final boolean result = CopyOperation.isAncestorOrSameNode(src, dest);
+        final boolean result = JCRSupportImpl.isAncestorOrSameNode(src, dest);
         assertEquals(
                 "Expecting isAncestorOrSameNode to be " + expectedResult + " for " + srcPath + " and " + destPath,
                 expectedResult.booleanValue(), result);
     }
-    
+
     @Test
     public void testIsAncestorOrSameNode() throws RepositoryException {
         final Object [] testCases = {
@@ -68,10 +69,10 @@
                 "/ab/cd", "/ab/cde", false,
                 "/ab", "/cd", false,
         };
-        
+
         for(int i=0; i < testCases.length; i+=3) {
             assertResult((String)testCases[i], (String)testCases[i+1], (Boolean)(testCases[i+2]));
         }
-        
+
     }
 }
\ No newline at end of file