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

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1790179 13f79535-47bb-0310-9956-ffa450edef68
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 89274c3..f6574b0 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,8 +19,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
@@ -35,116 +33,109 @@
 /**
  * The <code>AbstractCopyMoveOperation</code> is the abstract base close for
  * the {@link CopyOperation} and {@link MoveOperation} classes implementing
- * common behaviour.
+ * common behavior.
  */
 abstract class AbstractCopyMoveOperation extends AbstractPostOperation {
 
     @Override
-    protected final void doRun(SlingHttpServletRequest request,
-            PostResponse response,
-            List<Modification> changes)
+    protected final void doRun(final SlingHttpServletRequest request,
+            final PostResponse response,
+            final List<Modification> changes)
     throws PersistenceException {
-        try {
-            Session session = request.getResourceResolver().adaptTo(Session.class);
+        final VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
 
-            VersioningConfiguration versioningConfiguration = getVersioningConfiguration(request);
+        final Resource resource = request.getResource();
+        final 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 "
+        // 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);
             }
 
-            // register whether the path ends with a trailing slash
-            boolean trailingSlash = dest.endsWith("/");
+        } 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
-            final 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) {
+            // 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 + ": destination exists");
+                            + dest + ": parent of destination does not exist");
                     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);
             }
 
-            Iterator<Resource> resources = getApplyToResources(request);
-            Resource destResource = null;
-            if (resources == 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());
-                return;
-            }
-            // finally apply the ordering parameter
-            this.jcrSsupport.orderNode(request, destResource, changes);
-        } catch ( final RepositoryException re) {
-            throw new PersistenceException(re.getMessage(), re);
+            // 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);
     }
 
     /**
@@ -162,13 +153,14 @@
      * @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 occurs executing
+     * @throws PersistenceException May be thrown if an error occurs executing
      *             the operation.
      */
-    protected abstract Resource execute(List<Modification> changes, Resource source,
+    protected abstract Resource execute(List<Modification> changes,
+            Resource source,
             String destParent,
             String destName,
             VersioningConfiguration versioningConfiguration)
-    throws PersistenceException, RepositoryException;
+    throws PersistenceException;
 
 }
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
index f336a02..95f503f 100644
--- a/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
+++ b/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
@@ -17,22 +17,19 @@
 
 package org.apache.sling.servlets.post.impl.operations;
 
-import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
 import javax.servlet.ServletException;
 
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.request.RequestParameter;
 import org.apache.sling.api.request.RequestParameterMap;
@@ -135,9 +132,9 @@
     }
 
     /**
-     * Create node(s) according to current request
+     * Create resource(s) according to current request
      *
-     * @throws RepositoryException if a repository error occurs
+     * @throws PersistenceException if a resource error occurs
      */
     protected void processCreate(final ResourceResolver resolver,
             final Map<String, RequestProperty> reqProperties,
@@ -159,10 +156,6 @@
         }
     }
 
-    private boolean isVersionable(final Node node) throws RepositoryException {
-        return node.isNodeType("mix:versionable");
-    }
-
     protected void updateNodeType(final ResourceResolver resolver,
                     final String path,
                     final Map<String, RequestProperty> reqProperties,
@@ -175,13 +168,13 @@
             final ModifiableValueMap mvm = rsrc.adaptTo(ModifiableValueMap.class);
             if ( mvm != null ) {
                 final Node node = rsrc.adaptTo(Node.class);
-                final boolean wasVersionable = (node == null ? false : isVersionable(node));
+                final boolean wasVersionable = (node == null ? false : this.jcrSsupport.isVersionable(rsrc));
 
                 if ( node != null ) {
                     this.jcrSsupport.checkoutIfNecessary(rsrc, changes, versioningConfiguration);
                     node.setPrimaryType(nodeType);
                 } else {
-                    mvm.put("jcr:primaryType", nodeType);
+                    mvm.put(JcrConstants.JCR_PRIMARYTYPE, nodeType);
                 }
 
                 if ( node != null ) {
@@ -189,7 +182,7 @@
                     // the mix:versionable mixin does an implicit checkout
                     if (!wasVersionable &&
                             versioningConfiguration.isCheckinOnNewVersionableNode() &&
-                            isVersionable(node)) {
+                            this.jcrSsupport.isVersionable(rsrc)) {
                         changes.add(Modification.onCheckout(path));
                     }
                 }
@@ -202,42 +195,24 @@
                     final Map<String, RequestProperty> reqProperties,
                     final List<Modification> changes,
                     final VersioningConfiguration versioningConfiguration)
-    throws PersistenceException, RepositoryException {
+    throws PersistenceException {
         final String[] mixins = getMixinTypes(reqProperties, path);
         if (mixins != null) {
 
             final Resource rsrc = resolver.getResource(path);
             final ModifiableValueMap mvm = rsrc.adaptTo(ModifiableValueMap.class);
             if ( mvm != null ) {
-                final Node node = rsrc.adaptTo(Node.class);
+                this.jcrSsupport.checkoutIfNecessary(rsrc, changes, versioningConfiguration);
+                mvm.put(JcrConstants.JCR_MIXINTYPES, mixins);
 
-                final Set<String> newMixins = new HashSet<>();
-                newMixins.addAll(Arrays.asList(mixins));
-
-                // clear existing mixins first
-                if ( node != null ) {
-                    this.jcrSsupport.checkoutIfNecessary(rsrc, changes, versioningConfiguration);
-                    for (NodeType mixin : node.getMixinNodeTypes()) {
-                        String mixinName = mixin.getName();
-                        if (!newMixins.remove(mixinName)) {
-                            node.removeMixin(mixinName);
-                        }
+                for(final String mixin : mixins) {
+                    // this is a bit of a cheat; there isn't a formal checkout, but assigning
+                    // the mix:versionable mixin does an implicit checkout
+                    if (mixin.equals(JcrConstants.MIX_VERSIONABLE) &&
+                            versioningConfiguration.isCheckinOnNewVersionableNode()) {
+                        changes.add(Modification.onCheckout(path));
                     }
-
-                    // add new mixins
-                    for (String mixin : newMixins) {
-                        node.addMixin(mixin);
-                        // this is a bit of a cheat; there isn't a formal checkout, but assigning
-                        // the mix:versionable mixin does an implicit checkout
-                        if (mixin.equals("mix:versionable") &&
-                                versioningConfiguration.isCheckinOnNewVersionableNode()) {
-                            changes.add(Modification.onCheckout(path));
-                        }
-                    }
-                } else {
-                    mvm.put("jcr:mixinTypes", mixins);
                 }
-
             }
         }
     }
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 37fef28..fd918f5 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.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.servlets.post.Modification;
 import org.apache.sling.servlets.post.VersioningConfiguration;
@@ -43,22 +44,28 @@
     }
 
     @Override
-    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;
+    protected Resource execute(final List<Modification> changes,
+            final Resource source,
+            final String destParent,
+            final String destName,
+            final VersioningConfiguration versioningConfiguration)
+    throws PersistenceException {
+        try {
+            // ensure we have an item underlying the request's resource
+            Item item = source.adaptTo(Item.class);
+            if (item == null) {
+                return null;
+            }
+
+            Item destItem = copy(item, (Node) item.getSession().getItem(destParent), destName);
+
+            String dest = destParent + "/" + destName;
+            changes.add(Modification.onCopied(source.getPath(), dest));
+            log.debug("copy {} to {}", source, dest);
+            return source.getResourceResolver().getResource(destItem.getPath());
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re, source.getPath(), null);
         }
-
-        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 source.getResourceResolver().getResource(destItem.getPath());
     }
 
     /**
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
index fc1a8df..e9c866b 100644
--- 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
@@ -81,6 +81,13 @@
         return false;
     }
 
+    public boolean isVersionable(final Resource rsrc) throws PersistenceException {
+        if ( supportImpl != null ) {
+            return ((JCRSupportImpl)supportImpl).isVersionable(rsrc);
+        }
+        return false;
+    }
+
     public boolean isNodeType(final Resource rsrc, final String typeHint) {
         if ( rsrc != null && supportImpl != null ) {
             return ((JCRSupportImpl)supportImpl).isNodeType(rsrc, typeHint);
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
index d5b040c..7318c39 100644
--- 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
@@ -149,7 +149,16 @@
     }
 
     private boolean isVersionable(final Node node) throws RepositoryException {
-        return node.isNodeType("mix:versionable");
+        return node.isNodeType(JcrConstants.MIX_VERSIONABLE);
+    }
+
+    public boolean isVersionable(final Resource rsrc) throws PersistenceException {
+        try {
+            final Node node = rsrc.adaptTo(Node.class);
+            return node != null && isVersionable(node);
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re, rsrc.getPath(), null);
+        }
     }
 
     public boolean checkin(final Resource rsrc)
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 05c1ffb..eda2acb 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
@@ -40,36 +40,43 @@
     }
 
     @Override
-    protected Resource execute(List<Modification> changes, Resource source,
-            String destParent, String destName,
-            VersioningConfiguration versioningConfiguration)
-    throws PersistenceException, RepositoryException {
-        // ensure we have an item underlying the request's resource
-        Item item = source.adaptTo(Item.class);
-        if (item == null) {
-            return null;
+    protected Resource execute(final List<Modification> changes,
+            final Resource source,
+            String destParent,
+            String destName,
+            final VersioningConfiguration versioningConfiguration)
+    throws PersistenceException {
+        try {
+            // ensure we have an item underlying the request's resource
+            Item item = source.adaptTo(Item.class);
+            if (item == null) {
+                return null;
+            }
+
+            if (destName == null) {
+                destName = source.getName();
+            }
+
+            String sourcePath = source.getPath();
+            if (destParent.equals("/")) {
+                destParent = "";
+            }
+            String destPath = destParent + "/" + destName;
+            Session session = item.getSession();
+
+            this.jcrSsupport.checkoutIfNecessary(source.getParent(), changes, versioningConfiguration);
+
+            final Resource dest = source.getResourceResolver().getResource(destPath);
+            if (dest != null ) {
+                source.getResourceResolver().delete(dest);
+            }
+
+            session.move(sourcePath, destPath);
+            changes.add(Modification.onMoved(sourcePath, destPath));
+            return source.getResourceResolver().getResource(destPath);
+        } catch ( final RepositoryException re) {
+            throw new PersistenceException(re.getMessage(), re);
         }
-
-        if (destName == null) {
-            destName = source.getName();
-        }
-
-        String sourcePath = source.getPath();
-        if (destParent.equals("/")) {
-            destParent = "";
-        }
-        String destPath = destParent + "/" + destName;
-        Session session = item.getSession();
-
-        this.jcrSsupport.checkoutIfNecessary(source.getParent(), changes, versioningConfiguration);
-
-        if (session.itemExists(destPath)) {
-            session.getItem(destPath).remove();
-        }
-
-        session.move(sourcePath, destPath);
-        changes.add(Modification.onMoved(sourcePath, destPath));
-        return source.getResourceResolver().getResource(destPath);
     }
 
 }