| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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. |
| ******************************************************************************/ |
| package org.apache.sling.scripting.sightly.models.impl; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.regex.Pattern; |
| import javax.script.Bindings; |
| import javax.servlet.ServletRequest; |
| |
| import org.apache.sling.api.SlingHttpServletRequest; |
| import org.apache.sling.api.resource.Resource; |
| import org.apache.sling.api.scripting.SlingBindings; |
| import org.apache.sling.commons.classloader.DynamicClassLoaderManager; |
| import org.apache.sling.models.factory.ModelFactory; |
| import org.apache.sling.scripting.sightly.render.RenderContext; |
| import org.apache.sling.scripting.sightly.use.ProviderOutcome; |
| import org.apache.sling.scripting.sightly.use.UseProvider; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.metatype.annotations.AttributeDefinition; |
| import org.osgi.service.metatype.annotations.Designate; |
| import org.osgi.service.metatype.annotations.ObjectClassDefinition; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * <p> |
| * HTL {@link UseProvider} which will instantiate a referenced Sling Model. |
| * </p> |
| * <p> |
| * For that it tries to use the {@link ModelFactory#createModel(Object, Class)} first with the adaptable {@link SlingHttpServletRequest} |
| * then with the adaptable {@link Resource}. It will always fail with an exception (i.e. no other {@code UseProvider} is |
| * asked afterwards and the exception is being rethrown) in case the following two preconditions are fulfilled: |
| * </p> |
| * <ol> |
| * <li>the given identifier specifies a class which can be loaded by a {@link org.apache.sling.commons.classloader.DynamicClassLoader}</li> |
| * <li>the loaded class has a {@link org.apache.sling.models.annotations.Model} annotation</li> |
| * </ol> |
| * |
| * <p> |
| * In case any of those preconditions are not fulfilled the other registered {@link UseProvider}s will be queried. |
| * </p> |
| * |
| * @deprecated This service's functionality has been fully integrated into the |
| * {@link org.apache.sling.scripting.sightly.impl.engine.extension.use.JavaUseProvider} starting from version 1.4.0-1.4.0 of the {@code |
| * org.apache.sling.scripting.sightly} bundle. |
| */ |
| @Component( |
| service = UseProvider.class, |
| configurationPid = "org.apache.sling.scripting.sightly.models.impl.SlingModelsUseProvider", |
| property = { |
| /** |
| * Must have a higher priority than {@link org.apache.sling.scripting.sightly.impl.engine.extension.use.JavaUseProvider} but lower |
| * than {@link org.apache.sling.scripting.sightly.impl.engine.extension.use.RenderUnitProvider} to kick in before the |
| * JavaUseProvider but after the RenderUnitProvider. |
| */ |
| Constants.SERVICE_RANKING + ":Integer=95" |
| } |
| ) |
| @Deprecated |
| public class SlingModelsUseProvider implements UseProvider { |
| |
| @interface Configuration { |
| |
| @AttributeDefinition( |
| name = "Service Ranking", |
| description = "The Service Ranking value acts as the priority with which this Use Provider is queried to return an " + |
| "Use-object. A higher value represents a higher priority." |
| ) |
| int service_ranking() default 95; |
| } |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(SlingModelsUseProvider.class); |
| private static final Pattern JAVA_PATTERN = Pattern.compile( |
| "([[\\p{L}&&[^\\p{Lu}]]_$][\\p{L}\\p{N}_$]*\\.)*[\\p{Lu}_$][\\p{L}\\p{N}_$]*"); |
| |
| |
| @Reference |
| private ModelFactory modelFactory = null; |
| |
| @Reference |
| private DynamicClassLoaderManager dynamicClassLoaderManager = null; |
| |
| @Override |
| public ProviderOutcome provide(final String identifier, final RenderContext renderContext, final Bindings arguments) { |
| if (!JAVA_PATTERN.matcher(identifier).matches()) { |
| LOGGER.debug("Identifier {} does not match a Java class name pattern.", identifier); |
| return ProviderOutcome.failure(); |
| } |
| final Class<?> cls; |
| try { |
| cls = dynamicClassLoaderManager.getDynamicClassLoader().loadClass(identifier); |
| } catch (ClassNotFoundException e) { |
| LOGGER.debug("Could not find class with the given name {}.", identifier); |
| // next use provider will be queried |
| return ProviderOutcome.failure(); |
| } |
| if (!modelFactory.isModelClass(cls)) { |
| LOGGER.debug("{} is not a Sling Model."); |
| // next use provider will be queried |
| return ProviderOutcome.failure(); |
| } |
| Bindings globalBindings = renderContext.getBindings(); |
| SlingHttpServletRequest request = (SlingHttpServletRequest) globalBindings.get(SlingBindings.REQUEST); |
| if (request == null) { |
| return ProviderOutcome.failure(new IllegalStateException("Could not get request from bindings.")); |
| } |
| // pass parameters as request attributes |
| Map<String, Object> overrides = setRequestAttributes(request, arguments); |
| |
| try { |
| // try to instantiate class via Sling Models (first via request, then via resource) |
| if (modelFactory.canCreateFromAdaptable(request, cls)) { |
| LOGGER.debug("Trying to instantiate class {} as Sling Model from request.", cls); |
| return ProviderOutcome.notNullOrFailure(modelFactory.createModel(request, cls)); |
| } |
| Resource resource = (Resource) globalBindings.get(SlingBindings.RESOURCE); |
| if (resource == null) { |
| LOGGER.debug("Could not get resource from bindings."); |
| return ProviderOutcome.failure(new IllegalStateException("Could not get resource from bindings.")); |
| } |
| if (modelFactory.canCreateFromAdaptable(resource, cls)) { |
| LOGGER.debug("Trying to instantiate class {} as Sling Model from resource.", cls); |
| return ProviderOutcome.notNullOrFailure(modelFactory.createModel(resource, cls)); |
| } |
| return ProviderOutcome.failure( |
| new IllegalStateException("Could not adapt the given Sling Model from neither request nor resource: " + cls)); |
| } catch (Throwable e) { |
| return ProviderOutcome.failure(e); |
| } finally { |
| resetRequestAttribute(request, overrides); |
| |
| } |
| } |
| |
| private Map<String, Object> setRequestAttributes(final ServletRequest request, final Bindings arguments) { |
| Map<String, Object> overrides = new HashMap<>(); |
| for (Map.Entry<String, Object> entry : arguments.entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| Object oldValue = request.getAttribute(key); |
| if (oldValue != null) { |
| overrides.put(key, oldValue); |
| } else { |
| overrides.put(key, null); |
| } |
| request.setAttribute(key, value); |
| } |
| return overrides; |
| } |
| |
| private void resetRequestAttribute(final ServletRequest request, final Map<String, Object> overrides) { |
| for (Map.Entry<String, Object> entry : overrides.entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| if (value == null) { |
| request.removeAttribute(key); |
| } else { |
| request.setAttribute(key, value); |
| } |
| } |
| } |
| } |