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

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1789343 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java b/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
index e837d20..2b65cbf 100644
--- a/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
+++ b/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
@@ -320,13 +320,13 @@
      * not set or set to any other value.
      */
     public static final String RP_STATUS = RP_PREFIX + "status";
-    
+
 	/**
-     * Optional request parameter: defines if to enable the error handling 
-     * also for POST request. 
+     * Optional request parameter: defines if to enable the error handling
+     * also for POST request.
      * The parameter value is checked to see if it matches the case-insensitive
-     * value true. 
-     * 
+     * value true.
+     *
      * @since 2.2.0 (Bundle version 2.3.0)
      */
     public static final String RP_SEND_ERROR = RP_PREFIX + "sendError";
@@ -508,15 +508,15 @@
     /**
      * Name of the request parameter indicating whether versionable nodes should
      * be checked in during an {@link SlingPostConstants#OPERATION_IMPORT} operation.
-     * 
+     *
      * @since 2.1.2
      */
     public static final String RP_AUTO_CHECKOUT = RP_PREFIX + "autoCheckout";
 
     /**
      * Name of the request attribute (not parameter) indicating that a post operation
-     * should not invoke session.save() upon completion.
-     * 
+     * should not invoke the commit method on the resource resolver upon completion.
+     *
      * @since 2.1.2
      */
     public static final String ATTR_SKIP_SESSION_HANDLING = "skip-session-handling";
@@ -582,7 +582,7 @@
      * @since 2.3.4
      */
     public static final String NT_SLING_CHUNK_OFFSET = "sling:offset";
-    
+
     /**
      * Constant for prefix for sling:chunk node name.
      * @since 2.3.4
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java
index e39d4f1..4f80685 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java
@@ -19,13 +19,13 @@
 import java.util.Iterator;
 import java.util.List;
 
-import javax.jcr.Item;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.servlets.post.Modification;
@@ -44,107 +44,106 @@
     protected final void doRun(SlingHttpServletRequest request,
             PostResponse response,
             List<Modification> changes)
-    throws RepositoryException {
-        Session session = request.getResourceResolver().adaptTo(Session.class);
+    throws PersistenceException {
+        try {
+            Session session = request.getResourceResolver().adaptTo(Session.class);
 
-        VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
+            VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
 
-        Resource resource = request.getResource();
-        String source = resource.getPath();
+            Resource resource = request.getResource();
+            String source = resource.getPath();
 
-        // ensure dest is not empty/null and is absolute
-        String dest = request.getParameter(SlingPostConstants.RP_DEST);
-        if (dest == null || dest.length() == 0) {
-            throw new IllegalArgumentException("Unable to process "
-                + getOperationName() + ". Missing destination");
-        }
-
-        // register whether the path ends with a trailing slash
-        boolean trailingSlash = dest.endsWith("/");
-
-        // ensure destination is an absolute and normalized path
-        if (!dest.startsWith("/")) {
-            dest = ResourceUtil.getParent(source) + "/" + dest;
-        }
-        dest = ResourceUtil.normalize(dest);
-
-        // destination parent and name
-        String dstParent = trailingSlash ? dest : ResourceUtil.getParent(dest);
-
-        // delete destination if already exists
-        if (!trailingSlash && session.itemExists(dest)) {
-
-            final String replaceString = request.getParameter(SlingPostConstants.RP_REPLACE);
-            final boolean isReplace = "true".equalsIgnoreCase(replaceString);
-            if (!isReplace) {
-                response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
-                    "Cannot " + getOperationName() + " " + resource + " to "
-                        + dest + ": destination exists");
-                return;
-            } else {
-                checkoutIfNecessary(session.getItem(dest).getParent(), changes, versioningConfiguration);
+            // ensure dest is not empty/null and is absolute
+            String dest = request.getParameter(SlingPostConstants.RP_DEST);
+            if (dest == null || dest.length() == 0) {
+                throw new IllegalArgumentException("Unable to process "
+                    + getOperationName() + ". Missing destination");
             }
 
-        } else {
+            // register whether the path ends with a trailing slash
+            boolean trailingSlash = dest.endsWith("/");
 
-            // check if path to destination exists and create it, but only
-            // if it's a descendant of the current node
-            if (!dstParent.equals("")) {
-                if (session.itemExists(dstParent)) {
-                    checkoutIfNecessary((Node) session.getItem(dstParent), changes, versioningConfiguration);
-                } else {
+            // ensure destination is an absolute and normalized path
+            if (!dest.startsWith("/")) {
+                dest = ResourceUtil.getParent(source) + "/" + dest;
+            }
+            dest = ResourceUtil.normalize(dest);
+
+            // destination parent and name
+            String dstParent = trailingSlash ? dest : ResourceUtil.getParent(dest);
+
+            // delete destination if already exists
+            if (!trailingSlash && session.itemExists(dest)) {
+
+                final String replaceString = request.getParameter(SlingPostConstants.RP_REPLACE);
+                final boolean isReplace = "true".equalsIgnoreCase(replaceString);
+                if (!isReplace) {
                     response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
                         "Cannot " + getOperationName() + " " + resource + " to "
-                            + dest + ": parent of destination does not exist");
+                            + dest + ": destination exists");
                     return;
+                } else {
+                    checkoutIfNecessary(session.getItem(dest).getParent(), changes, versioningConfiguration);
                 }
+
+            } else {
+
+                // check if path to destination exists and create it, but only
+                // if it's a descendant of the current node
+                if (!dstParent.equals("")) {
+                    if (session.itemExists(dstParent)) {
+                        checkoutIfNecessary((Node) session.getItem(dstParent), changes, versioningConfiguration);
+                    } else {
+                        response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
+                            "Cannot " + getOperationName() + " " + resource + " to "
+                                + dest + ": parent of destination does not exist");
+                        return;
+                    }
+                }
+
+                // the destination is newly created, hence a create request
+                response.setCreateRequest(true);
             }
 
-            // the destination is newly created, hence a create request
-            response.setCreateRequest(true);
-        }
+            Iterator<Resource> resources = getApplyToResources(request);
+            Resource destResource = null;
+            if (resources == null) {
 
-        Iterator<Resource> resources = getApplyToResources(request);
-        Item destItem = null;
-        if (resources == null) {
 
-            // ensure we have an item underlying the request's resource
-            Item item = resource.adaptTo(Item.class);
-            if (item == null) {
+                String dstName = trailingSlash ? null : ResourceUtil.getName(dest);
+                destResource = execute(changes, resource, dstParent, dstName, versioningConfiguration);
+
+            } else {
+
+                // multiple applyTo requires trailing slash on destination
+                if (!trailingSlash) {
+                    throw new IllegalArgumentException(
+                        "Applying "
+                            + getOperationName()
+                            + " to multiple resources requires a trailing slash on the destination");
+                }
+
+                // multiple copy will never return 201/CREATED
+                response.setCreateRequest(false);
+
+                while (resources.hasNext()) {
+                    Resource applyTo = resources.next();
+                    execute(changes, applyTo, dstParent, null, versioningConfiguration);
+                }
+                destResource = request.getResourceResolver().getResource(dest);
+
+            }
+
+            if ( destResource == null ) {
                 response.setStatus(HttpServletResponse.SC_NOT_FOUND,
-                    "Missing source " + resource + " for " + getOperationName());
+                        "Missing source " + resource + " for " + getOperationName());
                 return;
             }
-
-            String dstName = trailingSlash ? null : ResourceUtil.getName(dest);
-            destItem = execute(changes, item, dstParent, dstName, versioningConfiguration);
-
-        } else {
-
-            // multiple applyTo requires trailing slash on destination
-            if (!trailingSlash) {
-                throw new IllegalArgumentException(
-                    "Applying "
-                        + getOperationName()
-                        + " to multiple resources requires a trailing slash on the destination");
-            }
-
-            // multiple copy will never return 201/CREATED
-            response.setCreateRequest(false);
-
-            while (resources.hasNext()) {
-                Resource applyTo = resources.next();
-                Item item = applyTo.adaptTo(Item.class);
-                if (item != null) {
-                    execute(changes, item, dstParent, null, versioningConfiguration);
-                }
-            }
-            destItem = session.getItem(dest);
-
+            // finally apply the ordering parameter
+            this.jcrSsupport.orderNode(request, destResource, changes);
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
-
-        // finally apply the ordering parameter
-        orderNode(request, destItem, changes);
     }
 
     /**
@@ -162,11 +161,12 @@
      * @param destName The name of the target item inside the
      *            <code>destParent</code>. If <code>null</code> the name of
      *            the <code>source</code> is used as the target item name.
-     * @throws RepositoryException May be thrown if an error occurrs executing
+     * @throws RepositoryException May be thrown if an error occurs executing
      *             the operation.
      */
-    protected abstract Item execute(List<Modification> changes, Item source,
-            String destParent, String destName,
+    protected abstract Resource execute(List<Modification> changes, Resource source,
+            String destParent,
+            String destName,
             VersioningConfiguration versioningConfiguration) throws RepositoryException;
 
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
index 6325b9a..9226778 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
@@ -27,15 +27,13 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
-import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
@@ -53,19 +51,21 @@
  * The <code>AbstractPostOperation</code> class is a base implementation of the
  * {@link PostOperation} service interface providing actual implementations with
  * useful tooling and common functionality like preparing the change logs or
- * saving or refreshing the JCR Session.
+ * saving or refreshing.
  */
 public abstract class AbstractPostOperation implements PostOperation {
 
     /**
-     * default log
+     * Default logger
      */
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
+    protected final JCRSupport jcrSsupport = JCRSupport.INSTANCE;
+
     /**
      * Prepares and finalizes the actual operation. Preparation encompasses
      * getting the absolute path of the item to operate on by calling the
-     * {@link #getItemPath(SlingHttpServletRequest)} method and setting the
+     * {@link #getResourcePath(SlingHttpServletRequest)} method and setting the
      * location and parent location on the response. After the operation has
      * been done in the {@link #doRun(SlingHttpServletRequest, PostResponse, List)}
      * method the session is saved if there are unsaved modifications. In case
@@ -80,13 +80,11 @@
     public void run(final SlingHttpServletRequest request,
                     final PostResponse response,
                     final SlingPostProcessor[] processors) {
-        final Session session = request.getResourceResolver().adaptTo(Session.class);
-
         final VersioningConfiguration versionableConfiguration = getVersioningConfiguration(request);
 
         try {
             // calculate the paths
-            String path = getItemPath(request);
+            String path = this.getResourcePath(request);
             response.setPath(path);
 
             // location
@@ -159,10 +157,12 @@
                         response.onChange("checkin", change.getSource());
                         nodesToCheckin.remove(change.getSource());
                         break;
+                    case RESTORE : response.onChange("restore", change.getSource());
+                        break;
                 }
             }
 
-            if (isSessionSaveRequired(session, request)) {
+            if (isResourceResolverCommitRequired(request)) {
                 request.getResourceResolver().commit();
             }
 
@@ -181,16 +181,10 @@
             response.setError(e);
 
         } finally {
-            try {
-                if (isSessionSaveRequired(session, request)) {
-                    request.getResourceResolver().revert();
-                }
-            } catch (RepositoryException e) {
-                log.warn("RepositoryException in finally block: {}",
-                    e.getMessage(), e);
+            if (isResourceResolverCommitRequired(request)) {
+                request.getResourceResolver().revert();
             }
         }
-
     }
 
     /**
@@ -209,19 +203,19 @@
      *            information
      * @param changes A container to add {@link Modification} instances
      *            representing the operations done.
-     * @throws RepositoryException Maybe thrown if any error occurrs while
+     * @throws PersistenceException Maybe thrown if any error occurs while
      *             accessing the repository.
      */
     protected abstract void doRun(SlingHttpServletRequest request,
             PostResponse response,
-            List<Modification> changes) throws RepositoryException;
+            List<Modification> changes) throws PersistenceException;
 
     /**
      * Get the versioning configuration.
      * @param request The http request
      * @return The versioning configuration
      */
-    protected VersioningConfiguration getVersioningConfiguration(SlingHttpServletRequest request) {
+    protected VersioningConfiguration getVersioningConfiguration(final SlingHttpServletRequest request) {
         VersioningConfiguration versionableConfiguration =
             (VersioningConfiguration) request.getAttribute(VersioningConfiguration.class.getName());
         return versionableConfiguration != null ? versionableConfiguration : new VersioningConfiguration();
@@ -239,37 +233,22 @@
     /**
      * Check whether changes should be written back
      * @param request The http request
-     * @return {@code true} If session handling should be skipped
+     * @return {@code true} If committing be skipped
      */
-    protected boolean isSkipSessionHandling(SlingHttpServletRequest request) {
+    private boolean isSkipSessionHandling(SlingHttpServletRequest request) {
         return Boolean.parseBoolean((String) request.getAttribute(SlingPostConstants.ATTR_SKIP_SESSION_HANDLING)) == true;
     }
 
     /**
      * Check whether commit to the resource resolver should be called.
-     * @param session The JCR session
      * @param request The http request
-     * @return {@code true} if a save is required.
-     * @throws RepositoryException
+     * @return {@code true} if a commit is required.
      */
-    protected boolean isSessionSaveRequired(Session session, SlingHttpServletRequest request)
-            throws RepositoryException {
+    private boolean isResourceResolverCommitRequired(SlingHttpServletRequest request) {
         return !isSkipSessionHandling(request) && request.getResourceResolver().hasChanges();
     }
 
     /**
-     * Returns the path of the resource of the request as the item path.
-     * <p>
-     * This method may be overwritten by extension if the operation has
-     * different requirements on path processing.
-     * @param request The http request
-     * @return The item path
-     */
-    protected String getItemPath(SlingHttpServletRequest request) {
-        return request.getResource().getPath();
-    }
-
-    /**
      * Returns an iterator on <code>Resource</code> instances addressed in the
      * {@link SlingPostConstants#RP_APPLY_TO} request parameter. If the request
      * parameter is not set, <code>null</code> is returned. If the parameter
@@ -335,6 +314,18 @@
     }
 
     /**
+     * Returns the path of the resource of the request as the item path.
+     * <p>
+     * This method may be overwritten by extension if the operation has
+     * different requirements on path processing.
+     * @param request The http request
+     * @return The resource path
+     */
+    protected String getResourcePath(SlingHttpServletRequest request) {
+        return request.getResource().getPath();
+    }
+
+    /**
      * Returns true if any of the request parameters starts with
      * {@link SlingPostConstants#ITEM_PREFIX_RELATIVE_CURRENT <code>./</code>}.
      * In this case only parameters starting with either of the prefixes
@@ -378,104 +369,6 @@
             || name.startsWith(SlingPostConstants.ITEM_PREFIX_RELATIVE_PARENT);
     }
 
-    /**
-     * Orders the given node according to the specified command. The following
-     * syntax is supported: &lt;xmp&gt; | first | before all child nodes | before A |
-     * before child node A | after A | after child node A | last | after all
-     * nodes | N | at a specific position, N being an integer &lt;/xmp&gt;
-     *
-     * @param request The http request
-     * @param item node to order
-     * @param changes The list of modifications
-     * @throws RepositoryException if an error occurs
-     */
-    protected void orderNode(SlingHttpServletRequest request, Item item,
-            List<Modification> changes) throws RepositoryException {
-
-        String command = request.getParameter(SlingPostConstants.RP_ORDER);
-        if (command == null || command.length() == 0) {
-            // nothing to do
-            return;
-        }
-
-        if (!item.isNode()) {
-            return;
-        }
-
-        Node parent = item.getParent();
-
-        String next = null;
-        if (command.equals(SlingPostConstants.ORDER_FIRST)) {
-
-            next = parent.getNodes().nextNode().getName();
-
-        } else if (command.equals(SlingPostConstants.ORDER_LAST)) {
-
-            next = "";
-
-        } else if (command.startsWith(SlingPostConstants.ORDER_BEFORE)) {
-
-            next = command.substring(SlingPostConstants.ORDER_BEFORE.length());
-
-        } else if (command.startsWith(SlingPostConstants.ORDER_AFTER)) {
-
-            String name = command.substring(SlingPostConstants.ORDER_AFTER.length());
-            NodeIterator iter = parent.getNodes();
-            while (iter.hasNext()) {
-                Node n = iter.nextNode();
-                if (n.getName().equals(name)) {
-                    if (iter.hasNext()) {
-                        next = iter.nextNode().getName();
-                    } else {
-                        next = "";
-                    }
-                }
-            }
-
-        } else {
-            // check for integer
-            try {
-                // 01234
-                // abcde move a -> 2 (above 3)
-                // bcade move a -> 1 (above 1)
-                // bacde
-                int newPos = Integer.parseInt(command);
-                next = "";
-                NodeIterator iter = parent.getNodes();
-                while (iter.hasNext() && newPos >= 0) {
-                    Node n = iter.nextNode();
-                    if (n.getName().equals(item.getName())) {
-                        // if old node is found before index, need to
-                        // inc index
-                        newPos++;
-                    }
-                    if (newPos == 0) {
-                        next = n.getName();
-                        break;
-                    }
-                    newPos--;
-                }
-            } catch (NumberFormatException e) {
-                throw new IllegalArgumentException(
-                    "provided node ordering command is invalid: " + command);
-            }
-        }
-
-        if (next != null) {
-            if (next.equals("")) {
-                next = null;
-            }
-            parent.orderBefore(item.getName(), next);
-            changes.add(Modification.onOrder(item.getPath(), next));
-            if (log.isDebugEnabled()) {
-                log.debug("Node {} moved '{}'", item.getPath(), command);
-            }
-        } else {
-            throw new IllegalArgumentException(
-                "provided node ordering command is invalid: " + command);
-        }
-    }
-
     protected Node findVersionableAncestor(Node node) throws RepositoryException {
         if (isVersionable(node)) {
             return node;
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java
index f5faf7a..21ac0e5 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java
@@ -24,6 +24,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
@@ -37,34 +38,37 @@
 
     @Override
     protected void doRun(SlingHttpServletRequest request, PostResponse response, List<Modification> changes)
-            throws RepositoryException {
-        Iterator<Resource> res = getApplyToResources(request);
-        if (res == null) {
+            throws PersistenceException {
+        try {
+            Iterator<Resource> res = getApplyToResources(request);
+            if (res == null) {
 
-            Resource resource = request.getResource();
-            Node node = resource.adaptTo(Node.class);
-            if (node == null) {
-                response.setStatus(HttpServletResponse.SC_NOT_FOUND,
-                    "Missing source " + resource + " for checkout");
-                return;
-            }
-
-            node.checkin();
-            changes.add(Modification.onCheckin(resource.getPath()));
-
-        } else {
-
-            while (res.hasNext()) {
-                Resource resource = res.next();
+                Resource resource = request.getResource();
                 Node node = resource.adaptTo(Node.class);
-                if (node != null) {
-                    node.checkin();
-                    changes.add(Modification.onCheckin(resource.getPath()));
+                if (node == null) {
+                    response.setStatus(HttpServletResponse.SC_NOT_FOUND,
+                        "Missing source " + resource + " for checkout");
+                    return;
                 }
+
+                node.checkin();
+                changes.add(Modification.onCheckin(resource.getPath()));
+
+            } else {
+
+                while (res.hasNext()) {
+                    Resource resource = res.next();
+                    Node node = resource.adaptTo(Node.class);
+                    if (node != null) {
+                        node.checkin();
+                        changes.add(Modification.onCheckin(resource.getPath()));
+                    }
+                }
+
             }
-
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
-
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java
index 610eaa7..a835d6a 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java
@@ -24,6 +24,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
@@ -36,34 +37,37 @@
 public class CheckoutOperation extends AbstractPostOperation {
     @Override
     protected void doRun(SlingHttpServletRequest request, PostResponse response, List<Modification> changes)
-            throws RepositoryException {
-        Iterator<Resource> res = getApplyToResources(request);
-        if (res == null) {
+            throws PersistenceException {
+        try {
+            Iterator<Resource> res = getApplyToResources(request);
+            if (res == null) {
 
-            Resource resource = request.getResource();
-            Node node = resource.adaptTo(Node.class);
-            if (node == null) {
-                response.setStatus(HttpServletResponse.SC_NOT_FOUND,
-                    "Missing source " + resource + " for checkout");
-                return;
-            }
-
-            node.checkout();
-            changes.add(Modification.onCheckout(resource.getPath()));
-
-        } else {
-
-            while (res.hasNext()) {
-                Resource resource = res.next();
+                Resource resource = request.getResource();
                 Node node = resource.adaptTo(Node.class);
-                if (node != null) {
-                    node.checkout();
-                    changes.add(Modification.onCheckout(resource.getPath()));
+                if (node == null) {
+                    response.setStatus(HttpServletResponse.SC_NOT_FOUND,
+                        "Missing source " + resource + " for checkout");
+                    return;
                 }
+
+                node.checkout();
+                changes.add(Modification.onCheckout(resource.getPath()));
+
+            } else {
+
+                while (res.hasNext()) {
+                    Resource resource = res.next();
+                    Node node = resource.adaptTo(Node.class);
+                    if (node != null) {
+                        node.checkout();
+                        changes.add(Modification.onCheckout(resource.getPath()));
+                    }
+                }
+
             }
-
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
-
     }
 
     /**
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 0530965..37fef28 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
@@ -26,6 +26,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.NodeType;
 
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.VersioningConfiguration;
 
@@ -42,16 +43,22 @@
     }
 
     @Override
-    protected Item execute(List<Modification> changes, Item source,
+    protected Resource execute(List<Modification> changes,
+            Resource source,
             String destParent, String destName,
             VersioningConfiguration versioningConfiguration) throws RepositoryException {
+        // ensure we have an item underlying the request's resource
+        Item item = source.adaptTo(Item.class);
+        if (item == null) {
+            return null;
+        }
 
-        Item destItem = copy(source, (Node) source.getSession().getItem(destParent), destName);
+        Item destItem = copy(item, (Node) item.getSession().getItem(destParent), destName);
 
         String dest = destParent + "/" + destName;
         changes.add(Modification.onCopied(source.getPath(), dest));
         log.debug("copy {} to {}", source, dest);
-        return destItem;
+        return source.getResourceResolver().getResource(destItem.getPath());
     }
 
     /**
@@ -101,7 +108,7 @@
             throw new RepositoryException(
                     "Cannot copy ancestor " + src.getPath() + " to descendant " + dstParent.getPath());
         }
-        
+
         // ensure destination name
         if (name == null) {
             name = src.getName();
@@ -132,7 +139,7 @@
         }
         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 {
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java
index bde5017..ec5c19d 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java
@@ -52,7 +52,7 @@
     @Override
     protected void doRun(final SlingHttpServletRequest request,
             final PostResponse response, final List<Modification> changes)
-            throws RepositoryException {
+            throws PersistenceException {
 
         // SLING-3203: selectors, extension and suffix make no sense here and
         // might lead to deleting other resources than the one the user means.
@@ -69,17 +69,21 @@
         final VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
         final boolean deleteChunks = isDeleteChunkRequest(request);
         final Iterator<Resource> res = getApplyToResources(request);
-        if (res == null) {
-            final Resource resource = request.getResource();
-            deleteResource(resource, changes, versioningConfiguration,
-                deleteChunks);
-        } else {
-            while (res.hasNext()) {
-                final Resource resource = res.next();
+        try {
+            if (res == null) {
+                final Resource resource = request.getResource();
                 deleteResource(resource, changes, versioningConfiguration,
                     deleteChunks);
-            }
+            } else {
+                while (res.hasNext()) {
+                    final Resource resource = res.next();
+                    deleteResource(resource, changes, versioningConfiguration,
+                        deleteChunks);
+                }
 
+            }
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java
index cce7df1..0d6910b 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java
@@ -65,205 +65,204 @@
 
     @Override
     protected void doRun(SlingHttpServletRequest request, PostResponse response, final List<Modification> changes)
-            throws RepositoryException {
-        ContentImporter importer = contentImporter;
-        if (importer == null) {
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-                    "Missing content importer for import");
-            return;
-        }
-        Map<String, RequestProperty> reqProperties = collectContent(request,
-             response);
-
-        VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
-
-        // do not change order unless you have a very good reason.
-        Session session = request.getResourceResolver().adaptTo(Session.class);
-
+            throws PersistenceException {
         try {
-            processCreate(request.getResourceResolver(), reqProperties, response, changes, versioningConfiguration);
-        } catch ( final PersistenceException pe) {
-            if ( pe.getCause() instanceof RepositoryException ) {
-                throw (RepositoryException)pe.getCause();
-            }
-            throw new RepositoryException(pe);
-        }
-        String path = response.getPath();
-        Node node = null;
-        try {
-            node = (Node) session.getItem(path);
-        } catch ( RepositoryException e ) {
-            log.warn(e.getMessage(),e);
-            // was not able to resolve the node
-        } catch ( ClassCastException e) {
-            log.warn(e.getMessage(),e);
-            // it was not a node
-        }
-        if (node == null) {
-
-            response.setStatus(HttpServletResponse.SC_NOT_FOUND,
-                    "Missing target node " + path + " for import");
-            return;
-        }
-
-        String contentType = getRequestParamAsString(request, SlingPostConstants.RP_CONTENT_TYPE);
-        if (contentType == null) {
-            response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
-            "Required :contentType parameter is missing");
-            return;
-        }
-
-        //import options passed as request parameters.
-        final boolean replace = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_REPLACE));
-        final boolean replaceProperties = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_REPLACE_PROPERTIES));
-        final boolean checkin = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_CHECKIN));
-        final boolean autoCheckout = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_AUTO_CHECKOUT));
-
-        String basePath = getItemPath(request);
-        if (basePath.endsWith("/")) {
-            //remove the trailing slash
-            basePath = basePath.substring(0, basePath.length() - 1);
-        }
-
-        // default to creating content
-        response.setCreateRequest(true);
-
-        final String targetName;
-        //check if a name was posted to use as the name of the imported root node
-        if (getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME) != null) {
-            // exact name
-            targetName = getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME);
-            if (targetName.length() > 0 && node.hasNode(targetName)) {
-                if (replace) {
-                    response.setCreateRequest(false);
-                } else {
-                    response.setStatus(
-                        HttpServletResponse.SC_PRECONDITION_FAILED,
-                        "Cannot import " + path + "/" + targetName
-                            + ": node exists");
-                    return;
-                }
-            }
-        } else if (getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME_HINT) != null) {
-            // node name hint only
-            String nodePath = generateName(request, basePath);
-            String name = nodePath.substring(nodePath.lastIndexOf('/') + 1);
-            targetName = name;
-        } else {
-            // no name posted, so the import won't create a root node
-            targetName = "";
-        }
-        final String contentRootName = targetName + "." + contentType;
-
-        try {
-            InputStream contentStream = null;
-        	RequestParameter contentParameter = request.getRequestParameter(SlingPostConstants.RP_CONTENT);
-            if (contentParameter != null) {
-                contentStream = contentParameter.getInputStream();
-            } else {
-                RequestParameter contentFile = request.getRequestParameter(SlingPostConstants.RP_CONTENT_FILE);
-                if (contentFile != null) {
-                    contentStream = contentFile.getInputStream();
-                }
-            }
-
-            if (contentStream == null) {
-                response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
-                        "Missing content for import");
+            ContentImporter importer = contentImporter;
+            if (importer == null) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                        "Missing content importer for import");
                 return;
-            } else {
-                importer.importContent(node, contentRootName, contentStream,
-                        new ImportOptions() {
+            }
+            Map<String, RequestProperty> reqProperties = collectContent(request,
+                 response);
 
-                            @Override
-                            public boolean isCheckin() {
-                                return checkin;
-                            }
+            VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
 
-							@Override
-							public boolean isAutoCheckout() {
-								return autoCheckout;
-							}
+            // do not change order unless you have a very good reason.
+            Session session = request.getResourceResolver().adaptTo(Session.class);
 
-							@Override
-                            public boolean isIgnoredImportProvider(
-                                    String extension) {
-                                // this probably isn't important in this context.
-                                return false;
-                            }
+            processCreate(request.getResourceResolver(), reqProperties, response, changes, versioningConfiguration);
 
-                            @Override
-                            public boolean isOverwrite() {
-                                return replace;
-                            }
+            String path = response.getPath();
+            Node node = null;
+            try {
+                node = (Node) session.getItem(path);
+            } catch ( RepositoryException e ) {
+                log.warn(e.getMessage(),e);
+                // was not able to resolve the node
+            } catch ( ClassCastException e) {
+                log.warn(e.getMessage(),e);
+                // it was not a node
+            }
+            if (node == null) {
 
-                            /* (non-Javadoc)
-                             * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite()
-                             */
-                            @Override
-                            public boolean isPropertyOverwrite() {
-                                return replaceProperties;
-                            }
-                        },
-                        new ContentImportListener() {
-
-                            @Override
-                            public void onReorder(String orderedPath, String beforeSibbling) {
-                                changes.add(Modification.onOrder(orderedPath, beforeSibbling));
-                            }
-
-                            @Override
-                            public void onMove(String srcPath, String destPath) {
-                                changes.add(Modification.onMoved(srcPath, destPath));
-                            }
-
-                            @Override
-                            public void onModify(String srcPath) {
-                                changes.add(Modification.onModified(srcPath));
-                            }
-
-                            @Override
-                            public void onDelete(String srcPath) {
-                                changes.add(Modification.onDeleted(srcPath));
-                            }
-
-                            @Override
-                            public void onCreate(String srcPath) {
-                                changes.add(Modification.onCreated(srcPath));
-                            }
-
-                            @Override
-                            public void onCopy(String srcPath, String destPath) {
-                                changes.add(Modification.onCopied(srcPath, destPath));
-                            }
-
-                            @Override
-                            public void onCheckin(String srcPath) {
-                                changes.add(Modification.onCheckin(srcPath));
-                            }
-                            @Override
-                            public void onCheckout(String srcPath) {
-                                changes.add(Modification.onCheckout(srcPath));
-                            }
-                        });
+                response.setStatus(HttpServletResponse.SC_NOT_FOUND,
+                        "Missing target node " + path + " for import");
+                return;
             }
 
-            if (!changes.isEmpty()) {
-                //fill in the data for the response report
-                Modification modification = changes.get(0);
-                if (modification.getType() == ModificationType.CREATE) {
-                    String importedPath = modification.getSource();
-                    response.setLocation(externalizePath(request, importedPath));
-                    response.setPath(importedPath);
-                    int lastSlashIndex = importedPath.lastIndexOf('/');
-                    if (lastSlashIndex != -1) {
-                        String parentPath = importedPath.substring(0, lastSlashIndex);
-                        response.setParentLocation(externalizePath(request, parentPath));
+            String contentType = getRequestParamAsString(request, SlingPostConstants.RP_CONTENT_TYPE);
+            if (contentType == null) {
+                response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
+                "Required :contentType parameter is missing");
+                return;
+            }
+
+            //import options passed as request parameters.
+            final boolean replace = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_REPLACE));
+            final boolean replaceProperties = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_REPLACE_PROPERTIES));
+            final boolean checkin = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_CHECKIN));
+            final boolean autoCheckout = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_AUTO_CHECKOUT));
+
+            String basePath = getResourcePath(request);
+            if (basePath.endsWith("/")) {
+                //remove the trailing slash
+                basePath = basePath.substring(0, basePath.length() - 1);
+            }
+
+            // default to creating content
+            response.setCreateRequest(true);
+
+            final String targetName;
+            //check if a name was posted to use as the name of the imported root node
+            if (getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME) != null) {
+                // exact name
+                targetName = getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME);
+                if (targetName.length() > 0 && node.hasNode(targetName)) {
+                    if (replace) {
+                        response.setCreateRequest(false);
+                    } else {
+                        response.setStatus(
+                            HttpServletResponse.SC_PRECONDITION_FAILED,
+                            "Cannot import " + path + "/" + targetName
+                                + ": node exists");
+                        return;
                     }
                 }
+            } else if (getRequestParamAsString(request, SlingPostConstants.RP_NODE_NAME_HINT) != null) {
+                // node name hint only
+                String nodePath = generateName(request, basePath);
+                String name = nodePath.substring(nodePath.lastIndexOf('/') + 1);
+                targetName = name;
+            } else {
+                // no name posted, so the import won't create a root node
+                targetName = "";
             }
-        } catch (IOException e) {
-            throw new RepositoryException(e);
+            final String contentRootName = targetName + "." + contentType;
+            try {
+                InputStream contentStream = null;
+                RequestParameter contentParameter = request.getRequestParameter(SlingPostConstants.RP_CONTENT);
+                if (contentParameter != null) {
+                    contentStream = contentParameter.getInputStream();
+                } else {
+                    RequestParameter contentFile = request.getRequestParameter(SlingPostConstants.RP_CONTENT_FILE);
+                    if (contentFile != null) {
+                        contentStream = contentFile.getInputStream();
+                    }
+                }
+
+                if (contentStream == null) {
+                    response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED,
+                            "Missing content for import");
+                    return;
+                } else {
+                    importer.importContent(node, contentRootName, contentStream,
+                            new ImportOptions() {
+
+                                @Override
+                                public boolean isCheckin() {
+                                    return checkin;
+                                }
+
+                                @Override
+                                public boolean isAutoCheckout() {
+                                    return autoCheckout;
+                                }
+
+                                @Override
+                                public boolean isIgnoredImportProvider(
+                                        String extension) {
+                                    // this probably isn't important in this context.
+                                    return false;
+                                }
+
+                                @Override
+                                public boolean isOverwrite() {
+                                    return replace;
+                                }
+
+                                /* (non-Javadoc)
+                                 * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite()
+                                 */
+                                @Override
+                                public boolean isPropertyOverwrite() {
+                                    return replaceProperties;
+                                }
+                            },
+                            new ContentImportListener() {
+
+                                @Override
+                                public void onReorder(String orderedPath, String beforeSibbling) {
+                                    changes.add(Modification.onOrder(orderedPath, beforeSibbling));
+                                }
+
+                                @Override
+                                public void onMove(String srcPath, String destPath) {
+                                    changes.add(Modification.onMoved(srcPath, destPath));
+                                }
+
+                                @Override
+                                public void onModify(String srcPath) {
+                                    changes.add(Modification.onModified(srcPath));
+                                }
+
+                                @Override
+                                public void onDelete(String srcPath) {
+                                    changes.add(Modification.onDeleted(srcPath));
+                                }
+
+                                @Override
+                                public void onCreate(String srcPath) {
+                                    changes.add(Modification.onCreated(srcPath));
+                                }
+
+                                @Override
+                                public void onCopy(String srcPath, String destPath) {
+                                    changes.add(Modification.onCopied(srcPath, destPath));
+                                }
+
+                                @Override
+                                public void onCheckin(String srcPath) {
+                                    changes.add(Modification.onCheckin(srcPath));
+                                }
+                                @Override
+                                public void onCheckout(String srcPath) {
+                                    changes.add(Modification.onCheckout(srcPath));
+                                }
+                            });
+                }
+
+                if (!changes.isEmpty()) {
+                    //fill in the data for the response report
+                    Modification modification = changes.get(0);
+                    if (modification.getType() == ModificationType.CREATE) {
+                        String importedPath = modification.getSource();
+                        response.setLocation(externalizePath(request, importedPath));
+                        response.setPath(importedPath);
+                        int lastSlashIndex = importedPath.lastIndexOf('/');
+                        if (lastSlashIndex != -1) {
+                            String parentPath = importedPath.substring(0, lastSlashIndex);
+                            response.setParentLocation(externalizePath(request, parentPath));
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                throw new PersistenceException(e.getMessage(), e);
+            }
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
+
+
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupport.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupport.java
new file mode 100644
index 0000000..0c27116
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupport.java
@@ -0,0 +1,57 @@
+/*
+ * 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.sling.servlets.post.impl.operations;
+
+import java.util.List;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.servlets.post.Modification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JCRSupport {
+
+    public static final JCRSupport INSTANCE = new JCRSupport();
+
+    /** Logger. */
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final Object supportImpl;
+
+    public JCRSupport() {
+        Object impl = null;
+        try {
+            impl = new JCRSupportImpl();
+        } catch ( final Throwable t) {
+            logger.warn("Support for JCR operations like checkin, checkout, ordering etc. is currently disabled " +
+                        "in the servlets post module. Check whether the JCR API is available.");
+        }
+        this.supportImpl = impl;
+    }
+
+    public void orderNode(final SlingHttpServletRequest request,
+            final Resource resource,
+            final List<Modification> changes) throws PersistenceException {
+        if ( supportImpl != null ) {
+            ((JCRSupportImpl)supportImpl).orderNode(request, resource, changes);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupportImpl.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupportImpl.java
new file mode 100644
index 0000000..f9382c0
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/JCRSupportImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.sling.servlets.post.impl.operations;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JCRSupportImpl {
+
+    /** Logger. */
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * Orders the given node according to the specified command. The following
+     * syntax is supported: &lt;xmp&gt; | first | before all child nodes | before A |
+     * before child node A | after A | after child node A | last | after all
+     * nodes | N | at a specific position, N being an integer &lt;/xmp&gt;
+     *
+     * @param request The http request
+     * @param item node to order
+     * @param changes The list of modifications
+     * @throws RepositoryException if an error occurs
+     */
+    public void orderNode(final SlingHttpServletRequest request,
+            final Resource resource,
+            final List<Modification> changes) throws PersistenceException {
+
+        final String command = request.getParameter(SlingPostConstants.RP_ORDER);
+        if (command == null || command.length() == 0) {
+            // nothing to do
+            return;
+        }
+
+        final Node node = resource.adaptTo(Node.class);
+        if (node == null) {
+            return;
+        }
+
+        try {
+            final Node parent = node.getParent();
+
+            String next = null;
+            if (command.equals(SlingPostConstants.ORDER_FIRST)) {
+
+                next = parent.getNodes().nextNode().getName();
+
+            } else if (command.equals(SlingPostConstants.ORDER_LAST)) {
+
+                next = "";
+
+            } else if (command.startsWith(SlingPostConstants.ORDER_BEFORE)) {
+
+                next = command.substring(SlingPostConstants.ORDER_BEFORE.length());
+
+            } else if (command.startsWith(SlingPostConstants.ORDER_AFTER)) {
+
+                String name = command.substring(SlingPostConstants.ORDER_AFTER.length());
+                NodeIterator iter = parent.getNodes();
+                while (iter.hasNext()) {
+                    Node n = iter.nextNode();
+                    if (n.getName().equals(name)) {
+                        if (iter.hasNext()) {
+                            next = iter.nextNode().getName();
+                        } else {
+                            next = "";
+                        }
+                    }
+                }
+
+            } else {
+                // check for integer
+                try {
+                    // 01234
+                    // abcde move a -> 2 (above 3)
+                    // bcade move a -> 1 (above 1)
+                    // bacde
+                    int newPos = Integer.parseInt(command);
+                    next = "";
+                    NodeIterator iter = parent.getNodes();
+                    while (iter.hasNext() && newPos >= 0) {
+                        Node n = iter.nextNode();
+                        if (n.getName().equals(node.getName())) {
+                            // if old node is found before index, need to
+                            // inc index
+                            newPos++;
+                        }
+                        if (newPos == 0) {
+                            next = n.getName();
+                            break;
+                        }
+                        newPos--;
+                    }
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException(
+                        "provided node ordering command is invalid: " + command);
+                }
+            }
+
+            if (next != null) {
+                if (next.equals("")) {
+                    next = null;
+                }
+                parent.orderBefore(node.getName(), next);
+                changes.add(Modification.onOrder(node.getPath(), next));
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Node {} moved '{}'", node.getPath(), command);
+                }
+            } else {
+                throw new IllegalArgumentException(
+                    "provided node ordering command is invalid: " + command);
+            }
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException("Unable to order resource", re, resource.getPath(), 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 19fbdab..72b14ce 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
@@ -78,7 +78,7 @@
     protected void doRun(final SlingHttpServletRequest request,
                     final PostResponse response,
                     final List<Modification> changes)
-    throws RepositoryException {
+    throws PersistenceException {
 
         try {
             final Map<String, RequestProperty> reqProperties = collectContent(request, response);
@@ -102,20 +102,14 @@
 
             // order content
             final Resource newResource = request.getResourceResolver().getResource(response.getPath());
-            final Node newNode = newResource.adaptTo(Node.class);
-            if ( newNode != null ) {
-                orderNode(request, newNode, changes);
-            }
-        } catch ( final PersistenceException pe) {
-            if ( pe.getCause() instanceof RepositoryException ) {
-                throw (RepositoryException)pe.getCause();
-            }
-            throw new RepositoryException(pe);
+            this.jcrSsupport.orderNode(request, newResource, changes);
+        } catch ( final RepositoryException pe) {
+            throw new PersistenceException(pe.getMessage(), pe);
         }
     }
 
     @Override
-    protected String getItemPath(SlingHttpServletRequest request) {
+    protected String getResourcePath(SlingHttpServletRequest request) {
 
         // calculate the paths
         StringBuilder rootPathBuf = new StringBuilder();
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 1a680ef..baacf55 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
@@ -22,6 +22,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.VersioningConfiguration;
 
@@ -38,9 +39,15 @@
     }
 
     @Override
-    protected Item execute(List<Modification> changes, Item source,
+    protected Resource execute(List<Modification> changes, Resource source,
             String destParent, String destName,
-            VersioningConfiguration versioningConfiguration) throws RepositoryException {
+            VersioningConfiguration versioningConfiguration)
+    throws RepositoryException {
+        // 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();
@@ -51,17 +58,17 @@
             destParent = "";
         }
         String destPath = destParent + "/" + destName;
-        Session session = source.getSession();
-        
-        checkoutIfNecessary(source.getParent(), changes, versioningConfiguration);
+        Session session = item.getSession();
+
+        checkoutIfNecessary(item.getParent(), changes, versioningConfiguration);
 
         if (session.itemExists(destPath)) {
             session.getItem(destPath).remove();
         }
-        
+
         session.move(sourcePath, destPath);
         changes.add(Modification.onMoved(sourcePath, destPath));
-        return session.getItem(destPath);
+        return source.getResourceResolver().getResource(destPath);
     }
 
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java
index 5838525..e5f5d0d 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java
@@ -27,6 +27,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
@@ -41,34 +42,38 @@
 
     @Override
     protected void doRun(SlingHttpServletRequest request, PostResponse response, List<Modification> changes)
-            throws RepositoryException {
-        final String version = request.getParameter(SlingPostConstants.RP_VERSION);
-        if (version == null || version.length() == 0) {
-            throw new IllegalArgumentException("Unable to process restore. Missing version");
-        }
-        final String removeString = request.getParameter(SlingPostConstants.RP_REMOVE_EXISTING);
-        final boolean removeExisting = Boolean.parseBoolean(removeString);
-
-        Iterator<Resource> res = getApplyToResources(request);
-        if (res == null) {
-            Resource resource = request.getResource();
-            Node node = resource.adaptTo(Node.class);
-            if (node == null) {
-                response.setStatus(HttpServletResponse.SC_NOT_FOUND,
-                    "Missing source " + resource + " for restore");
-                return;
+            throws PersistenceException {
+        try {
+            final String version = request.getParameter(SlingPostConstants.RP_VERSION);
+            if (version == null || version.length() == 0) {
+                throw new IllegalArgumentException("Unable to process restore. Missing version");
             }
-            restore(node, version, removeExisting);
-            changes.add(Modification.onRestore(resource.getPath(), version));
-        } else {
-            while (res.hasNext()) {
-                Resource resource = res.next();
+            final String removeString = request.getParameter(SlingPostConstants.RP_REMOVE_EXISTING);
+            final boolean removeExisting = Boolean.parseBoolean(removeString);
+
+            Iterator<Resource> res = getApplyToResources(request);
+            if (res == null) {
+                Resource resource = request.getResource();
                 Node node = resource.adaptTo(Node.class);
-                if (node != null) {
-                    restore(node, version, removeExisting);
-                    changes.add(Modification.onRestore(resource.getPath(), version));
+                if (node == null) {
+                    response.setStatus(HttpServletResponse.SC_NOT_FOUND,
+                        "Missing source " + resource + " for restore");
+                    return;
+                }
+                restore(node, version, removeExisting);
+                changes.add(Modification.onRestore(resource.getPath(), version));
+            } else {
+                while (res.hasNext()) {
+                    Resource resource = res.next();
+                    Node node = resource.adaptTo(Node.class);
+                    if (node != null) {
+                        restore(node, version, removeExisting);
+                        changes.add(Modification.onRestore(resource.getPath(), version));
+                    }
                 }
             }
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java
index 956cfd0..d875329 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java
@@ -24,7 +24,6 @@
 import java.util.List;
 import java.util.Map;
 
-import javax.jcr.RepositoryException; // required due to AbstractPostOperation signature.
 import javax.servlet.ServletContext;
 import javax.servlet.http.Part;
 
@@ -76,36 +75,30 @@
     }
 
     @Override
-    protected void doRun(SlingHttpServletRequest request, PostResponse response, List<Modification> changes) throws RepositoryException {
-        try {
-            Iterator<Part> partsIterator = (Iterator<Part>) request.getAttribute("request-parts-iterator");
-            Map<String, List<String>> formFields = new HashMap<>();
-            boolean streamingBodies = false;
-            while (partsIterator.hasNext()) {
-                Part part = partsIterator.next();
-                String name = part.getName();
+    protected void doRun(SlingHttpServletRequest request, PostResponse response, List<Modification> changes)
+    throws PersistenceException {
+        @SuppressWarnings("unchecked")
+        Iterator<Part> partsIterator = (Iterator<Part>) request.getAttribute("request-parts-iterator");
+        Map<String, List<String>> formFields = new HashMap<>();
+        boolean streamingBodies = false;
+        while (partsIterator.hasNext()) {
+            Part part = partsIterator.next();
+            String name = part.getName();
 
-                if (isFormField(part)) {
-                    addField(formFields, name, part);
-                    if (streamingBodies) {
-                        LOG.warn("Form field {} was sent after the bodies started to be streamed. " +
-                                "Will not have been available to all streamed bodies. " +
-                                "It is recommended to send all form fields before streamed bodies in the POST ", name);
-                    }
-                } else {
-                    streamingBodies = true;
-                    // process the file body and commit.
-                    writeContent(request.getResourceResolver(), part, formFields, response, changes);
-
+            if (isFormField(part)) {
+                addField(formFields, name, part);
+                if (streamingBodies) {
+                    LOG.warn("Form field {} was sent after the bodies started to be streamed. " +
+                            "Will not have been available to all streamed bodies. " +
+                            "It is recommended to send all form fields before streamed bodies in the POST ", name);
                 }
-            }
-        } catch ( final PersistenceException pe) {
-            if ( pe.getCause() instanceof RepositoryException ) {
-                throw (RepositoryException)pe.getCause();
-            }
-            throw new RepositoryException(pe);
-        }
+            } else {
+                streamingBodies = true;
+                // process the file body and commit.
+                writeContent(request.getResourceResolver(), part, formFields, response, changes);
 
+            }
+        }
     }
 
     /**
diff --git a/src/test/java/org/apache/sling/servlets/post/impl/operations/StreamingUploadOperationTest.java b/src/test/java/org/apache/sling/servlets/post/impl/operations/StreamingUploadOperationTest.java
index 8dd2a62..064bdf2 100644
--- a/src/test/java/org/apache/sling/servlets/post/impl/operations/StreamingUploadOperationTest.java
+++ b/src/test/java/org/apache/sling/servlets/post/impl/operations/StreamingUploadOperationTest.java
@@ -19,6 +19,22 @@
 
 package org.apache.sling.servlets.post.impl.operations;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.ModifiableValueMap;
@@ -39,21 +55,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.Part;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 
 public class StreamingUploadOperationTest {
 
@@ -72,8 +73,8 @@
     }
 
     @Test
-    public void test() throws RepositoryException, UnsupportedEncodingException {
-        List<Modification> changes = new ArrayList<Modification>();
+    public void test() throws PersistenceException, RepositoryException, UnsupportedEncodingException {
+        List<Modification> changes = new ArrayList<>();
         PostResponse response = new AbstractPostResponse() {
             @Override
             protected void doSend(HttpServletResponse response) throws IOException {
@@ -91,14 +92,14 @@
             }
         };
 
-        List<Part> partsList = new ArrayList<Part>();
+        List<Part> partsList = new ArrayList<>();
         partsList.add(new MockPart("formfield1", null, null, 0, new ByteArrayInputStream("testformfield1".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("formfield2", null, null, 0, new ByteArrayInputStream("testformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("test".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP));
         final Iterator<Part> partsIterator = partsList.iterator();
-        final Map<String, Resource> repository = new HashMap<String, Resource>();
+        final Map<String, Resource> repository = new HashMap<>();
         final ResourceResolver resourceResolver = new MockResourceResolver() {
             @Override
             public Resource getResource(String path) {
@@ -244,8 +245,8 @@
     }
 
     @Test
-    public void testParts() throws RepositoryException, UnsupportedEncodingException {
-        List<Modification> changes = new ArrayList<Modification>();
+    public void testParts() throws PersistenceException, RepositoryException, UnsupportedEncodingException {
+        List<Modification> changes = new ArrayList<>();
         PostResponse response = new AbstractPostResponse() {
             @Override
             protected void doSend(HttpServletResponse response) throws IOException {
@@ -263,7 +264,7 @@
             }
         };
 
-        List<Part> partsList = new ArrayList<Part>();
+        List<Part> partsList = new ArrayList<>();
         partsList.add(new MockPart("test1.txt@Length", null, null, 0, new ByteArrayInputStream("8".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("test1.txt@Offset", null, null, 0, new ByteArrayInputStream("0".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart(
@@ -284,7 +285,7 @@
         partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP));
         final Iterator<Part> partsIterator = partsList.iterator();
-        final Map<String, Resource> repository = new HashMap<String, Resource>();
+        final Map<String, Resource> repository = new HashMap<>();
         final ResourceResolver resourceResolver = new MockResourceResolver() {
             @Override
             public Resource getResource(String path) {
@@ -309,7 +310,7 @@
             @Override
             public Iterable<Resource> getChildren(Resource resource) {
 
-                List<Resource> children = new ArrayList<Resource>();
+                List<Resource> children = new ArrayList<>();
                 for(Map.Entry<String, Resource> e : repository.entrySet()) {
                     if (isChild(resource.getPath(), e.getKey())) {
                         children.add(e.getValue());
@@ -457,8 +458,8 @@
     }
 
     @Test
-    public void testPartsContentRange() throws RepositoryException, UnsupportedEncodingException {
-        List<Modification> changes = new ArrayList<Modification>();
+    public void testPartsContentRange() throws PersistenceException, RepositoryException, UnsupportedEncodingException {
+        List<Modification> changes = new ArrayList<>();
         PostResponse response = new AbstractPostResponse() {
             @Override
             protected void doSend(HttpServletResponse response) throws IOException {
@@ -476,7 +477,7 @@
             }
         };
 
-        List<Part> partsList = new ArrayList<Part>();
+        List<Part> partsList = new ArrayList<>();
         partsList.add(new MockPart("formfield1", null, null, 0, new ByteArrayInputStream("testformfield1".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("formfield2", null, null, 0, new ByteArrayInputStream("testformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart(
@@ -496,7 +497,7 @@
         partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP));
         partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP));
         final Iterator<Part> partsIterator = partsList.iterator();
-        final Map<String, Resource> repository = new HashMap<String, Resource>();
+        final Map<String, Resource> repository = new HashMap<>();
         final ResourceResolver resourceResolver = new MockResourceResolver() {
             @Override
             public Resource getResource(String path) {
@@ -521,7 +522,7 @@
             @Override
             public Iterable<Resource> getChildren(Resource resource) {
 
-                List<Resource> children = new ArrayList<Resource>();
+                List<Resource> children = new ArrayList<>();
                 for(Map.Entry<String, Resource> e : repository.entrySet()) {
                     if (isChild(resource.getPath(), e.getKey())) {
                         children.add(e.getValue());
@@ -671,7 +672,7 @@
 
 
     private Map<String,Object> mapOf(String ... s) {
-        Map<String, Object> m = new HashMap<String, Object>();
+        Map<String, Object> m = new HashMap<>();
         for (int i = 0; i < s.length; i+=2) {
             m.put(s[i],s[i+1]);
         }