| /* |
| * 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.Iterator; |
| import java.util.List; |
| |
| 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; |
| import org.apache.sling.servlets.post.PostResponse; |
| import org.apache.sling.servlets.post.SlingPostConstants; |
| import org.apache.sling.servlets.post.VersioningConfiguration; |
| |
| /** |
| * The <code>AbstractCopyMoveOperation</code> is the abstract base close for |
| * the {@link CopyOperation} and {@link MoveOperation} classes implementing |
| * common behavior. |
| */ |
| abstract class AbstractCopyMoveOperation extends AbstractPostOperation { |
| |
| @Override |
| protected final void doRun(final SlingHttpServletRequest request, |
| final PostResponse response, |
| final List<Modification> changes) |
| throws PersistenceException { |
| final VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request); |
| |
| final Resource resource = request.getResource(); |
| final 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 |
| final 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 |
| final String dstParent = trailingSlash ? dest : ResourceUtil.getParent(dest); |
| |
| // delete destination if already exists |
| if (!trailingSlash && request.getResourceResolver().getResource(dest) != null ) { |
| |
| 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 { |
| this.jcrSsupport.checkoutIfNecessary(request.getResourceResolver().getResource(dstParent), |
| 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("")) { |
| final Resource parentResource = request.getResourceResolver().getResource(dstParent); |
| if (parentResource != null ) { |
| this.jcrSsupport.checkoutIfNecessary(parentResource, 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); |
| } |
| |
| final Iterator<Resource> resources = getApplyToResources(request); |
| final Resource destResource; |
| if (resources == null) { |
| |
| final 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()) { |
| final 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()); |
| return; |
| } |
| // finally apply the ordering parameter |
| this.jcrSsupport.orderNode(request, destResource, changes); |
| } |
| |
| /** |
| * Returns a short name to be used in log and status messages. |
| * @return the name of the operation |
| */ |
| protected abstract String getOperationName(); |
| |
| /** |
| * Actually executes the operation. |
| * |
| * @param changes the changes to execute |
| * @param source The source item to act upon. |
| * @param destParent The absolute path of the parent of the target item. |
| * @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. |
| * @param versioningConfiguration the configuration for versioning |
| * @return the resource which results of this operation |
| * @throws PersistenceException May be thrown if an error occurs executing |
| * the operation. |
| */ |
| protected abstract Resource execute(List<Modification> changes, |
| Resource source, |
| String destParent, |
| String destName, |
| VersioningConfiguration versioningConfiguration) |
| throws PersistenceException; |
| |
| } |