Package org.apache.sling.servlets.get.impl

Source Code of org.apache.sling.servlets.get.impl.RedirectServlet

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

import java.io.IOException;
import java.util.Dictionary;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.servlets.get.impl.helpers.JsonRendererServlet;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The <code>RedirectServlet</code> implements support for GET requests to
* resources of type <code>sling:redirect</code>. This servlet tries to get the
* redirect target by
* <ul>
* <li>first adapting the resource to a {@link ValueMap} and trying to get the
* property <code>sling:target</code>.</li>
* <li>The second attempt is to access the resource <code>sling:target</code>
* below the requested resource and attapt this to a string.</li>
* <p>
* If there is no value found for <code>sling:target</code> a 404 (NOT FOUND)
* status is sent by this servlet. Otherwise a 302 (FOUND, temporary redirect)
* status is sent where the target is the relative URL from the current resource
* to the target resource. Selectors, extension, suffix and query string are
* also appended to the redirect URL.
*/
@SuppressWarnings("serial")
@Component(immediate=true, metatype=true)
@Service(Servlet.class)
@Properties({
    @Property(name="service.description", value="Request Redirect Servlet"),
    @Property(name="service.vendor", value="The Apache Software Foundation"),
    @Property(name="sling.servlet.resourceTypes", value="sling:redirect", propertyPrivate=true),
    @Property(name="sling.servlet.methods", value="GET", propertyPrivate=true),
    @Property(name="sling.servlet.prefix", intValue=-1, propertyPrivate=true)  
})
public class RedirectServlet extends SlingSafeMethodsServlet {

    /** The name of the target property */
    public static final String TARGET_PROP = "sling:target";

    /** The name of the redirect status property */
    public static final String STATUS_PROP = "sling:status";

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

    private Servlet jsonRendererServlet;

    /** Default value for the maximum amount of results that should be returned by the jsonResourceWriter */
    public static final int DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS = 200;

    @Property(intValue=DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS)
    public static final String JSON_RENDERER_MAXIMUM_RESULTS_PROPERTY = "json.maximumresults";

    private int jsonMaximumResults;

    protected void activate(ComponentContext ctx) {
      Dictionary<?, ?> props = ctx.getProperties();
      this.jsonMaximumResults = OsgiUtil.toInteger(props.get(JSON_RENDERER_MAXIMUM_RESULTS_PROPERTY),
          DEFAULT_JSON_RENDERER_MAXIMUM_RESULTS);
      // When the maximumResults get updated, we force a reset for the jsonRendererServlet.
      jsonRendererServlet = getJsonRendererServlet();
    }

    @Override
    protected void doGet(SlingHttpServletRequest request,
            SlingHttpServletResponse response) throws ServletException,
            IOException {

        // handle json export of the redirect node
        if (JsonRendererServlet.EXT_JSON.equals(request.getRequestPathInfo().getExtension())) {
            getJsonRendererServlet().service(request, response);
            return;
        }

        // check for redirectability
        if (response.isCommitted()) {
            // committed response cannot be redirected
            log.warn("RedirectServlet: Response is already committed, not redirecting");
            request.getRequestProgressTracker().log(
                "RedirectServlet: Response is already committed, not redirecting");
            return;
        } else if (request.getAttribute(SlingConstants.ATTR_REQUEST_SERVLET) != null) {
            // included request will not redirect
            log.warn("RedirectServlet: Servlet is included, not redirecting");
            request.getRequestProgressTracker().log(
                "RedirectServlet: Servlet is included, not redirecting");
            return;
        }

        String targetPath = null;

        // convert resource to a value map
        final Resource rsrc = request.getResource();
        final ValueMap valueMap = rsrc.adaptTo(ValueMap.class);
        if (valueMap != null) {
            targetPath = valueMap.get(TARGET_PROP, String.class);
        }
        if (targetPath == null) {
            // old behaviour
            final Resource targetResource = request.getResourceResolver().getResource(
                rsrc, TARGET_PROP);
            if (targetResource == null) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                    "Missing target for redirection");
                return;
            }

