Package org.apache.sling.engine.impl.request

Source Code of org.apache.sling.engine.impl.request.RequestData$SlingHttpServletRequestFactory

/*
* 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.engine.impl.request;

import static org.apache.sling.api.SlingConstants.SLING_CURRENT_SERVLET_NAME;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RecursionTooDeepException;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.request.RequestProgressTracker;
import org.apache.sling.api.request.RequestUtil;
import org.apache.sling.api.request.TooManyCallsException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.ServletResolver;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
import org.apache.sling.engine.impl.SlingHttpServletRequestImpl;
import org.apache.sling.engine.impl.SlingHttpServletRequestImpl3;
import org.apache.sling.engine.impl.SlingHttpServletResponseImpl;
import org.apache.sling.engine.impl.SlingMainServlet;
import org.apache.sling.engine.impl.SlingRequestProcessorImpl;
import org.apache.sling.engine.impl.StaticResponseHeader;
import org.apache.sling.engine.impl.adapter.SlingServletRequestAdapter;
import org.apache.sling.engine.impl.adapter.SlingServletResponseAdapter;
import org.apache.sling.engine.impl.parameters.ParameterSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The <code>RequestData</code> class provides access to objects which are set
* on a Servlet Request wide basis such as the repository session, the
* persistence manager, etc.
*
* The setup order is:
* <ol>
*   <li>Invoke constructor</li>
*   <li>Invoke initResource()</li>
*   <li>Invoke initServlet()</li>
* </ol>
* @see ContentData
*/
public class RequestData {

    /** default log */
    private final Logger log = LoggerFactory.getLogger(RequestData.class);

    /**
     * The default value for the number of recursive inclusions for a single
     * instance of this class (value is 50).
     */
    public static final int DEFAULT_MAX_INCLUSION_COUNTER = 50;

    /**
     * The default value for the number of calls to the
     * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
     * method for a single instance of this class (value is 1000).
     */
    public static final int DEFAULT_MAX_CALL_COUNTER = 1000;

    /**
     * The name of the request attribute providing the resource addressed by the
     * request URL.
     */
    public static final String REQUEST_RESOURCE_PATH_ATTR = "$$sling.request.resource$$";

    /**
     * The maximum inclusion depth (default
     * {@link #DEFAULT_MAX_INCLUSION_COUNTER}). This value is compared to the
     * number of entries in the {@link #contentDataStack} when the
     * {@link #pushContent(Resource, RequestPathInfo)} method is called.
     */
    private static int maxInclusionCounter = DEFAULT_MAX_INCLUSION_COUNTER;

    /**
     * The maximum number of scripts which may be included through the
     * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
     * method (default {@link #DEFAULT_MAX_CALL_COUNTER}). This number should
     * not be too small to prevent request aborts.
     */
    private static int maxCallCounter = DEFAULT_MAX_CALL_COUNTER;

    /**
     * The name of the request attribute to override the max call number (-1 for infinite or integer value).
     */
    private static String REQUEST_MAX_CALL_OVERRIDE = SlingMainServlet.PROP_MAX_CALL_COUNTER;

    private static SlingMainServlet SLING_MAIN_SERVLET;

    private static SlingHttpServletRequestFactory REQUEST_FACTORY;

    private static ArrayList<StaticResponseHeader> ADDITIONAL_RESPONSE_HEADERS;

    /** The SlingMainServlet used for request dispatching and other stuff */
    private final SlingRequestProcessorImpl slingRequestProcessor;

    private final long startTimestamp;

    /** The original servlet Servlet Request Object */
    private HttpServletRequest servletRequest;

    /** The original servlet Servlet Response object */
    private HttpServletResponse servletResponse;

    /** The original servlet Servlet Request Object */
    private SlingHttpServletRequest slingRequest;

    /** The original servlet Servlet Response object */
    private SlingHttpServletResponse slingResponse;

    /** The parameter support class */
    private ParameterSupport parameterSupport;

    private ResourceResolver resourceResolver;

    private RequestProgressTracker requestProgressTracker;

    /** the current ContentData */
    private ContentData currentContentData;

    /**
     * the number of servlets called by
     * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
     */
    private int servletCallCounter;

    /**
     * The name of the currently active serlvet.
     *
     * @see #setActiveServletName(String)
     * @see #getActiveServletName()
     */
    private String activeServletName;

    /**
     * Recursion depth
     */
    private int recursionDepth;

    /**
     * The peak value for the recursion depth.
     */
    private int peakRecusionDepth;

