You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2017/03/29 10:09:32 UTC

svn commit: r1789321 - /sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/

Author: cziegeler
Date: Wed Mar 29 10:09:31 2017
New Revision: 1789321

URL: http://svn.apache.org/viewvc?rev=1789321&view=rev
Log:
SLING-6743 : Drop workspace support

Added:
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java   (with props)
Modified:
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java
    sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCopyMoveOperation.java Wed Mar 29 10:09:31 2017
@@ -28,7 +28,6 @@ import javax.servlet.http.HttpServletRes
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 import org.apache.sling.servlets.post.SlingPostConstants;
@@ -37,7 +36,7 @@ import org.apache.sling.servlets.post.Ve
 /**
  * The <code>AbstractCopyMoveOperation</code> is the abstract base close for
  * the {@link CopyOperation} and {@link MoveOperation} classes implementing
- * commong behaviour.
+ * common behaviour.
  */
 abstract class AbstractCopyMoveOperation extends AbstractPostOperation {
 
@@ -68,7 +67,6 @@ abstract class AbstractCopyMoveOperation
             dest = ResourceUtil.getParent(source) + "/" + dest;
         }
         dest = ResourceUtil.normalize(dest);
-        dest = removeAndValidateWorkspace(dest, session);
 
         // destination parent and name
         String dstParent = trailingSlash ? dest : ResourceUtil.getParent(dest);

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java Wed Mar 29 10:09:31 2017
@@ -29,8 +29,8 @@ import java.util.regex.Pattern;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.jcr.nodetype.NodeType;
+import javax.servlet.ServletException;
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.request.RequestParameter;
@@ -40,7 +40,6 @@ import org.apache.sling.api.resource.Per
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.NodeNameGenerator;
 import org.apache.sling.servlets.post.PostResponse;