            // if the target resource is a path (string), redirect there
            targetPath = targetResource.adaptTo(String.class);
        }

        // if we got a target path, make it external and redirect to it
        if (targetPath != null) {
            if (!isUrl(targetPath)) {
                // make path relative and append selectors, extension etc.
                // this is an absolute URI suitable for the Location header
                targetPath = toRedirectPath(targetPath, request);
            } else {
                // just append any selectors, extension, suffix and query string
                targetPath = appendSelectorsExtensionSuffixQuery(request,
                    new StringBuilder(targetPath)).toString();
            }

            final int status = getStatus(valueMap);

            // redirect the client, use our own setup since we might have a
            // custom response status and we already have converted the target
            // into an absolute URI.
            response.reset();
            response.setStatus(status);
            response.setHeader("Location", targetPath);
            response.flushBuffer();

            return;
        }

        // no way of finding the target, just fail
        response.sendError(HttpServletResponse.SC_NOT_FOUND,
            "Cannot redirect to target resource " + targetPath);
    }

    /**
     * Returns the response status from the {@link #STATUS_PROP} property in the
     * value map. If <code>valueMap</code> is <code>null</code>, the property is
     * not contained in the map or if the value is outside of the value HTTP
     * response status range of [ 100 .. 999 ], the default status 302/FOUND is
     * returned.
     *
     * @param valueMap The <code>valueMap</code> providing the optional status
     *            property.
     * @return The status value as defined above.
     */
    static int getStatus(final ValueMap valueMap) {
        if (valueMap != null) {
            final Integer statusInt = valueMap.get(STATUS_PROP, Integer.class);
            if (statusInt != null) {
                int status = statusInt.intValue();
                if (status >= 100 && status <= 999) {
                    return status;
                }
            }
        }

        // fall back to default value
        return HttpServletResponse.SC_FOUND;

    }

    /**
     * Create an absolute URI suitable for the "Location" response header
     * including any selectors, extension, suffix and query from the current
     * request.
     */
    static String toRedirectPath(String targetPath,
            SlingHttpServletRequest request) {

        // make sure the target path is absolute
        final String rawAbsPath;
        if (targetPath.startsWith("/")) {
            rawAbsPath = targetPath;
        } else {
            rawAbsPath = request.getResource().getPath() + "/" + targetPath;
        }

        final StringBuilder target = new StringBuilder();

        // and ensure the path is normalized, us unnormalized if not possible
        final String absPath = ResourceUtil.normalize(rawAbsPath);
        if (absPath == null) {
            target.append(rawAbsPath);
        } else {
            target.append(absPath);
        }

        appendSelectorsExtensionSuffixQuery(request, target);

        // return the mapped full path
        return request.getResourceResolver().map(request, target.toString());
    }

    /**
     * Appends optional request selectors, extension, suffix and query string to
     * the URL to be prepared in the target string builder and returns the
     * string builder.
     *
     * @param request The Sling HTTP Servlet Request providing access to the
     *            data to be appended
     * @param target The String builder to append the data to. This must not be
     *            null.
     * @return The <code>target</code> string builder.
     * @throws NullPointerException if request or target is <code>null</code>.
     */
    private static StringBuilder appendSelectorsExtensionSuffixQuery(
            SlingHttpServletRequest request, StringBuilder target) {
        // append current selectors, extension and suffix
        final RequestPathInfo rpi = request.getRequestPathInfo();
        if (rpi.getExtension() != null) {

            if (rpi.getSelectorString() != null) {
                target.append('.').append(rpi.getSelectorString());
            }

            target.append('.').append(rpi.getExtension());

            if (rpi.getSuffix() != null) {
                target.append(rpi.getSuffix());
            }
        }

        // append current querystring
        if (request.getQueryString() != null) {
            target.append('?').append(request.getQueryString());
        }

        return target;
    }

    /**
     * Returns an absolute URI built from the given parameters.
     *
     * @param scheme The scheme for the URI to be built.
     * @param host The name of the host.
     * @param port The port or -1 to not add a port number to the URI. For
     *            <code>http</code> and <code>https</code> schemes the port is
     *            not added if it is the default port.
     * @param targetPath The path of the resulting URI. This path is expected to
     *            not be an absolute URI.
     * @return The absolute URI built from the components.
     */
    static String toAbsoluteUri(final String scheme, final String host,
            final int port, final String targetPath) {

        // 1. scheme and host
        final StringBuilder absUriBuilder = new StringBuilder();
        absUriBuilder.append(scheme).append("://").append(host);

        // 2. append the port depending on the scheme and whether the port is
        // the default or not
        if (port > 0) {
            if (!(("http".equals(scheme) && port == 80) || ("https".equals(scheme) && port == 443))) {
                absUriBuilder.append(':').append(port);
            }
        }

        // 3. the actual target path
        absUriBuilder.append(targetPath);
        return absUriBuilder.toString();
    }

    private Servlet getJsonRendererServlet() {
        if (jsonRendererServlet == null) {
            Servlet jrs = new JsonRendererServlet(jsonMaximumResults);
            try {
                jrs.init(getServletConfig());
            } catch (Exception e) {
                // don't care too much here
            }
            jsonRendererServlet = jrs;
        }
        return jsonRendererServlet;
    }

    /**
     * Returns <code>true</code> if the path is potentially an URL. This
     * checks whether the path starts with a scheme followed by a colon
     * according to <a href="http://www.faqs.org/rfcs/rfc2396.html">RFC-2396</a>:
     * <pre>
     *     scheme = alpha *( alpha | digit | "+" | "-" | "." )
     *     alpha  = [ "A" .. "Z", "a" .. "z" ]
     *     digit  = [ "0" .. "9" ]
     * </pre>
     */
    private static boolean isUrl(final String path) {
        for (int i = 0; i < path.length(); i++) {
            char c = path.charAt(i);
            if (c == ':') {
                return true;
            }
            if (!((c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z')
                    || (i > 0
                            && ((c >= '0' && c <= '9')
                                    || c == '.'
                                    || c == '+'
                                    || c == '-')))) {
                break;
            }
        }
        return false;
    }

}
TOP

Related Classes of org.apache.sling.servlets.get.impl.RedirectServlet

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.