io.wcm.sling.models.injectors.impl.AemObjectInjector.java Source code

Java tutorial

Introduction

Here is the source code for io.wcm.sling.models.injectors.impl.AemObjectInjector.java

Source

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * 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 io.wcm.sling.models.injectors.impl;

import io.wcm.sling.commons.request.RequestContext;
import io.wcm.sling.models.annotations.AemObject;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.models.spi.AcceptsNullName;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
import org.apache.sling.models.spi.Injector;
import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
import org.osgi.framework.Constants;

import com.adobe.granite.xss.XSSAPI;
import com.day.cq.i18n.I18n;
import com.day.cq.wcm.api.AuthoringUIMode;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.WCMMode;
import com.day.cq.wcm.api.components.ComponentContext;
import com.day.cq.wcm.api.designer.Design;
import com.day.cq.wcm.api.designer.Designer;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.commons.WCMUtils;

/**
 * Injects common AEM objects that can be derived from a SlingHttpServletRequest.
 * Documentation see {@link AemObject}.
 */
@Component
@Service
/*
 * SERVICE_RANKING of this service should be lower than the ranking of the OsgiServiceInjector (5000),
 * otherwise the generic XSSAPI service would be injected from the OSGi Service Registry instead of the
 * pre-configured from the current request.
 */
@Property(name = Constants.SERVICE_RANKING, intValue = 4500)
public final class AemObjectInjector implements Injector, InjectAnnotationProcessorFactory, AcceptsNullName {

    /**
     * Injector name
     */
    public static final String NAME = "wcm-io-aem-object";

    static final String RESOURCE_PAGE = "resourcePage";
    static final String USER_I18N = "userI18n";

    @Reference
    private RequestContext requestContext;
    @Reference
    private ModelsImplConfiguration modelsImplConfiguration;

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public Object getValue(final Object adaptable, final String name, final Type type,
            final AnnotatedElement element, final DisposalCallbackRegistry callbackRegistry) {

        // only class types are supported
        if (!(type instanceof Class<?>)) {
            return null;
        }
        Class<?> requestedClass = (Class<?>) type;

        SlingHttpServletRequest request = getRequest(adaptable);
        if (request != null) {
            if (requestedClass.equals(WCMMode.class)) {
                return getWcmMode(request);
            }
            if (requestedClass.equals(AuthoringUIMode.class)) {
                return getAuthoringUiMode(request);
            }
            if (requestedClass.equals(ComponentContext.class)) {
                return getComponentContext(request);
            }
            if (requestedClass.equals(Style.class)) {
                return getStyle(request);
            }
            if (requestedClass.equals(XSSAPI.class)) {
                return getXssApi(request);
            }
            if (requestedClass.equals(I18n.class)) {
                if (StringUtils.equals(name, USER_I18N)) {
                    return getUserI18n(request);
                } else {
                    return getResourceI18n(request);
                }
            }
        }

        if (requestedClass.equals(PageManager.class)) {
            return getPageManager(adaptable);
        } else if (requestedClass.equals(Page.class)) {
            if (StringUtils.equals(name, RESOURCE_PAGE)) {
                return getResourcePage(adaptable);
            } else {
                return getCurrentPage(adaptable);
            }
        } else if (requestedClass.equals(Designer.class)) {
            return getDesigner(adaptable);
        } else if (requestedClass.equals(Design.class)) {
            return getCurrentDesign(adaptable);
        }

        return null;
    }

    private SlingHttpServletRequest getRequest(final Object adaptable) {
        if (adaptable instanceof SlingHttpServletRequest) {
            return (SlingHttpServletRequest) adaptable;
        } else if (modelsImplConfiguration.isRequestThreadLocal()) {
            return requestContext.getThreadRequest();
        } else {
            return null;
        }
    }

    private ResourceResolver getResourceResolver(final Object adaptable) {
        if (adaptable instanceof ResourceResolver) {
            return (ResourceResolver) adaptable;
        }
        if (adaptable instanceof Resource) {
            return ((Resource) adaptable).getResourceResolver();
        }
        SlingHttpServletRequest request = getRequest(adaptable);
        if (request != null) {
            return request.getResourceResolver();
        }
        return null;
    }

    private Resource getResource(final Object adaptable) {
        if (adaptable instanceof Resource) {
            return (Resource) adaptable;
        }
        SlingHttpServletRequest request = getRequest(adaptable);
        if (request != null) {
            return request.getResource();
        }
        return null;
    }

    private PageManager getPageManager(final Object adaptable) {
        ResourceResolver resolver = getResourceResolver(adaptable);
        if (resolver != null) {
            return resolver.adaptTo(PageManager.class);
        }
        return null;
    }

    private Designer getDesigner(final Object adaptable) {
        ResourceResolver resolver = getResourceResolver(adaptable);
        if (resolver != null) {
            return resolver.adaptTo(Designer.class);
        }
        return null;
    }

    private Page getCurrentPage(final Object adaptable) {
        SlingHttpServletRequest request = getRequest(adaptable);
        if (request != null) {
            ComponentContext context = getComponentContext(request);
            if (context != null) {
                return context.getPage();
            }
        }
        return getResourcePage(adaptable);
    }

    private Page getResourcePage(final Object adaptable) {
        PageManager pageManager = getPageManager(adaptable);
        Resource resource = getResource(adaptable);
        if (pageManager != null && resource != null) {
            return pageManager.getContainingPage(resource);
        }
        return null;
    }

    private WCMMode getWcmMode(final SlingHttpServletRequest request) {
        return WCMMode.fromRequest(request);
    }

    private AuthoringUIMode getAuthoringUiMode(final SlingHttpServletRequest request) {
        AuthoringUIMode mode = AuthoringUIMode.fromRequest(request);
        if (mode == null) {
            // if no mode is set (e.g. if WCMMode is disabled) default to Touch UI
            mode = AuthoringUIMode.TOUCH;
        }
        return mode;
    }

    private ComponentContext getComponentContext(final SlingHttpServletRequest request) {
        return WCMUtils.getComponentContext(request);
    }

    private Design getCurrentDesign(final Object adaptable) {
        Page currentPage = getCurrentPage(adaptable);
        Designer designer = getDesigner(adaptable);
        if (currentPage != null && designer != null) {
            return designer.getDesign(currentPage);
        }
        return null;
    }

    private Style getStyle(final SlingHttpServletRequest request) {
        Design currentDesign = getCurrentDesign(request);
        ComponentContext componentContext = getComponentContext(request);
        if (currentDesign != null && componentContext != null) {
            return currentDesign.getStyle(componentContext.getCell());
        }
        return null;
    }

    private XSSAPI getXssApi(final SlingHttpServletRequest request) {
        return request.adaptTo(XSSAPI.class);
    }

    private I18n getResourceI18n(final SlingHttpServletRequest request) {
        Page currentPage = getCurrentPage(request);
        if (currentPage != null) {
            Locale currentLocale = currentPage.getLanguage(false);
            return new I18n(getI18nEnabledRequest(request).getResourceBundle(currentLocale));
        }
        return null;
    }

    private I18n getUserI18n(final SlingHttpServletRequest request) {
        return new I18n(getI18nEnabledRequest(request));
    }

    /**
     * Returns a sling request that has a resource bundle set. Due to several wrappings inside Sling
     * this is not always the request that is available in the script or java code initiating the injection.
     * If a SlingBindings object is available the request from this is returned.
     * If not it is checked if the current request that was recorded in a ThreadLocal can be used.
     * As a last resort a fallback to the request that was used for the adaption is returned, but this
     * is likely to not be i18n-enabled.
     * @param request Original request
     * @return Request from sling bindings
     */
    private SlingHttpServletRequest getI18nEnabledRequest(SlingHttpServletRequest request) {
        SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
        if (bindings != null) {
            return bindings.getRequest();
        }
        if (modelsImplConfiguration.isRequestThreadLocal()) {
            SlingHttpServletRequest threadLocalRequest = requestContext.getThreadRequest();
            if (threadLocalRequest != null) {
                return threadLocalRequest;
            }
        }
        return request;
    }

    @Override
    public InjectAnnotationProcessor createAnnotationProcessor(final Object adaptable,
            final AnnotatedElement element) {
        // check if the element has the expected annotation
        AemObject annotation = element.getAnnotation(AemObject.class);
        if (annotation != null) {
            return new AemObjectAnnotationProcessor(annotation);
        }
        return null;
    }

    private static class AemObjectAnnotationProcessor extends AbstractInjectAnnotationProcessor {

        private final AemObject annotation;

        public AemObjectAnnotationProcessor(final AemObject annotation) {
            this.annotation = annotation;
        }

        @Override
        public Boolean isOptional() {
            return this.annotation.optional();
        }
    }

}