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

com.adobe.acs.commons.wcm.vanity.impl.VanityURLServiceImpl Maven / Gradle / Ivy

There is a newer version: 6.6.0
Show newest version
/*
 * #%L
 * ACS AEM Commons Bundle
 * %%
 * Copyright (C) 2017 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.wcm.vanity.impl;

import com.adobe.acs.commons.wcm.vanity.VanityURLService;
import com.adobe.acs.commons.wcm.vanity.VanityUrlAdjuster;
import com.day.cq.commons.PathInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
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;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(service = VanityURLService.class)
@SuppressWarnings("checkstyle:abbreviationaswordinname")
public class VanityURLServiceImpl implements VanityURLService {

    private static final Logger log = LoggerFactory.getLogger(VanityURLServiceImpl.class);

    private static final String VANITY_DISPATCH_CHECK_ATTR = "acs-aem-commons__vanity-check-loop-detection";
    private static final String DEFAULT_PATH_SCOPE = "/content";

    @Reference(
            cardinality = ReferenceCardinality.OPTIONAL,
            policy = ReferencePolicy.DYNAMIC,
            policyOption = ReferencePolicyOption.GREEDY
    )
    private volatile VanityUrlAdjuster vanityUrlAdjuster;

    public boolean dispatch(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException, RepositoryException {
        if (request.getAttribute(VANITY_DISPATCH_CHECK_ATTR) != null) {
            log.trace("Processing a previously vanity dispatched request. Skipping...");
            return false;
        }

        request.setAttribute(VANITY_DISPATCH_CHECK_ATTR, true);

        final String requestURI = request.getRequestURI();

        // new PathInfo(..) will try to perform a rr.map(..) on requestUri as long as it can be rr.resolved(..)
        final PathInfo pathInfo = new PathInfo(request.getResourceResolver(), requestURI);

        String candidateVanity = pathInfo.getResourcePath();

        // This check mirrors the check in new PathInfo(..); if resolving the requestUri == null
        // then the pathInfo.resourcePath() just returns the requestUri (ie. the 2nd param)
        // TBH, im not sure how resolve(..) can return null, but it has been reported as happening..?
        if (request.getResourceResolver().resolve(requestURI) == null) {
            candidateVanity = request.getResourceResolver().map(request, candidateVanity);
        }
        // else, new PathInfo(..) has already handled the mapping...

        log.trace("Generated Candidate Vanity URL from the mapping of [ {} -> {} ]", requestURI, candidateVanity);

        final String pathScope = StringUtils.removeEnd(requestURI, candidateVanity);

        log.debug("Candidate vanity URL to check and dispatch: [ {} ]", candidateVanity);

        if (vanityUrlAdjuster != null) {
            final String originalCandidateVanity = candidateVanity;
            candidateVanity = vanityUrlAdjuster.adjust(request, candidateVanity);
            log.debug("Custom adjustment of candidate vanity [ {} -> {} ]", originalCandidateVanity, candidateVanity);
        }

        // Check if...
        // 1) the candidateVanity and the requestURI are the same; If they are it means the request has already
        // gone through resource resolution and failed so there is no sense in sending it through again.
        // 2) the candidate is in at least 1 sling:vanityPath under /content
        if (!StringUtils.equals(candidateVanity, requestURI) && isVanityPath(pathScope, candidateVanity, request)) {
            log.debug("Forwarding request to vanity resource [ {} ]", candidateVanity);

            final RequestDispatcher requestDispatcher = request.getRequestDispatcher(candidateVanity);
            requestDispatcher.forward(new ExtensionlessRequestWrapper(request), response);
            return true;
        }

        return false;
    }

    /**
     * Checks if the provided vanity path is a valid redirect
     *
     * @param pathScope  The content path to scope the vanity path too.
     * @param vanityPath Vanity path that needs to be validated.
     * @param request    SlingHttpServletRequest object used for performing query/lookup
     * @return return true if the vanityPath is a registered sling:vanityPath under /content
     */
    protected boolean isVanityPath(String pathScope, String vanityPath, SlingHttpServletRequest request) throws RepositoryException {
        final Resource vanityResource = request.getResourceResolver().resolve(vanityPath);

        if (vanityResource != null) {
            String targetPath = null;

            if (vanityResource.isResourceType("sling:redirect")) {
                targetPath = vanityResource.getValueMap().get("sling:target", String.class);
            } else if (!StringUtils.equals(vanityPath, vanityResource.getPath())) {
                targetPath = vanityResource.getPath();
            }

            if (targetPath != null && StringUtils.startsWith(targetPath, StringUtils.defaultIfEmpty(pathScope, DEFAULT_PATH_SCOPE))) {
                log.debug("Found vanity resource at [ {} ] for sling:vanityPath [ {} ]", targetPath, vanityPath);
                return true;
            }
        }

        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy