com.adobe.acs.commons.util.impl.DelegatingServletFactoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.adobe.acs.commons.util.impl.DelegatingServletFactoryImpl.java

Source

/*
 * #%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<String> 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<String> getDelegationHistory(final SlingHttpServletRequest request) {
        Set<String> history = new HashSet<String>();
        final Object tmp = request.getAttribute(REQUEST_ATTR_DELEGATION_HISTORY);
        if (history.getClass().isInstance(tmp)) {
            return (Set<String>) 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<String> history = this.getDelegationHistory(request);
        history.add(targetResourceType);
        request.setAttribute(REQUEST_ATTR_DELEGATION_HISTORY, history);
    }

    @Activate
    protected void activate(final Map<String, String> 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);
    }
}