Index: src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java =================================================================== --- src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java (revision 1784538) +++ src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java (working copy) @@ -629,7 +629,7 @@ }; } - private Bindings verifySlingBindings(final SlingBindings slingBindings) throws IOException { + Bindings verifySlingBindings(final SlingBindings slingBindings) throws IOException { final Bindings bindings = new SimpleBindings(); Index: src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java =================================================================== --- src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java (revision 0) +++ src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java (revision 0) @@ -0,0 +1,117 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * 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. + */ +package org.apache.sling.scripting.core.impl; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URL; + +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Properties; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.felix.webconsole.AbstractWebConsolePlugin; +import org.apache.felix.webconsole.WebConsoleConstants; + +/** + * Web Console Plugin exposing all binding provider values. + * @see AbstractWebConsolePlugin#spoolResource + * + * @param path the requested path + * @return either a URL from which to spool the resource requested through the given path or {@code null} + */ + public URL getResource(String path) { + if (path.endsWith(JS_RES_PATH)) { + return this.getClass().getResource("/" + JS_RES_PATH); + } + return null; + } + + @Override + public String getLabel() { + return LABEL; + } + + @Override + public String getTitle() { + return TITLE; + } + + @Override + protected void renderContent(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + final PrintWriter pw = response.getWriter(); + pw.append(""); + pw.append("
"); + pw.append(""); + pw.append(""); + pw.append(""); + pw.append(""); + pw.append(" "); + pw.append(""); + pw.append(""); + pw.append(" "); + pw.append("
Sling Scripting Variables
Provide a resource path url and script engine (via extension) and then click on 'Retrieve Variables' to expose all script binding variables which are available for that resource and script engine.
Resource Url (without selectors and extension)
Script Engine "); + pw.append("
"); + pw.append("
"); + } +} Index: src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java =================================================================== --- src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java (revision 0) +++ src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java (revision 0) @@ -0,0 +1,168 @@ +/* + * 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; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.servlet.ServletException; + +import org.apache.commons.lang.StringUtils; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.sling.SlingServlet; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.resource.NonExistingResource; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.api.scripting.SlingScriptConstants; +import org.apache.sling.api.servlets.SlingSafeMethodsServlet; +import org.apache.sling.commons.json.JSONArray; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.JSONObject; +import org.apache.sling.scripting.api.BindingsValuesProvider; +import org.apache.sling.scripting.api.BindingsValuesProvidersByContext; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; + +/** + * Return all scripting variables for all registered scripting languages for the default context (=request). + * This can only be achieved when a real Sling request and Sling response is available. + * Also the context (i.e. the resource on which the request is acting) is important, + * because the actual binding variables might differ depending on the context + */ +@SlingServlet( + resourceTypes = "sling/servlet/default", + selectors = "availablebindings", + methods = "GET", + extensions = "json" +) +public class SlingBindingsVariablesListJsonServlet extends SlingSafeMethodsServlet { + + /** + * + */ + private static final long serialVersionUID = -6744726829737263875L; + + /** + * The script engine manager. + */ + @Reference + private ScriptEngineManager scriptEngineManager; + + /** + * The BindingsValuesProviderTracker + */ + @Reference + private BindingsValuesProvidersByContext bindingsValuesProviderTracker; + + private BundleContext bundleContext; + + private static final String PARAMETER_EXTENSION = "extension"; + + @Activate + protected void activate(ComponentContext context) { + bundleContext = context.getBundleContext(); + } + + @Override + protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) + throws ServletException, IOException { + response.setContentType("application/json"); + JSONArray jsonArray = new JSONArray(); + // get filter by engine selector + String requestedExtension = request.getParameter(PARAMETER_EXTENSION); + if (StringUtils.isNotBlank(requestedExtension)) { + ScriptEngine selectedScriptEngine = scriptEngineManager.getEngineByExtension(requestedExtension); + if (selectedScriptEngine == null) { + throw new IllegalArgumentException("Invalid extension requested: "+requestedExtension); + } else { + try { + JSONObject jsonObject = getBindingsJsonByEngineFactory(selectedScriptEngine.getFactory(), request, response); + jsonArray.put(jsonObject); + } catch (JSONException e) { + throw new IOException("Can not output json", e); + } + } + } else { + for (ScriptEngineFactory engineFactory : scriptEngineManager.getEngineFactories()) { + try { + JSONObject jsonObject = getBindingsJsonByEngineFactory(engineFactory, request, response); + jsonArray.put(jsonObject); + } catch (JSONException e) { + throw new IOException("Can not output json", e); + } + } + } + response.getWriter().print(jsonArray.toString()); + } + + private JSONObject getBindingsJsonByEngineFactory(ScriptEngineFactory engineFactory, SlingHttpServletRequest request, SlingHttpServletResponse response) throws JSONException, IOException { + Bindings bindings = getBindingsByEngine(engineFactory, request, response); + JSONArray bindingsJson = new JSONArray(); + for (Map.Entry entry : bindings.entrySet()) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("name", entry.getKey()); + jsonObject.put("class", entry.getValue().getClass().getName()); + bindingsJson.put(jsonObject); + } + JSONObject jsonObject = new JSONObject(); + jsonObject.put("engine", engineFactory.getEngineName()); + jsonObject.put("extensions", engineFactory.getExtensions()); + jsonObject.put("bindings", bindingsJson); + return jsonObject; + } + + /** + * Gets the {@link Bindings} object for the given {@link ScriptEngineFactory}. + * It only considers the default context "request". + * + * @see binding contexts(SLING-3083) + * + * @param scriptEngineFactory the factory of the script engine, for which to retrieve the bindings + * @param request the current request (necessary to create the bindings) + * @param response the current response (necessary to create the bindings) + * @return the bindings (list of key/value pairs) as defined by {@link Bindings} for the given script engine. + * @throws IOException + * @throws JSONException + */ + private Bindings getBindingsByEngine(ScriptEngineFactory scriptEngineFactory, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, JSONException { + String context = SlingScriptAdapterFactory.BINDINGS_CONTEXT; // use default context only + final Collection bindingsValuesProviders = + bindingsValuesProviderTracker.getBindingsValuesProviders(scriptEngineFactory, context); + + Resource invalidScriptResource = new NonExistingResource(request.getResourceResolver(), "some/invalid/scriptpath"); + DefaultSlingScript defaultSlingScript = new DefaultSlingScript(bundleContext, invalidScriptResource, scriptEngineFactory.getScriptEngine(), bindingsValuesProviders, null, null); + + // prepare the bindings (similar as in DefaultSlingScript#service) + final SlingBindings initalBindings = new SlingBindings(); + initalBindings.setRequest((SlingHttpServletRequest) request); + initalBindings.setResponse((SlingHttpServletResponse) response); + final Bindings bindings = defaultSlingScript.verifySlingBindings(initalBindings); + + // only thing being added in {DefaultSlingScript#call(...)} is resource resolver + bindings.put(SlingScriptConstants.ATTR_SCRIPT_RESOURCE_RESOLVER, request.getResourceResolver()); + + return bindings; + } +} Index: src/main/resources/scriptingvariables/ui/scriptingvariables.js =================================================================== --- src/main/resources/scriptingvariables/ui/scriptingvariables.js (revision 0) +++ src/main/resources/scriptingvariables/ui/scriptingvariables.js (revision 0) @@ -0,0 +1,74 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * 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. + */ +(function () { + $(document).ready(function () { + $('#submitButton').on('click', function (e) { + e.preventDefault(); + lookupVariables($("input[name='form.path']").val(), $("select[name='form.extension']").val()); + }); + }); + + function renderContent(variables) { + var myTable = ""; + for (var engineIndex = 0; engineIndex < variables.length; engineIndex++) { + engineSection = variables[engineIndex]; + myTable += "
" + + "Variables for engine '" + engineSection.engine +"' (extensions: "+ engineSection.extensions.join() +")" + + "
" + + "" + "" + + ""; + + for (var variableIndex = 0; variableIndex < engineSection.bindings.length; variableIndex++) { + myTable += produceTableRow(engineSection.bindings[variableIndex], variableIndex); + } + myTable += "
Name Class
"; + } + return myTable; + } + + function lookupVariables(path, extension) { + if (/^\//.test(path)) { + $.ajax(appendSelectorToPath(path) + "?extension="+extension, + { + type: 'GET' + } + ).success( + function (data) { + $('#response').html(renderContent(data)); + } + ).fail( + function () { + $('#response').html('No scripting context available under provided path.'); + } + ); + } else { + $('#response').html('Invalid path given.'); + } + } + + function appendSelectorToPath(path) { + return path + ".availablebindings.json"; + } + + + function produceTableRow(variable, i) { + return "" + + "" + variable.name + "" + + "" + variable.class + "" + + ""; + } +})(); + Index: src/test/java/org/apache/sling/scripting/core/impl/BindingsValuesProvidersByContextIT.java =================================================================== --- src/test/java/org/apache/sling/scripting/core/impl/BindingsValuesProvidersByContextIT.java (revision 1784515) +++ src/test/java/org/apache/sling/scripting/core/impl/BindingsValuesProvidersByContextIT.java (working copy) @@ -103,7 +103,10 @@ mavenBundle().groupId(SLING_GID).artifactId("org.apache.sling.api").versionAsInProject(), mavenBundle().groupId(SLING_GID).artifactId("org.apache.sling.commons.mime").versionAsInProject(), mavenBundle().groupId(SLING_GID).artifactId("org.apache.sling.commons.osgi").versionAsInProject(), - + mavenBundle().groupId(SLING_GID).artifactId("org.apache.sling.commons.json").versionAsInProject(), + // transitive dependency of commons.json + mavenBundle().groupId(SLING_GID).artifactId("org.apache.sling.jcr.jcr-wrapper").version("2.0.0"), + mavenBundle().groupId("javax.servlet").artifactId("javax.servlet-api").versionAsInProject(), mavenBundle().groupId("commons-io").artifactId("commons-io").versionAsInProject(), mavenBundle().groupId("commons-lang").artifactId("commons-lang").versionAsInProject(),