SLING-7831 support injecting custom/alternate PostResponse
authorEric Norman <enorman@apache.org>
Sun, 19 Aug 2018 20:38:40 +0000 (13:38 -0700)
committerEric Norman <enorman@apache.org>
Sun, 19 Aug 2018 20:38:40 +0000 (13:38 -0700)
implementations for the servlets in the usermanager and accessmanager
bundles

pom.xml
src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessPostServlet.java
src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAclServlet.java
src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/DeleteAcesServlet.java
src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java
src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/package-info.java

diff --git a/pom.xml b/pom.xml
index d0cdb61..8da4c32 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,9 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
+                        <Embed-Dependency>
+                            org.apache.sling.servlets.post;inline="org/apache/sling/servlets/post/impl/helper/MediaRangeList*"
+                        </Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>
index 0044688..2887921 100644 (file)
@@ -18,7 +18,9 @@ package org.apache.sling.jcr.jackrabbit.accessmanager.post;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -34,13 +36,17 @@ import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.servlets.post.HtmlResponse;
 import org.apache.sling.api.servlets.SlingAllMethodsServlet;
 import org.apache.sling.api.wrappers.SlingRequestPaths;
 import org.apache.sling.servlets.post.AbstractPostResponse;
+import org.apache.sling.servlets.post.HtmlResponse;
+import org.apache.sling.servlets.post.JSONResponse;
 import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
 import org.apache.sling.servlets.post.SlingPostConstants;
-import org.apache.sling.servlets.post.JSONResponse;
+import org.apache.sling.servlets.post.impl.helper.MediaRangeList;
+import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,6 +61,13 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
      */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    /** Sorted list of post response creator holders. */
+    private final List<PostResponseCreatorHolder> postResponseCreators = new ArrayList<>();
+
+    /** Cached array of post response creators used during request processing. */
+    private PostResponseCreator[] cachedPostResponseCreators = new PostResponseCreator[0];
+
+
        /* (non-Javadoc)
         * @see org.apache.sling.api.servlets.SlingAllMethodsServlet#doPost(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.SlingHttpServletResponse)
         */
