blob: 3ce7ef1c4430d8825301008dfcf17525d1b9ccdd [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
~
~ 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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package org.apache.sling.scripting.core.impl.bundled;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.scripting.LazyBindings;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptConstants;
import org.apache.sling.scripting.api.BindingsValuesProvider;
import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
import org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider;
import org.apache.sling.scripting.core.impl.helper.ProtectedBindings;
import org.apache.sling.scripting.core.ScriptHelper;
import org.apache.sling.scripting.spi.bundle.BundledRenderUnit;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(
service = ScriptContextProvider.class
)
public class ScriptContextProvider {
private static final Set<String> PROTECTED_BINDINGS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
SlingBindings.REQUEST,
SlingBindings.RESPONSE,
SlingBindings.READER,
SlingBindings.RESOURCE,
SlingBindings.RESOLVER,
SlingBindings.OUT,
SlingBindings.LOG,
SlingBindings.SLING,
ScriptEngine.FILENAME,
BundledRenderUnit.VARIABLE
)));
@Reference
private BindingsValuesProvidersByContext bvpTracker;
@Reference
private ScriptEngineManager scriptEngineManager;
@Reference
private ScriptingResourceResolverProvider scriptingResourceResolverProvider;
public ExecutableContext prepareScriptContext(SlingHttpServletRequest request, SlingHttpServletResponse response, ExecutableUnit executable)
throws IOException {
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(executable.getScriptEngineName());
if (scriptEngine == null) {
scriptEngine = scriptEngineManager.getEngineByExtension(executable.getScriptExtension());
if (scriptEngine == null) {
throw new IllegalStateException(String.format("Cannot find a script engine with name %s and extension %s for executable %s.",
executable.getScriptEngineName(), executable.getScriptExtension(), executable.getPath()));
}
}
// prepare the SlingBindings
Bindings bindings = new LazyBindings();
bindings.put("properties", request.getResource().getValueMap());
bindings.put(SlingBindings.REQUEST, request);
bindings.put(SlingBindings.RESPONSE, response);
bindings.put(SlingBindings.READER, request.getReader());
bindings.put(SlingBindings.RESOURCE, request.getResource());
bindings.put(SlingBindings.RESOLVER, request.getResource().getResourceResolver());
bindings.put(SlingBindings.OUT, response.getWriter());
Logger scriptLogger = LoggerFactory.getLogger(executable.getName());
bindings.put(SlingBindings.LOG, scriptLogger);
bindings.put(SlingBindings.SLING, new ScriptHelper(executable.getBundleContext(), new SlingScriptAdapter(request.getResourceResolver(),
executable.getPath(), "sling/bundle/resource"), request, response));
bindings.put(BundledRenderUnit.VARIABLE, executable);
bindings.put(ScriptEngine.FILENAME, executable.getPath());
bindings.put(ScriptEngine.FILENAME.replaceAll("\\.", "_"), executable.getPath());
ProtectedBindings protectedBindings = new ProtectedBindings(bindings, PROTECTED_BINDINGS);
for (BindingsValuesProvider bindingsValuesProvider : bvpTracker.getBindingsValuesProviders(scriptEngine.getFactory(),
BindingsValuesProvider.DEFAULT_CONTEXT)) {
bindingsValuesProvider.addBindings(protectedBindings);
}
ScriptContext scriptContext = new BundledScriptContext();
Map<String, LazyBindings.Supplier> slingBindingsSuppliers = new HashMap<>();
slingBindingsSuppliers.put(SlingScriptConstants.ATTR_SCRIPT_RESOURCE_RESOLVER,
() -> scriptingResourceResolverProvider.getRequestScopedResourceResolver());
LazyBindings slingScopeBindings = new LazyBindings(slingBindingsSuppliers);
scriptContext.setBindings(slingScopeBindings, SlingScriptConstants.SLING_SCOPE);
scriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
scriptContext.setWriter(response.getWriter());
scriptContext.setErrorWriter(new LogWriter(scriptLogger));
scriptContext.setReader(request.getReader());
return new ExecutableContext(scriptContext, executable, scriptEngine);
}
static class ExecutableContext {
private final ScriptContext scriptContext;
private final ExecutableUnit executable;
private final ScriptEngine scriptEngine;
private ExecutableContext(ScriptContext scriptContext, ExecutableUnit executable, ScriptEngine scriptEngine) {
this.scriptContext = scriptContext;
this.executable = executable;
this.scriptEngine = scriptEngine;
}
void eval() throws ScriptException {
executable.eval(scriptEngine, scriptContext);
}
void clean() {
Bindings engineBindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
if (engineBindings != null && engineBindings.containsKey(SlingBindings.SLING)) {
Object scriptHelper = engineBindings.get(SlingBindings.SLING);
if (scriptHelper instanceof ScriptHelper) {
((ScriptHelper) scriptHelper).cleanup();
}
}
executable.releaseDependencies();
}
}
}