@@ -161,7 +160,7 @@ abstract class AbstractCreateOperation e
             if ( mvm != null ) {
                 final Node node = rsrc.adaptTo(Node.class);
 
-                final Set<String> newMixins = new HashSet<String>();
+                final Set<String> newMixins = new HashSet<>();
                 newMixins.addAll(Arrays.asList(mixins));
 
                 // clear existing mixins first
@@ -206,7 +205,7 @@ abstract class AbstractCreateOperation e
         final boolean requireItemPrefix = requireItemPathPrefix(request);
 
         // walk the request parameters and collect the properties
-        final LinkedHashMap<String, RequestProperty> reqProperties = new LinkedHashMap<String, RequestProperty>();
+        final LinkedHashMap<String, RequestProperty> reqProperties = new LinkedHashMap<>();
         for (final Map.Entry<String, RequestParameter[]> e : request.getRequestParameterMap().entrySet()) {
             final String paramName = e.getKey();
 
@@ -570,7 +569,7 @@ abstract class AbstractCreateOperation e
                 }
 
                 try {
-                    final Map<String, Object> props = new HashMap<String, Object>();
+                    final Map<String, Object> props = new HashMap<>();
                     if (nodeType != null) {
                         props.put("jcr:primaryType", nodeType);
                     }
@@ -636,9 +635,7 @@ abstract class AbstractCreateOperation e
 		        basePath = basePath += "/" + specialParam.getString();
 
 		        // if the resulting path already exists then report an error
-		        Session session = request.getResourceResolver().adaptTo(Session.class);
-	            String jcrPath = removeAndValidateWorkspace(basePath, session);
-	            if (request.getResourceResolver().getResource(jcrPath) != null) {
+	            if (request.getResourceResolver().getResource(basePath) != null) {
 	    		    throw new RepositoryException(
 	    			        "Collision in node names for path=" + basePath);
 	            }
@@ -676,31 +673,26 @@ abstract class AbstractCreateOperation e
     private String ensureUniquePath(SlingHttpServletRequest request, String basePath) throws RepositoryException {
 		// if resulting path exists, add a suffix until it's not the case
 		// anymore
-		final Session session = request.getResourceResolver().adaptTo(Session.class);
         final ResourceResolver resolver = request.getResourceResolver();
 
-        // basePath might contain a workspace prefix, need to remove it
-        // to test for existence
-        String jcrPath = removeAndValidateWorkspace(basePath, session);
-
 		// if resulting path exists, add a random suffix until it's not the case
 		// anymore
 		final int MAX_TRIES = 1000;
-		if (resolver.getResource(jcrPath) != null ) {
+		if (resolver.getResource(basePath) != null ) {
 		    for(int i=0; i < MAX_TRIES; i++) {
 		        final int uniqueIndex = Math.abs(randomCollisionIndex.nextInt());
-		        String newPath = jcrPath + "_" + uniqueIndex;
+		        String newPath = basePath + "_" + uniqueIndex;
 		        if (resolver.getResource(newPath) == null) {
 		            basePath = basePath + "_" + uniqueIndex;
-		            jcrPath = newPath;
+		            basePath = newPath;
 		            break;
 		        }
 		    }
 
 	        // Give up after MAX_TRIES
-	        if (resolver.getResource(jcrPath) != null ) {
+	        if (resolver.getResource(basePath) != null ) {
 	            throw new RepositoryException(
-	                "Collision in generated node names under " + basePath + ", generated path " + jcrPath + " already exists");
+	                "Collision in generated node names under " + basePath + ", generated path " + basePath + " already exists");
 	        }
 		}
 

Added: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java?rev=1789321&view=auto
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java (added)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java Wed Mar 29 10:09:31 2017
@@ -0,0 +1,612 @@
+/*
+ * 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.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+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.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.wrappers.SlingRequestPaths;
+import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.PostOperation;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.apache.sling.servlets.post.SlingPostProcessor;
+import org.apache.sling.servlets.post.VersioningConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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.
+ */
+public abstract class AbstractPostOperation implements PostOperation {
+
+    /**
+     * default log
+     */
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * 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
+     * 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
+     * of errors, the unsaved changes in the session are rolled back.
+     *
+     * @param request the request to operate on
+     * @param response The <code>PostResponse</code> to record execution
+     *            progress.
+     * @param processors The array of processors
+     */
+    @Override
+    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);
+            response.setPath(path);
+
+            // location
+            response.setLocation(externalizePath(request, path));
+
+            // parent location
+            path = ResourceUtil.getParent(path);
+            if (path != null) {
+                response.setParentLocation(externalizePath(request, path));
+            }
+
+            final List<Modification> changes = new ArrayList<>();
+
+            doRun(request, response, changes);
+
+            // invoke processors
+            if (processors != null) {
+                for (SlingPostProcessor processor : processors) {
+                    processor.process(request, changes);
+                }
+            }
+
+            // check modifications for remaining postfix and store the base path
+            final Map<String, String> modificationSourcesContainingPostfix = new HashMap<>();
+            final Set<String> allModificationSources = new HashSet<>(changes.size());
+            for (final Modification modification : changes) {
+                final String source = modification.getSource();
+                if (source != null) {
+                    allModificationSources.add(source);
+                    final int atIndex = source.indexOf('@');
+                    if (atIndex > 0) {
+                        modificationSourcesContainingPostfix.put(source.substring(0, atIndex), source);
+                    }
+                }
+            }
+
+            // fail if any of the base paths (before the postfix) which had a postfix are contained in the modification set
+            if (modificationSourcesContainingPostfix.size() > 0) {
+                for (final Map.Entry<String, String> sourceToCheck : modificationSourcesContainingPostfix.entrySet()) {
+                    if (allModificationSources.contains(sourceToCheck.getKey())) {
+                        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                "Postfix-containing path " + sourceToCheck.getValue() +
+                                " contained in the modification list. Check configuration.");
+                        return;
+                    }
+                }
+            }
+
+            final Set<String> nodesToCheckin = new LinkedHashSet<>();
+
+            // set changes on html response
+            for(Modification change : changes) {
+                switch ( change.getType() ) {
+                    case MODIFY : response.onModified(change.getSource()); break;
+                    case DELETE : response.onDeleted(change.getSource()); break;
+                    case MOVE   : response.onMoved(change.getSource(), change.getDestination()); break;
+                    case COPY   : response.onCopied(change.getSource(), change.getDestination()); break;
+                    case CREATE :
+                        response.onCreated(change.getSource());
+                        if (versionableConfiguration.isCheckinOnNewVersionableNode()) {
+                            nodesToCheckin.add(change.getSource());
+                        }
+                        break;
+                    case ORDER  : response.onChange("ordered", change.getSource(), change.getDestination()); break;
+                    case CHECKOUT :
+                        response.onChange("checkout", change.getSource());
+                        nodesToCheckin.add(change.getSource());
+                        break;
+                    case CHECKIN :
+                        response.onChange("checkin", change.getSource());
+                        nodesToCheckin.remove(change.getSource());
+                        break;
+                }
+            }
+
+            if (isSessionSaveRequired(session, request)) {
+                request.getResourceResolver().commit();
+            }
+
+            if (!isSkipCheckin(request)) {
+                // now do the checkins
+                for(String checkinPath : nodesToCheckin) {
+                    if (checkin(request.getResourceResolver(), checkinPath)) {
+                        response.onChange("checkin", checkinPath);
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+
+            log.error("Exception during response processing.", e);
+            response.setError(e);
+
+        } finally {
+            try {
+                if (isSessionSaveRequired(session, request)) {
+                    request.getResourceResolver().revert();
+                }
+            } catch (RepositoryException e) {
+                log.warn("RepositoryException in finally block: {}",
+                    e.getMessage(), e);
+            }
+        }
+
+    }
+
+    /**
+     * Actually performs the desired operation filling progress into the
+     * <code>changes</code> list and preparing and further information in the
+     * <code>response</code>.
+     * <p>
+     * The <code>response</code> comes prepared with the path, location and
+     * parent location set. Other properties are expected to be set by this
+     * implementation.
+     *
+     * @param request The <code>SlingHttpServletRequest</code> providing the
+     *            input, mostly in terms of request parameters, to the
+     *            operation.
+     * @param response The {@link PostResponse} to fill with response
+     *            information
+     * @param changes A container to add {@link Modification} instances
+     *            representing the operations done.
+     * @throws RepositoryException Maybe thrown if any error occurrs while
+     *             accessing the repository.
+     */
+    protected abstract void doRun(SlingHttpServletRequest request,
+            PostResponse response,
+            List<Modification> changes) throws RepositoryException;
+
+    /**
+     * Get the versioning configuration.
+     * @param request The http request
+     * @return The versioning configuration
+     */
+    protected VersioningConfiguration getVersioningConfiguration(SlingHttpServletRequest request) {
+        VersioningConfiguration versionableConfiguration =
+            (VersioningConfiguration) request.getAttribute(VersioningConfiguration.class.getName());
+        return versionableConfiguration != null ? versionableConfiguration : new VersioningConfiguration();
+    }
+
+    /**
+     * Check if checkin should be skipped
+     * @param request The http request
+     * @return {@code true} if checkin should be skipped
+     */
+    protected boolean isSkipCheckin(SlingHttpServletRequest request) {
+        return !getVersioningConfiguration(request).isAutoCheckin();
+    }
+
+    /**
+     * Check whether changes should be written back
+     * @param request The http request
+     * @return {@code true} If session handling should be skipped
+     */
+    protected 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
+     */
+    protected boolean isSessionSaveRequired(Session session, SlingHttpServletRequest request)
+            throws RepositoryException {
+        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
+     * is set with valid resources an empty iterator is returned. Any resources
+     * addressed in the {@link SlingPostConstants#RP_APPLY_TO} parameter is
+     * ignored.
+     *
+     * @param request The <code>SlingHttpServletRequest</code> object used to
+     *            get the {@link SlingPostConstants#RP_APPLY_TO} parameter.
+     * @return The iterator of resources listed in the parameter or
+     *         <code>null</code> if the parameter is not set in the request.
+     */
+    protected Iterator<Resource> getApplyToResources(
+            SlingHttpServletRequest request) {
+
+        final String[] applyTo = request.getParameterValues(SlingPostConstants.RP_APPLY_TO);
+        if (applyTo == null) {
+            return null;
+        }
+
+        return new ApplyToIterator(request, applyTo);
+    }
+
+    /**
+     * Returns an external form of the given path prepending the context path
+     * and appending a display extension.
+     *
+     * @param request The http request
+     * @param path the path to externalize
+     * @return the url
+     */
+    protected final String externalizePath(SlingHttpServletRequest request,
+            String path) {
+        StringBuilder ret = new StringBuilder();
+        ret.append(SlingRequestPaths.getContextPath(request));
+        ret.append(request.getResourceResolver().map(path));
+
+        // append optional extension
+        String ext = request.getParameter(SlingPostConstants.RP_DISPLAY_EXTENSION);
+        if (ext != null && ext.length() > 0) {
+            if (ext.charAt(0) != '.') {
+                ret.append('.');
+            }
+            ret.append(ext);
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Resolves the given path with respect to the current root path.
+     *
+     * @param absPath The absolute base path
+     * @param relPath the path to resolve
+     * @return the given path if it starts with a '/'; a resolved path
+     *         otherwise.
+     */
+    protected final String resolvePath(String absPath, String relPath) {
+        if (relPath.startsWith("/")) {
+            return relPath;
+        }
+        return absPath + "/" + relPath;
+    }
+
+    /**
+     * 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
+     * {@link SlingPostConstants#ITEM_PREFIX_RELATIVE_CURRENT <code>./</code>},
+     * {@link SlingPostConstants#ITEM_PREFIX_RELATIVE_PARENT <code>../</code>}
+     * and {@link SlingPostConstants#ITEM_PREFIX_ABSOLUTE <code>/</code>} are
+     * considered as providing content to be stored. Otherwise all parameters
+     * not starting with the command prefix <code>:</code> are considered as
+     * parameters to be stored.
+     *
+     * @param request The http request
+     * @return If a prefix is required.
+     */
+    protected final boolean requireItemPathPrefix(
+            SlingHttpServletRequest request) {
+
+        boolean requirePrefix = false;
+
+        Enumeration<?> names = request.getParameterNames();
+        while (names.hasMoreElements() && !requirePrefix) {
+            String name = (String) names.nextElement();
+            requirePrefix = name.startsWith(SlingPostConstants.ITEM_PREFIX_RELATIVE_CURRENT);
+        }
+
+        return requirePrefix;
+    }
+
+    /**
+     * Returns <code>true</code> if the <code>name</code> starts with either
+     * of the prefixes
+     * {@link SlingPostConstants#ITEM_PREFIX_RELATIVE_CURRENT <code>./</code>},
+     * {@link SlingPostConstants#ITEM_PREFIX_RELATIVE_PARENT <code>../</code>}
+     * and {@link SlingPostConstants#ITEM_PREFIX_ABSOLUTE <code>/</code>}.
+     *
+     * @param name The name
+     * @return {@code true} if the name has a prefix
+     */
+    protected boolean hasItemPathPrefix(String name) {
+        return name.startsWith(SlingPostConstants.ITEM_PREFIX_ABSOLUTE)
+            || name.startsWith(SlingPostConstants.ITEM_PREFIX_RELATIVE_CURRENT)
+            || 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;
+        }
+        try {
+            node = node.getParent();
+            return findVersionableAncestor(node);
+        } catch (ItemNotFoundException e) {
+            // top-level
+            return null;
+        }
+    }
+
+    protected boolean isVersionable(Node node) throws RepositoryException {
+        return node.isNodeType("mix:versionable");
+    }
+
+    protected void checkoutIfNecessary(Node node, List<Modification> changes,
+            VersioningConfiguration versioningConfiguration) throws RepositoryException {
+        if (versioningConfiguration.isAutoCheckout()) {
+            Node versionableNode = findVersionableAncestor(node);
+            if (versionableNode != null) {
+                if (!versionableNode.isCheckedOut()) {
+                    versionableNode.checkout();
+                    changes.add(Modification.onCheckout(versionableNode.getPath()));
+                }
+            }
+        }
+    }
+
+    private boolean checkin(final ResourceResolver resolver, final String path) throws RepositoryException {
+        final Resource rsrc = resolver.getResource(path);
+        final Node node = (rsrc == null ? null : rsrc.adaptTo(Node.class));
+        if (node != null) {
+            if (node.isCheckedOut() && isVersionable(node)) {
+                node.checkin();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static class ApplyToIterator implements Iterator<Resource> {
+
+        private final ResourceResolver resolver;
+        private final Resource baseResource;
+        private final String[] paths;
+
+        private int pathIndex;
+
+        private Resource nextResource;
+
+        private Iterator<Resource> resourceIterator = null;
+
+        ApplyToIterator(SlingHttpServletRequest request, String[] paths) {
+            this.resolver = request.getResourceResolver();
+            this.baseResource = request.getResource();
+            this.paths = paths;
+            this.pathIndex = 0;
+
+            nextResource = seek();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return nextResource != null;
+        }
+
+        @Override
+        public Resource next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            Resource result = nextResource;
+            nextResource = seek();
+
+            return result;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Resource seek() {
+        	if (resourceIterator != null) {
+        		if (resourceIterator.hasNext()) {
+            		//return the next resource in the iterator
+        			Resource res = resourceIterator.next();
+        			return res;
+                }
+    			resourceIterator = null;
+        	}
+            while (pathIndex < paths.length) {
+                String path = paths[pathIndex];
+                pathIndex++;
+
+                //SLING-2415 - support wildcard as the last segment of the applyTo path
+                if (path.endsWith("*")) {
+                	if (path.length() == 1) {
+                		resourceIterator = baseResource.listChildren();
+                	} else if (path.endsWith("/*")) {
+                    	path = path.substring(0, path.length() - 2);
+                    	if (path.length() == 0) {
+                    		resourceIterator = baseResource.listChildren();
+                    	} else {
+                        	Resource res = resolver.getResource(baseResource, path);
+                            if (res != null) {
+                            	resourceIterator = res.listChildren();
+                            }
+                    	}
+                    }
+                	if (resourceIterator != null) {
+                		//return the first resource in the iterator
+                		if (resourceIterator.hasNext()) {
+                			Resource res = resourceIterator.next();
+                			return res;
+                		}
+                        resourceIterator = null;
+                	}
+                } else {
+                    Resource res = resolver.getResource(baseResource, path);
+                    if (res != null) {
+                        return res;
+                    }
+                }
+            }
+
+            // no more elements in the array
+            return null;
+        }
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractPostOperation.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckinOperation.java Wed Mar 29 10:09:31 2017
@@ -25,7 +25,6 @@ import javax.servlet.http.HttpServletRes
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CheckoutOperation.java Wed Mar 29 10:09:31 2017
@@ -25,7 +25,6 @@ import javax.servlet.http.HttpServletRes
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/DeleteOperation.java Wed Mar 29 10:09:31 2017
@@ -27,7 +27,6 @@ import org.apache.sling.api.SlingHttpSer
 import org.apache.sling.api.request.RequestPathInfo;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 import org.apache.sling.servlets.post.SlingPostConstants;

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java Wed Mar 29 10:09:31 2017
@@ -120,7 +120,6 @@ public class ImportOperation extends Abs
         final boolean autoCheckout = "true".equalsIgnoreCase(getRequestParamAsString(request, SlingPostConstants.RP_AUTO_CHECKOUT));
 
         String basePath = getItemPath(request);
-        basePath = removeAndValidateWorkspace(basePath, request.getResourceResolver().adaptTo(Session.class));
         if (basePath.endsWith("/")) {
             //remove the trailing slash
             basePath = basePath.substring(0, basePath.length() - 1);
@@ -208,33 +207,41 @@ public class ImportOperation extends Abs
                         },
                         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));
                             }

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/RestoreOperation.java Wed Mar 29 10:09:31 2017
@@ -28,7 +28,6 @@ import javax.servlet.http.HttpServletRes
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 import org.apache.sling.servlets.post.SlingPostConstants;

Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java?rev=1789321&r1=1789320&r2=1789321&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java (original)
+++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/StreamedUploadOperation.java Wed Mar 29 10:09:31 2017
@@ -17,6 +17,17 @@
 
 package org.apache.sling.servlets.post.impl.operations;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+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;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.util.Text;
 import org.apache.sling.api.SlingHttpServletRequest;
@@ -24,23 +35,12 @@ import org.apache.sling.api.resource.Per
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.servlets.post.AbstractPostOperation;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.PostResponse;
 import org.apache.sling.servlets.post.impl.helper.StreamedChunk;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException; // required due to AbstractPostOperation signature.
-import javax.servlet.ServletContext;
-import javax.servlet.http.Part;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Performs a streamed modification of the content.
  * Each File body encountered will result in a session save operation, to cause the underlying Resource implementation
@@ -79,7 +79,7 @@ public class StreamedUploadOperation ext
     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<String, List<String>>();
+            Map<String, List<String>> formFields = new HashMap<>();
             boolean streamingBodies = false;
             while (partsIterator.hasNext()) {
                 Part part = partsIterator.next();
@@ -117,7 +117,7 @@ public class StreamedUploadOperation ext
     private void addField(Map<String, List<String>> formFields, String name, Part part) {
         List<String> values = formFields.get(name);
         if ( values == null ) {
-            values = new ArrayList<String>();
+            values = new ArrayList<>();
             formFields.put(name, values);
         }
         try {
@@ -153,7 +153,7 @@ public class StreamedUploadOperation ext
         }
         String name = getUploadName(part);
         Resource fileResource = parentResource.getChild(name);
-        Map<String, Object> fileProps = new HashMap<String, Object>();
+        Map<String, Object> fileProps = new HashMap<>();
         if (fileResource == null) {
             fileProps.put("jcr:primaryType", NT_FILE);
             fileResource = parentResource.getResourceResolver().create(parentResource, name, fileProps);