This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.scripting.sightly.js.provider-1.0.10 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-js-provider.git
commit bc26af185d4db9206be862fd6728c0f09f3dea3f Author: Radu Cotescu <[email protected]> AuthorDate: Mon Aug 24 22:57:39 2015 +0000 SLING-4964 - Deprecate the asynchronous JavaScript API provided by the Sightly JS Use Provider * proxy objects are used to load the async namespaces on first usage for a Use script * created a new JS performance test for purely synchronous code git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/scripting/sightly/js-use-provider@1697513 13f79535-47bb-0310-9956-ffa450edef68 --- .../scripting/sightly/js/impl/JsEnvironment.java | 75 +----------------- .../scripting/sightly/js/impl/JsUseProvider.java | 32 ++++---- .../sling/scripting/sightly/js/impl/Utils.java | 51 ++++++++++++ .../js/impl/jsapi/ProxyAsyncScriptableFactory.java | 92 ++++++++++++++++++++++ .../js/impl/jsapi/SlyBindingsValuesProvider.java | 34 +++++--- .../sightly/js/impl/use/DependencyResolver.java | 3 +- 6 files changed, 185 insertions(+), 102 deletions(-) diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsEnvironment.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsEnvironment.java index 3d95960..6175ce6 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsEnvironment.java +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsEnvironment.java @@ -30,14 +30,8 @@ import javax.script.SimpleBindings; import javax.script.SimpleScriptContext; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.apache.sling.api.resource.Resource; -import org.apache.sling.api.resource.ResourceResolver; -import org.apache.sling.api.resource.ResourceUtil; -import org.apache.sling.api.scripting.SlingBindings; -import org.apache.sling.api.scripting.SlingScriptHelper; import org.apache.sling.scripting.core.ScriptNameAwareReader; -import org.apache.sling.scripting.sightly.ResourceResolution; import org.apache.sling.scripting.sightly.SightlyException; import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer; import org.apache.sling.scripting.sightly.js.impl.async.TimingBindingsValuesProvider; @@ -80,31 +74,6 @@ public class JsEnvironment { Context.exit(); } - /** - * Run a Js script at a given path - * @param caller the resource of the script that invokes the Js code - * @param path the path to the JS script - * @param globalBindings the global bindings for the script - * @param arguments the arguments from the use-plugin - * @param callback callback that will receive the result of the script - */ - public void run(Resource caller, String path, Bindings globalBindings, Bindings arguments, UnaryCallback callback) { - Resource scriptResource = caller.getChild(path); - SlingScriptHelper scriptHelper = (SlingScriptHelper) globalBindings.get(SlingBindings.SLING); - Resource componentCaller = ResourceResolution.getResourceForRequest(caller.getResourceResolver(), scriptHelper.getRequest()); - if (scriptResource == null) { - if (isResourceOverlay(caller, componentCaller)) { - scriptResource = ResourceResolution.getResourceFromSearchPath(componentCaller, path); - } else { - scriptResource = ResourceResolution.getResourceFromSearchPath(caller, path); - } - } - if (scriptResource == null) { - throw new SightlyException("Required script resource could not be located: " + path); - } - runResource(scriptResource, globalBindings, arguments, callback); - } - public void runResource(Resource scriptResource, Bindings globalBindings, Bindings arguments, UnaryCallback callback) { ScriptContext scriptContext = new SimpleScriptContext(); CommonJsModule module = new CommonJsModule(); @@ -120,22 +89,6 @@ public class JsEnvironment { return asyncContainer; } - /** - * Run a script at a given path - * - * @param caller the resource of the script that invokes the Js code - * @param path the path to the JS script - * @param globalBindings bindings for the JS script - * @param arguments the arguments for the JS script - * @return an asynchronous container for the result - * @throws UnsupportedOperationException if this method is run when the event loop is not empty - */ - public AsyncContainer run(Resource caller, String path, Bindings globalBindings, Bindings arguments) { - AsyncContainer asyncContainer = new AsyncContainer(); - run(caller, path, globalBindings, arguments, asyncContainer.createCompletionCallback()); - return asyncContainer; - } - private Bindings buildBindings(Resource scriptResource, Bindings local, Bindings arguments, CommonJsModule commonJsModule) { Bindings bindings = new SimpleBindings(); bindings.putAll(engineBindings); @@ -186,31 +139,5 @@ public class JsEnvironment { }); } - /** - * Using the inheritance chain created with the help of {@code sling:resourceSuperType} this method checks if {@code resourceB} - * inherits from {@code resourceA}. In case {@code resourceA} is a {@code nt:file}, its parent will be used for the inheritance check. - * - * @param resourceA the base resource - * @param resourceB the potentially overlaid resource - * @return {@code true} if {@code resourceB} overlays {@code resourceB}, {@code false} otherwise - */ - private boolean isResourceOverlay(Resource resourceA, Resource resourceB) { - String resourceBSuperType = resourceB.getResourceSuperType(); - if (StringUtils.isNotEmpty(resourceBSuperType)) { - ResourceResolver resolver = resourceA.getResourceResolver(); - String parentResourceType = resourceA.getResourceType(); - if ("nt:file".equals(parentResourceType)) { - parentResourceType = ResourceUtil.getParent(resourceA.getPath()); - } - Resource parentB = resolver.getResource(resourceBSuperType); - while (parentB != null && !"/".equals(parentB.getPath()) && StringUtils.isNotEmpty(resourceBSuperType)) { - if (parentB.getPath().equals(parentResourceType)) { - return true; - } - resourceBSuperType = parentB.getResourceSuperType(); - parentB = resolver.getResource(resourceBSuperType); - } - } - return false; - } + } diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsUseProvider.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsUseProvider.java index 4ce28bc..2e11221 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsUseProvider.java +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/JsUseProvider.java @@ -33,7 +33,7 @@ import org.apache.sling.api.scripting.SlingScriptHelper; import org.apache.sling.scripting.sightly.SightlyException; import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer; import org.apache.sling.scripting.sightly.js.impl.async.AsyncExtractor; -import org.apache.sling.scripting.sightly.js.impl.jsapi.SlyBindingsValuesProvider; +import org.apache.sling.scripting.sightly.js.impl.jsapi.ProxyAsyncScriptableFactory; import org.apache.sling.scripting.sightly.js.impl.rhino.JsValueAdapter; import org.apache.sling.scripting.sightly.render.RenderContext; import org.apache.sling.scripting.sightly.use.ProviderOutcome; @@ -44,21 +44,21 @@ import org.osgi.framework.Constants; * Use provider for JavaScript Use-API objects. */ @Component( - metatype = true, - label = "Apache Sling Scripting Sightly JavaScript Use Provider", - description = "The JavaScript Use Provider is responsible for instantiating JavaScript Use-API objects." + metatype = true, + label = "Apache Sling Scripting Sightly JavaScript Use Provider", + description = "The JavaScript Use Provider is responsible for instantiating JavaScript Use-API objects." ) @Service(UseProvider.class) @Properties({ - @Property( - name = Constants.SERVICE_RANKING, - label = "Service Ranking", - description = "The Service Ranking value acts as the priority with which this Use Provider is queried to return an " + + @Property( + name = Constants.SERVICE_RANKING, + label = "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.", - intValue = 80, - propertyPrivate = false - ) -}) + intValue = 80, + propertyPrivate = false + ) + }) public class JsUseProvider implements UseProvider { private static final String JS_ENGINE_NAME = "javascript"; @@ -68,12 +68,11 @@ public class JsUseProvider implements UseProvider { private ScriptEngineManager scriptEngineManager = null; @Reference - private SlyBindingsValuesProvider slyBindingsValuesProvider = null; + private ProxyAsyncScriptableFactory proxyAsyncScriptableFactory = null; @Override public ProviderOutcome provide(String identifier, RenderContext renderContext, Bindings arguments) { Bindings globalBindings = renderContext.getBindings(); - slyBindingsValuesProvider.processBindings(globalBindings); if (!Utils.isJsScript(identifier)) { return ProviderOutcome.failure(); } @@ -89,7 +88,10 @@ public class JsUseProvider implements UseProvider { String callerPath = scriptHelper.getScript().getScriptResource().getPath(); ResourceResolver adminResolver = renderContext.getScriptResourceResolver(); Resource caller = adminResolver.getResource(callerPath); - AsyncContainer asyncContainer = environment.run(caller, identifier, globalBindings, arguments); + Resource scriptResource = Utils.getScriptResource(caller, identifier, globalBindings); + globalBindings.put(ScriptEngine.FILENAME, scriptResource.getPath()); + proxyAsyncScriptableFactory.registerProxies(globalBindings); + AsyncContainer asyncContainer = environment.runResource(scriptResource, globalBindings, arguments); return ProviderOutcome.success(jsValueAdapter.adapt(asyncContainer)); } finally { if (environment != null) { diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/Utils.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/Utils.java index 3b21ede..3c7a4cf 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/js/impl/Utils.java +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/Utils.java @@ -23,8 +23,14 @@ import javax.script.SimpleBindings; import java.util.Collections; import org.apache.commons.lang.StringUtils; +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.resource.ResourceUtil; import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.api.scripting.SlingScriptHelper; +import org.apache.sling.scripting.sightly.ResourceResolution; +import org.apache.sling.scripting.sightly.SightlyException; /** * Utilities for script evaluation @@ -43,4 +49,49 @@ public class Utils { return JS_EXTENSION.equalsIgnoreCase(extension); } + public static Resource getScriptResource(Resource caller, String path, Bindings bindings) { + Resource scriptResource = caller.getChild(path); + Resource componentCaller = ResourceResolution.getResourceForRequest(caller.getResourceResolver(), (SlingHttpServletRequest) bindings.get + (SlingBindings.REQUEST)); + if (scriptResource == null) { + if (isResourceOverlay(caller, componentCaller)) { + scriptResource = ResourceResolution.getResourceFromSearchPath(componentCaller, path); + } else { + scriptResource = ResourceResolution.getResourceFromSearchPath(caller, path); + } + } + if (scriptResource == null) { + throw new SightlyException("Required script resource could not be located: " + path); + } + return scriptResource; + } + + /** + * Using the inheritance chain created with the help of {@code sling:resourceSuperType} this method checks if {@code resourceB} + * inherits from {@code resourceA}. In case {@code resourceA} is a {@code nt:file}, its parent will be used for the inheritance check. + * + * @param resourceA the base resource + * @param resourceB the potentially overlaid resource + * @return {@code true} if {@code resourceB} overlays {@code resourceB}, {@code false} otherwise + */ + private static boolean isResourceOverlay(Resource resourceA, Resource resourceB) { + String resourceBSuperType = resourceB.getResourceSuperType(); + if (StringUtils.isNotEmpty(resourceBSuperType)) { + ResourceResolver resolver = resourceA.getResourceResolver(); + String parentResourceType = resourceA.getResourceType(); + if ("nt:file".equals(parentResourceType)) { + parentResourceType = ResourceUtil.getParent(resourceA.getPath()); + } + Resource parentB = resolver.getResource(resourceBSuperType); + while (parentB != null && !"/".equals(parentB.getPath()) && StringUtils.isNotEmpty(resourceBSuperType)) { + if (parentB.getPath().equals(parentResourceType)) { + return true; + } + resourceBSuperType = parentB.getResourceSuperType(); + parentB = resolver.getResource(resourceBSuperType); + } + } + return false; + } + } diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/ProxyAsyncScriptableFactory.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/ProxyAsyncScriptableFactory.java new file mode 100644 index 0000000..3a38cac --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/ProxyAsyncScriptableFactory.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.sightly.js.impl.jsapi; + +import java.util.HashSet; +import java.util.Set; +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.SimpleBindings; + +import org.apache.commons.lang.StringUtils; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.scripting.sightly.js.impl.rhino.HybridObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Undefined; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +@Service(ProxyAsyncScriptableFactory.class) +public class ProxyAsyncScriptableFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(ProxyAsyncScriptableFactory.class); + + @Reference + private SlyBindingsValuesProvider slyBindingsValuesProvider = null; + + public void registerProxies(Bindings bindings) { + slyBindingsValuesProvider.initialise(bindings); + Bindings bindingsCopy = new SimpleBindings(); + for (String factoryName : slyBindingsValuesProvider.getScriptPaths().keySet()) { + ShadowScriptableObject shadowScriptableObject = new ShadowScriptableObject(factoryName, bindingsCopy); + bindings.put(factoryName, shadowScriptableObject); + } + bindingsCopy.putAll(bindings); + } + + class ShadowScriptableObject extends ScriptableObject { + + private String clazz; + private Bindings bindings; + private Set<String> scriptNSUse = new HashSet<String>(); + + public ShadowScriptableObject(String clazz, Bindings bindings) { + this.clazz = clazz; + this.bindings = bindings; + } + + @Override + public String getClassName() { + return clazz; + } + + @Override + public Object get(String name, Scriptable start) { + Object object = bindings.get(clazz); + if (!(object instanceof HybridObject)) { + slyBindingsValuesProvider.processBindings(bindings); + } + HybridObject hybridObject = (HybridObject) bindings.get(clazz); + if (hybridObject != null) { + String script = (String) bindings.get(ScriptEngine.FILENAME); + if (StringUtils.isNotEmpty(script)) { + if (scriptNSUse.add(clazz + ":" + script)) { + LOGGER.warn( + "Script {} uses the deprecated asynchronous API provided by the '{}' namespace. Please refactor the script to" + + " use the synchronous API provided by the org.apache.sling.scripting.javascript bundle.", script, clazz); + } + } + return hybridObject.get(name, start); + } + return Undefined.instance; + } + } +} diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/SlyBindingsValuesProvider.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/SlyBindingsValuesProvider.java index 71fc67c..ec9a422 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/SlyBindingsValuesProvider.java +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/jsapi/SlyBindingsValuesProvider.java @@ -21,10 +21,10 @@ package org.apache.sling.scripting.sightly.js.impl.jsapi; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Map; - import javax.script.Bindings; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -45,6 +45,7 @@ import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.scripting.sightly.SightlyException; import org.apache.sling.scripting.sightly.js.impl.JsEnvironment; import org.apache.sling.scripting.sightly.js.impl.Variables; import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer; @@ -92,10 +93,10 @@ public class SlyBindingsValuesProvider { private static final String REQ_NS = SlyBindingsValuesProvider.class.getCanonicalName(); - private static final Logger log = LoggerFactory.getLogger(SlyBindingsValuesProvider.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SlyBindingsValuesProvider.class); @Reference - private ScriptEngineManager scriptEngineManager; + private ScriptEngineManager scriptEngineManager = null; @Reference private ResourceResolverFactory rrf = null; @@ -109,10 +110,16 @@ public class SlyBindingsValuesProvider { private Script qScript; private final ScriptableObject qScope = createQScope(); - public void processBindings(Bindings bindings) { + public void initialise(Bindings bindings) { if (needsInit()) { init(bindings); } + } + + public void processBindings(Bindings bindings) { + if (needsInit()) { + throw new SightlyException("Attempted to call processBindings without calling initialise first."); + } Context context = null; try { context = Context.enter(); @@ -130,6 +137,10 @@ public class SlyBindingsValuesProvider { } } + public Map<String, String> getScriptPaths() { + return Collections.unmodifiableMap(scriptPaths); + } + @Activate protected void activate(ComponentContext componentContext) { Dictionary properties = componentContext.getProperties(); @@ -200,18 +211,17 @@ public class SlyBindingsValuesProvider { resolver = rrf.getAdministrativeResourceResolver(null); Resource resource = resolver.getResource(path); if (resource == null) { - log.warn("Sly namespace loader could not find the following script: " + path); - return null; + throw new SightlyException("Sly namespace loader could not find the following script: " + path); + } AsyncContainer container = jsEnvironment.runResource(resource, createBindings(bindings), new SimpleBindings()); Object obj = container.getResult(); if (!(obj instanceof Function)) { - log.warn("Script was expected to return a function"); - return null; + throw new SightlyException("Script " + path + " was expected to return a function."); } return (Function) obj; } catch (LoginException e) { - log.error("Cannot evaluate script " + path, e); + LOGGER.error("Cannot evaluate script " + path, e); return null; } finally { if (resolver != null) { @@ -280,12 +290,12 @@ public class SlyBindingsValuesProvider { resourceResolver = rrf.getAdministrativeResourceResolver(null); Resource resource = resourceResolver.getResource(Q_PATH); if (resource == null) { - log.warn("Could not load Q library at path: " + Q_PATH); + LOGGER.warn("Could not load Q library at path: " + Q_PATH); return null; } reader = resource.adaptTo(InputStream.class); if (reader == null) { - log.warn("Could not read content of Q library"); + LOGGER.warn("Could not read content of Q library"); return null; } return context.compileReader(new InputStreamReader(reader), Q_PATH, 0, null); @@ -298,7 +308,7 @@ public class SlyBindingsValuesProvider { try { reader.close(); } catch (IOException e) { - log.error("Error while closing reader", e); + LOGGER.error("Error while closing reader", e); } } if (resourceResolver != null) { diff --git a/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java b/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java index ef24105..46b7cfb 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java +++ b/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java @@ -51,7 +51,8 @@ public class DependencyResolver { if (!Utils.isJsScript(dependency)) { throw new SightlyException("Only JS scripts are allowed as dependencies. Invalid dependency: " + dependency); } - jsEnvironment.run(caller, dependency, globalBindings, Utils.EMPTY_BINDINGS, callback); + Resource scriptResource = Utils.getScriptResource(caller, dependency, globalBindings); + jsEnvironment.runResource(scriptResource, globalBindings, Utils.EMPTY_BINDINGS, callback); } } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
