SlingObjectOverlayInjector.java
/*
* #%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 java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
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.api.scripting.SlingScriptHelper;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
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.AbstractInjectAnnotationProcessor2;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor2;
import org.apache.sling.models.spi.injectorspecific.StaticInjectAnnotationProcessorFactory;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import io.wcm.sling.commons.request.RequestContext;
/**
* Injects common Sling objects that can be derived from either a SlingHttpServletRequest, a ResourceResolver or a
* Resource.
* Documentation see {@link SlingObject}.
* <p>
* This is an overlay of the SlingObject injector provided by the Sling Models implementation itself. It adds support to
* always get the sling request and all objects that can be derived from it whether the adaptable is a request or not -
* using a thread local (see also SLING-4083).
* </p>
* <p>
* With this overlay it is possible to always get these context objects if the adaption is done in context of a
* request-bound thread: resource resolver, current resource, request, response, sling script helper.
* </p>
*/
@Component(service = { Injector.class, StaticInjectAnnotationProcessorFactory.class }, property = {
// use ranking MAX_VALUE - 10 to overlay the sling-object injector of sling which is registered to MAX_VALUE
Constants.SERVICE_RANKING + ":Integer=" + (Integer.MAX_VALUE - 10)
})
public final class SlingObjectOverlayInjector implements Injector, StaticInjectAnnotationProcessorFactory, AcceptsNullName {
/**
* Injector name
*/
public static final @NotNull String NAME = "sling-object";
@Reference
private RequestContext requestContext;
@Reference
private ModelsImplConfiguration modelsImplConfiguration;
@Override
public @NotNull String getName() {
return NAME;
}
@Override
@SuppressWarnings("java:S3776") // complexity
public Object getValue(@NotNull final Object adaptable, final String name, @NotNull final Type type,
@NotNull final AnnotatedElement element, @NotNull final DisposalCallbackRegistry callbackRegistry) {
// let the original sling object injector step in if the thread-local request feature is deactivated
if (!modelsImplConfiguration.isRequestThreadLocal()) {
return null;
}
// only class types are supported
if (!(type instanceof Class<?>)) {
return null;
}
Class<?> requestedClass = (Class<?>)type;
// validate input
if (adaptable instanceof ResourceResolver) {
ResourceResolver resourceResolver = (ResourceResolver)adaptable;
if (requestedClass.equals(ResourceResolver.class)) {
return resourceResolver;
}
}
else if (adaptable instanceof Resource) {
Resource resource = (Resource)adaptable;
if (requestedClass.equals(ResourceResolver.class)) {
return resource.getResourceResolver();
}
if (requestedClass.equals(Resource.class) && element.isAnnotationPresent(SlingObject.class)) {
return resource;
}
}
SlingHttpServletRequest request = getRequest(adaptable);
if (request != null) {
if (requestedClass.equals(ResourceResolver.class)) {
return request.getResourceResolver();
}
if (requestedClass.equals(Resource.class) && element.isAnnotationPresent(SlingObject.class)) {
return request.getResource();
}
if (requestedClass.equals(SlingHttpServletRequest.class) || requestedClass.equals(HttpServletRequest.class)) {
return request;
}
if (requestedClass.equals(SlingHttpServletResponse.class) || requestedClass.equals(HttpServletResponse.class)) {
return getSlingHttpServletResponse(request);
}
if (requestedClass.equals(SlingScriptHelper.class)) {
return getSlingScriptHelper(request);
}
}
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 SlingScriptHelper getSlingScriptHelper(final SlingHttpServletRequest request) {
SlingBindings bindings = (SlingBindings)request.getAttribute(SlingBindings.class.getName());
if (bindings != null) {
return bindings.getSling();
}
return null;
}
private SlingHttpServletResponse getSlingHttpServletResponse(final SlingHttpServletRequest request) {
SlingScriptHelper scriptHelper = getSlingScriptHelper(request);
if (scriptHelper != null) {
return scriptHelper.getResponse();
}
return null;
}
@SuppressWarnings({ "null", "unused" })
@Override
public InjectAnnotationProcessor2 createAnnotationProcessor(final AnnotatedElement element) {
// check if the element has the expected annotation
SlingObject annotation = element.getAnnotation(SlingObject.class);
if (annotation != null) {
return new SlingObjectAnnotationProcessor(annotation);
}
return null;
}
private static class SlingObjectAnnotationProcessor extends AbstractInjectAnnotationProcessor2 {
private final SlingObject annotation;
SlingObjectAnnotationProcessor(final SlingObject annotation) {
this.annotation = annotation;
}
@Override
public InjectionStrategy getInjectionStrategy() {
return annotation.injectionStrategy();
}
@Override
@SuppressWarnings("deprecation")
public Boolean isOptional() {
return annotation.optional();
}
}
}