All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.acs.commons.util.impl.DelegatingServletFactoryImpl Maven / Gradle / Ivy

There is a newer version: 6.6.0
Show newest version
/*
 * #%L
 * ACS AEM Commons Bundle
 * %%
 * Copyright (C) 2013 Adobe
 * %%
 * Licensed 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.
 * #L%
 */
package com.adobe.acs.commons.util.impl;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
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.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestDispatcherOptions;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@SuppressWarnings("serial")
@Component(
        label = "ACS AEM Commons - Delegating Servlet",
        description = "Delegating Servlet enabling the unobtrusive delegate of Resource Types.",
        configurationFactory = true,
        policy = ConfigurationPolicy.REQUIRE,
        metatype = true,
        immediate = false)
@Properties({
        @Property(
                label = "Source Resource Types",
                description = "Requests matching the \"Source resource types, selectors, extensions and methods\" will be overlayed using the \"Target Resource Type\"",
                name = "sling.servlet.resourceTypes",
                cardinality = Integer.MAX_VALUE,
                value = {""}),
        @Property(
                label = "Source Selectors",
                description = "Requests matching the \"Source resource types, selectors, extensions and methods\" will be overlayed using the \"Target Resource Type\"",
                name = "sling.servlet.selectors",
                cardinality = Integer.MAX_VALUE,
                value = {""}),
        @Property(
                label = "Source Extensions",
                description = "Requests matching the \"Source resource types, selectors, extensions and methods\" will be overlayed using the \"Target Resource Type\"",
                name = "sling.servlet.extensions",
                cardinality = Integer.MAX_VALUE,
                value = {"html"}),
        @Property(
                label = "Source HTTP Methods",
                description = "Requests matching the \"Source resource types, selectors, extensions and methods\" will be overlayed using the \"Target Resource Type\"",
                name = "sling.servlet.methods",
                cardinality = Integer.MAX_VALUE,
                value = {"GET"}
        ),
        @Property(
                name = "webconsole.configurationFactory.nameHint",
                value = "Target type: {prop.target-resource-type}")
})
@Service(Servlet.class)
public final class DelegatingServletFactoryImpl extends SlingAllMethodsServlet {
    protected static final Logger log = LoggerFactory.getLogger(DelegatingServletFactoryImpl.class);
    private static final String REQUEST_ATTR_DELEGATION_HISTORY = DelegatingServletFactoryImpl.class.getName() + "_History";


    private static final String DEFAULT_TARGET_RESOURCE_TYPE = "";
    private String targetResourceType = DEFAULT_TARGET_RESOURCE_TYPE;
    @Property(label = "Target Resource Type",
            description = "The resource type to proxy requests to.",
            value = DEFAULT_TARGET_RESOURCE_TYPE)
    public static final String PROP_TARGET_RESOURCE_TYPE = "prop.target-resource-type";

    /** Safe HTTP Methods **/

    public void doGeneric(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doHead(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doOptions(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doTrace(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    /** Un-Safe HTTP Methods **/

    public void doDelete(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doPost(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    public void doPut(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        this.delegate(request, response);
    }

    /**
     * Delegates request through to the target resource type
     *
     * @param request
     * @param response
     */
    private void delegate(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException {
        final RequestDispatcherOptions options = new RequestDispatcherOptions();

        if(this.isCyclic(request, targetResourceType)) {
            log.error("Delegation Servlet creating a cycle for Target Resource Type: {}", targetResourceType);
            throw new ServletException("Cyclic delegation detected for " + targetResourceType);
        }

        if(StringUtils.isNotBlank(targetResourceType)) {
            log.debug("Delegating Request resource type with: {}", targetResourceType);
            options.setForceResourceType(targetResourceType);
        } else {
            log.warn("Delegating Servlet's \"Target Resource Type\" is blank or null");
        }

        try {
            this.setDelegationHistory(request, targetResourceType);
            request.getRequestDispatcher(request.getResource(), options).forward(request, response);
        } catch (ServletException e) {
            log.error("Could not properly re-route request to delegate resource type: {}", targetResourceType);
        } catch (IOException e) {
            log.error("Could not properly re-route request to delegate resource type: {}", targetResourceType);
        }
    }


    /**
     * Determines if the Request is or will be cyclic
     *
     * @param request
     * @param targetResourceType
     * @return true is Request will cause a and infinite delegation cycle
     */
    private boolean isCyclic(final SlingHttpServletRequest request, final String targetResourceType) {
        if(StringUtils.isBlank(targetResourceType)) {
            log.debug("Delegating Servlet's \"Target Resource Type\" is blank or null");
            return true;
        }

        final Set history = this.getDelegationHistory(request);
        if(history.contains(targetResourceType)) {
            log.debug("Delegating Servlet's \"Target Resource Type\" is been forwarded to previously");
            return true;
        }

        return false;
    }

    /**
     * Retrieves the delegation history from the Request
     *
     * @param request
     * @return the delegation history set (of resource types previously targeted by this Servlet)
     */
    @SuppressWarnings("unchecked")
    private Set getDelegationHistory(final SlingHttpServletRequest request) {
        Set history = new HashSet();
        final Object tmp = request.getAttribute(REQUEST_ATTR_DELEGATION_HISTORY);
        if(history.getClass().isInstance(tmp)) {
            return (Set)tmp;
        } else {
            return history;
        }
    }

    /**
     * Sets the targetResourceType as part of the delegation history and adds update history set to the Request
     *
     * @param request
     * @param targetResourceType
     */
    private void setDelegationHistory(final SlingHttpServletRequest request, final String targetResourceType) {
        final Set history = this.getDelegationHistory(request);
        history.add(targetResourceType);
        request.setAttribute(REQUEST_ATTR_DELEGATION_HISTORY, history);
    }

    @Activate
    protected void activate(final Map config) {
        targetResourceType = PropertiesUtil.toString(config.get(PROP_TARGET_RESOURCE_TYPE), "");

        if(StringUtils.isBlank(targetResourceType)) {
            throw new IllegalArgumentException("Target Resource Type must NOT be blank");
        }

        log.debug("Target Resource Type: {}", targetResourceType);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy