blob: 248df4a5aa5e9377fa15c4ab52fc47273f0fa873 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}
}