    public static void setMaxCallCounter(int maxCallCounter) {
        RequestData.maxCallCounter = maxCallCounter;
    }

    public static int getMaxCallCounter() {
        return maxCallCounter;
    }

    public static void setMaxIncludeCounter(int maxInclusionCounter) {
        RequestData.maxInclusionCounter = maxInclusionCounter;
    }

    public static int getMaxIncludeCounter() {
        return maxInclusionCounter;
    }

    public static void setSlingMainServlet(final SlingMainServlet slingMainServlet) {
        RequestData.SLING_MAIN_SERVLET = slingMainServlet;
        RequestData.REQUEST_FACTORY = null;
    }

    public static void setAdditionalResponseHeaders(ArrayList<StaticResponseHeader> mappings){
        RequestData.ADDITIONAL_RESPONSE_HEADERS = mappings;
    }

    public static ArrayList<StaticResponseHeader> getAdditionalResponseHeaders() {
        return ADDITIONAL_RESPONSE_HEADERS;
    }

    public RequestData(SlingRequestProcessorImpl slingRequestProcessor,
            HttpServletRequest request, HttpServletResponse response) {
        this.startTimestamp = System.currentTimeMillis();

        this.slingRequestProcessor = slingRequestProcessor;

        this.servletRequest = request;
        this.servletResponse = response;

        this.slingRequest = getSlingHttpServletRequestFactory().createRequest(this, this.servletRequest);
        this.slingResponse = new SlingHttpServletResponseImpl(this,
            servletResponse);

        this.requestProgressTracker = new SlingRequestProgressTracker();
        this.requestProgressTracker.log(
            "Method={0}, PathInfo={1}",
            this.slingRequest.getMethod(), this.slingRequest.getPathInfo()
        );
    }

    public Resource initResource(ResourceResolver resourceResolver) {
        // keep the resource resolver for request processing
        this.resourceResolver = resourceResolver;

        // resolve the resource
        requestProgressTracker.startTimer("ResourceResolution");
        final SlingHttpServletRequest request = getSlingRequest();
        Resource resource = resourceResolver.resolve(request, request.getPathInfo());
        if (request.getAttribute(REQUEST_RESOURCE_PATH_ATTR) == null) {
            request.setAttribute(REQUEST_RESOURCE_PATH_ATTR, resource.getPath());
        }
        requestProgressTracker.logTimer("ResourceResolution",
            "URI={0} resolves to Resource={1}",
            getServletRequest().getRequestURI(), resource);
        return resource;
    }

    public void initServlet(final Resource resource,
            final ServletResolver sr) {
        // the resource and the request path info, will never be null
        RequestPathInfo requestPathInfo = new SlingRequestPathInfo(resource);
        ContentData contentData = setContent(resource, requestPathInfo);

      requestProgressTracker.log("Resource Path Info: {0}", requestPathInfo);

        // finally resolve the servlet for the resource
        requestProgressTracker.startTimer("ServletResolution");
        Servlet servlet = sr.resolveServlet(slingRequest);
        requestProgressTracker.logTimer("ServletResolution",
            "URI={0} handled by Servlet={1}",
            getServletRequest().getRequestURI(), (servlet == null ? "-none-" : RequestUtil.getServletName(servlet)));
        contentData.setServlet(servlet);
    }

    public SlingRequestProcessorImpl getSlingRequestProcessor() {
        return slingRequestProcessor;
    }

    public HttpServletRequest getServletRequest() {
        return servletRequest;
    }

    public HttpServletResponse getServletResponse() {
        return servletResponse;
    }

    public SlingHttpServletRequest getSlingRequest() {
        return slingRequest;
    }

    public SlingHttpServletResponse getSlingResponse() {
        return slingResponse;
    }

    // ---------- Request Helper

    /**
     * Unwraps the ServletRequest to a SlingHttpServletRequest.
     *
     * @throws IllegalArgumentException If the <code>request</code> is not a
     *             <code>SlingHttpServletRequest</code> and not a
     *             <code>ServletRequestWrapper</code> wrapping a
     *             <code>SlingHttpServletRequest</code>.
     */
    public static SlingHttpServletRequest unwrap(ServletRequest request) {

        // early check for most cases
        if (request instanceof SlingHttpServletRequest) {
            return (SlingHttpServletRequest) request;
        }

        // unwrap wrappers
        while (request instanceof ServletRequestWrapper) {
            request = ((ServletRequestWrapper) request).getRequest();

            // immediate termination if we found one
            if (request instanceof SlingHttpServletRequest) {
                return (SlingHttpServletRequest) request;
            }
        }

        // if we unwrapped everything and did not find a
        // SlingHttpServletRequest, we lost
        throw new IllegalArgumentException(
            "ServletRequest not wrapping SlingHttpServletRequest");
    }