@@ -63,7 +76,7 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
                        SlingHttpServletResponse httpResponse) throws ServletException,
                        IOException {
         // prepare the response
-               AbstractPostResponse response = createHtmlResponse(request);
+       PostResponse response = createPostResponse(request);
         response.setReferer(request.getHeader("referer"));
 
         // calculate the paths
@@ -145,13 +158,53 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
      *   <li>the response content type is application/json
      * </ul>
      * or a {@link org.apache.sling.api.servlets.HtmlResponse} otherwise
+     * @deprecated use {@link #createPostResponse(SlingHttpServletRequest)} instead
      */
+       @Deprecated
     protected AbstractPostResponse createHtmlResponse(SlingHttpServletRequest req) {
-       if (JSONResponse.RESPONSE_CONTENT_TYPE.equals(req.getResponseContentType())) {
-               return new JSONResponse();
-       } else {
+       return (AbstractPostResponse)createPostResponse(req);
+    }
+       
+    /**
+     * Creates an instance of a PostResponse.
+     * @param req The request being serviced
+     * @return a {@link org.apache.sling.servlets.post.impl.helper.JSONResponse} if any of these conditions are true:
+     * <ul>
+     *   <li> the request has an <code>Accept</code> header of <code>application/json</code></li>
+     *   <li>the request is a JSON POST request (see SLING-1172)</li>
+     *   <li>the request has a request parameter <code>:accept=application/json</code></li>
+     * </ul>
+     * or a {@link org.apache.sling.api.servlets.PostResponse} otherwise
+     */
+    PostResponse createPostResponse(final SlingHttpServletRequest req) {
+        for (final PostResponseCreator creator : cachedPostResponseCreators) {
+            final PostResponse response = creator.createPostResponse(req);
+            if (response != null) {
+                return response;
+            }
+        }
+
+        //for backward compatibility, if no "accept" request param or header is supplied
+        // then prefer the SlingHttpServletRequest#getResponseContentType value
+        MediaRangeList mediaRangeList = null;
+        String queryParam = req.getParameter(MediaRangeList.PARAM_ACCEPT);
+        if (queryParam == null || queryParam.trim().length() == 0) {
+               String headerValue = req.getHeader(MediaRangeList.HEADER_ACCEPT);
+               if (headerValue == null || headerValue.trim().length() == 0) {
+                       //no param or header supplied, so try the response content type
+                       mediaRangeList = new MediaRangeList(req.getResponseContentType());
+               }
+        }
+
+        // Fall through to default behavior
+        if (mediaRangeList == null) {
+               mediaRangeList = new MediaRangeList(req);
+        }
+        if (JSONResponse.RESPONSE_CONTENT_TYPE.equals(mediaRangeList.prefer("text/html", JSONResponse.RESPONSE_CONTENT_TYPE))) {
+            return new JSONResponse();
+        } else {
             return new HtmlResponse();
-       }
+        }
     }
        
        /**
@@ -160,9 +213,24 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
         * @param request the sling http request to process
         * @param response the response
         * @param changes
+        * 
+        * @deprecated use {@link #handleOperation(SlingHttpServletRequest, PostResponse, List)} instead
+        */
+    @Deprecated
+       protected void handleOperation(SlingHttpServletRequest request,
+                       AbstractPostResponse response, List<Modification> changes) throws RepositoryException {
+               handleOperation(request, (PostResponse)response, changes);
+       }
+    
+       /**
+        * Extending Servlet should implement this operation to do the work
+        *
+        * @param request the sling http request to process
+        * @param response the response
+        * @param changes
         */
        abstract protected void handleOperation(SlingHttpServletRequest request,
-                       AbstractPostResponse response, List<Modification> changes) throws RepositoryException;
+                       PostResponse response, List<Modification> changes) throws RepositoryException;
 
 
     /**
@@ -170,8 +238,20 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
      *
      * @param ctx the post processor
      * @return the redirect location or <code>null</code>
+     * @deprecated use {@link #getRedirectUrl(HttpServletRequest, PostResponse)} instead
      */
+       @Deprecated
     protected String getRedirectUrl(HttpServletRequest request, AbstractPostResponse ctx) {
+       return getRedirectUrl(request, (PostResponse)ctx);
+    }
+    
+    /**
+     * compute redirect URL (SLING-126)
+     *
+     * @param ctx the post processor
+     * @return the redirect location or <code>null</code>
+     */
+    protected String getRedirectUrl(HttpServletRequest request, PostResponse ctx) {
         // redirect param has priority (but see below, magic star)
         String result = request.getParameter(SlingPostConstants.RP_REDIRECT_TO);
         if (result != null && ctx.getPath() != null) {
@@ -321,4 +401,74 @@ public abstract class AbstractAccessPostServlet extends SlingAllMethodsServlet {
                 + resourcePath);
 
     }
+
+
+    /**
+     * Bind a new post response creator
+     */
+       // NOTE: the @Reference annotation is not inherited, so subclasses will need to override the #bindPostResponseCreator 
+       // and #unbindPostResponseCreator methods to provide the @Reference annotation.     
+       //
+       // @Reference(service = PostResponseCreator.class,
+       //         cardinality = ReferenceCardinality.MULTIPLE,
+       //         policy = ReferencePolicy.DYNAMIC)
+    protected void bindPostResponseCreator(final PostResponseCreator creator, final Map<String, Object> properties) {
+        final PostResponseCreatorHolder nngh = new PostResponseCreatorHolder();
+        nngh.creator = creator;
+        nngh.ranking = getRanking(properties);
+
+        synchronized ( this.postResponseCreators ) {
+            int index = 0;
+            while ( index < this.postResponseCreators.size() &&
+                    nngh.ranking < this.postResponseCreators.get(index).ranking ) {
+                index++;
+            }
+            if ( index == this.postResponseCreators.size() ) {
+                this.postResponseCreators.add(nngh);
+            } else {
+                this.postResponseCreators.add(index, nngh);
+            }
+            this.updatePostResponseCreatorCache();
+        }
+    }
+
+    /**
+     * Unbind a post response creator
+     */
+    protected void unbindPostResponseCreator(final PostResponseCreator creator, final Map<String, Object> properties) {
+        synchronized ( this.postResponseCreators ) {
+            final Iterator<PostResponseCreatorHolder> i = this.postResponseCreators.iterator();
+            while ( i.hasNext() ) {
+                final PostResponseCreatorHolder current = i.next();
+                if ( current.creator == creator ) {
+                    i.remove();
+                }
+            }
+            this.updatePostResponseCreatorCache();
+        }
+    }
+
+    /**
+     * Update the post response creator cache
+     * This method is called by sync'ed methods, no need to add additional syncing.
+     */
+    private void updatePostResponseCreatorCache() {
+        final PostResponseCreator[] localCache = new PostResponseCreator[this.postResponseCreators.size()];
+        int index = 0;
+        for(final PostResponseCreatorHolder current : this.postResponseCreators) {
+            localCache[index] = current.creator;
+            index++;
+        }
+        this.cachedPostResponseCreators = localCache;
+    }
+    
+    private int getRanking(final Map<String, Object> properties) {
+        final Object val = properties.get(Constants.SERVICE_RANKING);
+        return val instanceof Integer ? (Integer)val : 0;
+    }
+    
+    private static final class PostResponseCreatorHolder {
+        public PostResponseCreator creator;
+        public int ranking;
+    }    
 }
index b56adf3..9ee88f6 100644 (file)
@@ -19,9 +19,7 @@ package org.apache.sling.jcr.jackrabbit.accessmanager.post;
 import java.io.IOException;
 import java.security.Principal;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -31,18 +29,14 @@ import java.util.Set;
 
 import javax.jcr.AccessDeniedException;
 import javax.jcr.Item;
-import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlManager;
 import javax.jcr.security.Privilege;
 import javax.json.Json;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
-import javax.json.JsonValue;
 import javax.json.stream.JsonGenerator;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletResponse;
index a676276..da8e3ec 100644 (file)
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.Item;
@@ -34,10 +35,14 @@ import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.apache.sling.jcr.jackrabbit.accessmanager.DeleteAces;
-import org.apache.sling.servlets.post.AbstractPostResponse;
 import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
 import org.apache.sling.servlets.post.SlingPostConstants;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
 
 /**
  * <p>
@@ -80,12 +85,34 @@ import org.osgi.service.component.annotations.Component;
 public class DeleteAcesServlet extends AbstractAccessPostServlet implements DeleteAces {
        private static final long serialVersionUID = 3784866802938282971L;
 
+    /**
+     * Overridden since the @Reference annotation is not inherited from the super method
+     *  
+        * @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#bindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
+        */
+       @Override
+    @Reference(service = PostResponseCreator.class,
+           cardinality = ReferenceCardinality.MULTIPLE,
+           policy = ReferencePolicy.DYNAMIC)
+       protected void bindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
+               super.bindPostResponseCreator(creator, properties);
+       }
+       
+       /* (non-Javadoc)
+        * @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#unbindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
+        */
+       @Override
+       protected void unbindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
+               super.unbindPostResponseCreator(creator, properties);
+       }
+    
+       
        /* (non-Javadoc)
-        * @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.servlets.HtmlResponse, java.util.List)
+        * @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.servlets.post.PostResponse, java.util.List)
         */
        @Override
        protected void handleOperation(SlingHttpServletRequest request,
-                       AbstractPostResponse htmlResponse, List<Modification> changes)
+                       PostResponse htmlResponse, List<Modification> changes)
                        throws RepositoryException {
 
                Session session = request.getResourceResolver().adaptTo(Session.class);
index 8647cf9..c11f85c 100644 (file)
@@ -35,9 +35,13 @@ import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
-import org.apache.sling.servlets.post.AbstractPostResponse;
 import org.apache.sling.servlets.post.Modification;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
 
 /**
  * <p>
@@ -90,12 +94,34 @@ property= {
 public class ModifyAceServlet extends AbstractAccessPostServlet implements ModifyAce {
        private static final long serialVersionUID = -9182485466670280437L;
 
+    /**
+     * Overridden since the @Reference annotation is not inherited from the super method
+     *  
+        * @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#bindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
+        */
+       @Override
+    @Reference(service = PostResponseCreator.class,
+           cardinality = ReferenceCardinality.MULTIPLE,
+           policy = ReferencePolicy.DYNAMIC)
+       protected void bindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
+               super.bindPostResponseCreator(creator, properties);
+       }
+       
+       /* (non-Javadoc)
+        * @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#unbindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
+        */
+       @Override
+       protected void unbindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
+               super.unbindPostResponseCreator(creator, properties);
+       }
+    
+       
        /* (non-Javadoc)
-        * @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.servlets.HtmlResponse, java.util.List)
+        * @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.servlets.post.PostResponse, java.util.List)
         */
        @Override
        protected void handleOperation(SlingHttpServletRequest request,
-                       AbstractPostResponse response, List<Modification> changes)
+                       PostResponse response, List<Modification> changes)
                        throws RepositoryException {
                Session session = request.getResourceResolver().adaptTo(Session.class);
        String resourcePath = request.getResource().getPath();
index 708b13c..79406f4 100644 (file)
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("3.0.1")
+@org.osgi.annotation.versioning.Version("3.1.0")
 package org.apache.sling.jcr.jackrabbit.accessmanager.post;