    /**
     * Unwraps the SlingHttpServletRequest to a SlingHttpServletRequestImpl
     *
     * @param request
     * @throws IllegalArgumentException If <code>request</code> is not a
     *             <code>SlingHttpServletRequestImpl</code> and not
     *             <code>SlingHttpServletRequestWrapper</code> wrapping a
     *             <code>SlingHttpServletRequestImpl</code>.
     */
    public static SlingHttpServletRequestImpl unwrap(
            SlingHttpServletRequest request) {
        while (request instanceof SlingHttpServletRequestWrapper) {
            request = ((SlingHttpServletRequestWrapper) request).getSlingRequest();
        }

        if (request instanceof SlingHttpServletRequestImpl) {
            return (SlingHttpServletRequestImpl) request;
        }

        throw new IllegalArgumentException(
            "SlingHttpServletRequest not of correct type");
    }

    /**
     * Unwraps the ServletRequest to a SlingHttpServletRequest.
     *
     * @throws IllegalArgumentException If the <code>response</code> is not a
     *             <code>SlingHttpServletResponse</code> and not a
     *             <code>ServletResponseWrapper</code> wrapping a
     *             <code>SlingHttpServletResponse</code>.
     */
    public static SlingHttpServletResponse unwrap(ServletResponse response) {

        // early check for most cases
        if (response instanceof SlingHttpServletResponse) {
            return (SlingHttpServletResponse) response;
        }

        // unwrap wrappers
        while (response instanceof ServletResponseWrapper) {
            response = ((ServletResponseWrapper) response).getResponse();

            // immediate termination if we found one
            if (response instanceof SlingHttpServletResponse) {
                return (SlingHttpServletResponse) response;
            }
        }

        // if we unwrapped everything and did not find a
        // SlingHttpServletResponse, we lost
        throw new IllegalArgumentException(
            "ServletResponse not wrapping SlingHttpServletResponse");
    }

    /**
     * Unwraps a SlingHttpServletResponse to a SlingHttpServletResponseImpl
     *
     * @param response
     * @throws IllegalArgumentException If <code>response</code> is not a
     *             <code>SlingHttpServletResponseImpl</code> and not
     *             <code>SlingHttpServletResponseWrapper</code> wrapping a
     *             <code>SlingHttpServletResponseImpl</code>.
     */
    public static SlingHttpServletResponseImpl unwrap(
            SlingHttpServletResponse response) {
        while (response instanceof SlingHttpServletResponseWrapper) {
            response = ((SlingHttpServletResponseWrapper) response).getSlingResponse();
        }

        if (response instanceof SlingHttpServletResponseImpl) {
            return (SlingHttpServletResponseImpl) response;
        }

        throw new IllegalArgumentException(
            "SlingHttpServletResponse not of correct type");
    }

    /**
     * @param request
     * @throws IllegalArgumentException If the <code>request</code> is not a
     *             <code>SlingHttpServletRequest</code> and not a
     *             <code>ServletRequestWrapper</code> wrapping a
     *             <code>SlingHttpServletRequest</code>.
     */
    public static RequestData getRequestData(ServletRequest request) {
        return unwrap(unwrap(request)).getRequestData();
    }

    /**
     * @param request
     * @throws IllegalArgumentException If <code>request</code> is not a
     *             <code>SlingHttpServletRequestImpl</code> and not
     *             <code>SlingHttpServletRequestWrapper</code> wrapping a
     *             <code>SlingHttpServletRequestImpl</code>.
     */
    public static RequestData getRequestData(SlingHttpServletRequest request) {
        return unwrap(request).getRequestData();
    }

    /**
     * @param request
     * @throws IllegalArgumentException if <code>request</code> is not a
     *             <code>HttpServletRequest</code> of if <code>request</code>
     *             is not backed by <code>SlingHttpServletRequestImpl</code>.
     */
    public static SlingHttpServletRequest toSlingHttpServletRequest(
            ServletRequest request) {

        // unwrap to SlingHttpServletRequest, may throw if no
        // SlingHttpServletRequest is wrapped in request
        SlingHttpServletRequest cRequest = unwrap(request);

        // ensure the SlingHttpServletRequest is backed by
        // SlingHttpServletRequestImpl
        RequestData.unwrap(cRequest);

        // if the request is not wrapper at all, we are done
        if (cRequest == request) {
            return cRequest;
        }

        // ensure the request is a HTTP request
        if (!(request instanceof HttpServletRequest)) {
            throw new IllegalArgumentException("Request is not an HTTP request");
        }

        // otherwise, we create a new response wrapping the servlet response
        // and unwrapped component response
        return new SlingServletRequestAdapter(cRequest,
            (HttpServletRequest) request);
    }

    /**
     * @param response
     * @throws IllegalArgumentException if <code>response</code> is not a
     *             <code>HttpServletResponse</code> of if
     *             <code>response</code> is not backed by
     *             <code>SlingHttpServletResponseImpl</code>.
     */
    public static SlingHttpServletResponse toSlingHttpServletResponse(
            ServletResponse response) {

        // unwrap to SlingHttpServletResponse
        SlingHttpServletResponse cResponse = unwrap(response);

        // check type of response, don't care actually for the response itself
        RequestData.unwrap(cResponse);

        // if the servlet response is actually the SlingHttpServletResponse, we
        // are done
        if (cResponse == response) {
            return cResponse;
        }

        // ensure the response is a HTTP response
        if (!(response instanceof HttpServletResponse)) {
            throw new IllegalArgumentException(
                "Response is not an HTTP response");
        }

        // otherwise, we create a new response wrapping the servlet response
        // and unwrapped component response
        return new SlingServletResponseAdapter(cResponse,
            (HttpServletResponse) response);
    }

    /**
     * Helper method to call the servlet for the current content data. If the
     * current content data has no servlet, <em>NOT_FOUND</em> (404) error is
     * sent and the method terminates.
     * <p>
     * If the the servlet exists, the
     * {@link org.apache.sling.api.SlingConstants#SLING_CURRENT_SERVLET_NAME} request attribute is set
     * to the name of that servlet and that servlet name is also set as the
     * {@link #setActiveServletName(String) currently active servlet}. After
     * the termination of the servlet (normal or throwing a Throwable) the
     * request attribute is reset to the previous value. The name of the
     * currently active servlet is only reset to the previous value if the
     * servlet terminates normally. In case of a Throwable, the active servlet
     * name is not reset and indicates which servlet caused the potential abort
     * of the request.
     *
     * @param request The request object for the service method
     * @param response The response object for the service method
     * @throws IOException May be thrown by the servlet's service method
     * @throws ServletException May be thrown by the servlet's service method
     */
    public static void service(SlingHttpServletRequest request,
            SlingHttpServletResponse response) throws IOException,
            ServletException {

        RequestData requestData = RequestData.getRequestData(request);
        Servlet servlet = requestData.getContentData().getServlet();
        if (servlet == null) {

            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                "No Servlet to handle request");

        } else {

            String name = RequestUtil.getServletName(servlet);

            // verify the number of service calls in this request
            if (requestData.hasServletMaxCallCount(request)) {
                throw new TooManyCallsException(name);
            }

            // replace the current servlet name in the request
            Object oldValue = request.getAttribute(SLING_CURRENT_SERVLET_NAME);
            request.setAttribute(SLING_CURRENT_SERVLET_NAME, name);

            // setup the tracker for this service call
            String timerName = name + "#" + requestData.servletCallCounter;
            requestData.servletCallCounter++;
            requestData.getRequestProgressTracker().startTimer(timerName);

            try {

                String callerServlet = requestData.setActiveServletName(name);

                servlet.service(request, response);

                requestData.setActiveServletName(callerServlet);

            } finally {

                request.setAttribute(SLING_CURRENT_SERVLET_NAME, oldValue);

                requestData.getRequestProgressTracker().logTimer(timerName);

            }
        }
    }

    // ---------- Content inclusion stacking -----------------------------------

    public ContentData setContent(final Resource resource,
            final RequestPathInfo requestPathInfo) {
        if ( this.recursionDepth >=  maxInclusionCounter) {
            throw new RecursionTooDeepException(requestPathInfo.getResourcePath());
        }
        this.recursionDepth++;
        if (this.recursionDepth > this.peakRecusionDepth) {
            this.peakRecusionDepth = this.recursionDepth;
        }
        currentContentData = new ContentData(resource, requestPathInfo);
        return currentContentData;
    }

    public void resetContent(final ContentData data) {
        this.recursionDepth--;
        currentContentData = data;
    }

    public ContentData getContentData() {
        return currentContentData;
    }

    public ResourceResolver getResourceResolver() {
        return resourceResolver;
    }

    public RequestProgressTracker getRequestProgressTracker() {
        return requestProgressTracker;
    }

    public int getPeakRecusionDepth() {
        return peakRecusionDepth;
    }

    public int getServletCallCount() {
        return servletCallCounter;
    }

    /**
     * Returns {@code true} if the number of {@code RequestDispatcher.include}
     * calls has been reached within the given request. That maximum number may
     * either be defined by the {@link #REQUEST_MAX_CALL_OVERRIDE} request
     * attribute or the {@link SlingMainServlet#PROP_MAX_CALL_COUNTER}
     * configuration of the {@link SlingMainServlet}.
     *
     * @param request The request to check
     * @return {@code true} if the maximum number of calls has been reached (or
     *         surpassed)
     */
    private boolean hasServletMaxCallCount(final ServletRequest request) {
        // verify the number of service calls in this request
        log.debug("Servlet call counter : {}", getServletCallCount());

        // max number of calls can be overriden with a request attribute (-1 for
        // infinite or integer value)
        int maxCallCounter = RequestData.getMaxCallCounter();
        Object reqMaxOverride = request.getAttribute(REQUEST_MAX_CALL_OVERRIDE);
        if (reqMaxOverride instanceof Number) {
            maxCallCounter = ((Number) reqMaxOverride).intValue();
        }

        return (maxCallCounter >= 0) && getServletCallCount() >= maxCallCounter;
    }

    public long getElapsedTimeMsec() {
        return System.currentTimeMillis() - startTimestamp;
    }

    /**
     * Sets the name of the currently active servlet and returns the name of the
     * previously active servlet.
     */
    public String setActiveServletName(String servletName) {
        String old = activeServletName;
        activeServletName = servletName;
        return old;
    }

    /**
     * Returns the name of the currently active servlet. If this name is not
     * <code>null</code> at the end of request processing, more precisly in
     * the case of an uncaught <code>Throwable</code> at the end of request
     * processing, this is the name of the servlet causing the uncaught
     * <code>Throwable</code>.
     */
    public String getActiveServletName() {
        return activeServletName;
    }

    public <Type> Type adaptTo(Object object, Class<Type> type) {
        return SLING_MAIN_SERVLET.adaptTo(object, type);
    }

    public String getMimeType(String fileName) {
        return SLING_MAIN_SERVLET.getMimeType(fileName);
    }

    // ---------- Parameter support -------------------------------------------

    public ServletInputStream getInputStream() throws IOException {
        if (parameterSupport != null && parameterSupport.requestDataUsed()) {
            throw new IllegalStateException(
                "Request Data has already been read");
        }

        // may throw IllegalStateException if the reader has already been
        // acquired
        return getServletRequest().getInputStream();
    }

    public BufferedReader getReader() throws UnsupportedEncodingException,
            IOException {
        if (parameterSupport != null && parameterSupport.requestDataUsed()) {
            throw new IllegalStateException(
                "Request Data has already been read");
        }

        // may throw IllegalStateException if the input stream has already been
        // acquired
        return getServletRequest().getReader();
    }

    public ParameterSupport getParameterSupport() {
        if (parameterSupport == null) {
            parameterSupport = ParameterSupport.getInstance(getServletRequest());
        }

        return parameterSupport;
    }

    // SlingHttpServletRequest instance factory

    private static SlingHttpServletRequestFactory getSlingHttpServletRequestFactory() {
        SlingHttpServletRequestFactory factory = RequestData.REQUEST_FACTORY;
        if (factory == null) {
            SlingMainServlet servlet = RequestData.SLING_MAIN_SERVLET;
            if (servlet == null || servlet.getServletContext() == null
                || servlet.getServletContext().getMajorVersion() < 3) {

                factory = new SlingHttpServletRequestFactory() {
                    public SlingHttpServletRequest createRequest(RequestData requestData, HttpServletRequest request) {
                        return new SlingHttpServletRequestImpl(requestData, request);
                    }
                };
            } else {
                factory = new SlingHttpServletRequestFactory() {
                    public SlingHttpServletRequest createRequest(RequestData requestData, HttpServletRequest request) {
                        return new SlingHttpServletRequestImpl3(requestData, request);
                    }
                };
            }
            RequestData.REQUEST_FACTORY = factory;
        }
        return factory;
    }

    private static interface SlingHttpServletRequestFactory {
        SlingHttpServletRequest createRequest(RequestData requestData, HttpServletRequest request);
    }
}
TOP

Related Classes of org.apache.sling.engine.impl.request.RequestData$SlingHttpServletRequestFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.