You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2017/02/28 08:26:34 UTC

svn commit: r1784700 - in /sling/trunk/bundles/scripting/core: ./ src/main/java/org/apache/sling/scripting/core/impl/ src/main/resources/scriptingvariables/ src/main/resources/scriptingvariables/ui/

Author: kwin
Date: Tue Feb 28 08:26:34 2017
New Revision: 1784700

URL: http://svn.apache.org/viewvc?rev=1784700&view=rev
Log:
SLING-3543 provide dedicated web console for exposing all scripting variables

This closes #135

Added:
    sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java   (with props)
    sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java   (with props)
    sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/
    sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/
    sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/scriptingvariables.js
Modified:
    sling/trunk/bundles/scripting/core/pom.xml
    sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java

Modified: sling/trunk/bundles/scripting/core/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/pom.xml?rev=1784700&r1=1784699&r2=1784700&view=diff
==============================================================================
--- sling/trunk/bundles/scripting/core/pom.xml (original)
+++ sling/trunk/bundles/scripting/core/pom.xml Tue Feb 28 08:26:34 2017
@@ -63,6 +63,8 @@
                         <Embed-Dependency>
                             org.apache.sling.commons.osgi;inline="org/apache/sling/commons/osgi/PropertiesUtil.*"
                         </Embed-Dependency>
+                        <!-- statically link JSONWriter (http://njbartlett.name/2014/05/26/static-linking.html) -->
+                        <Conditional-Package>org.apache.felix.utils.json.*</Conditional-Package>
                     </instructions>
                 </configuration>
             </plugin>
@@ -151,11 +153,17 @@
             <version>3.1.0</version>
             <scope>provided</scope>
         </dependency>
-
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.core</artifactId>
         </dependency>
+        <!-- for serializing to JSON, must be after osgi.core to make Maven use the correct osgi.core version -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>1.9.0</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.webconsole</artifactId>

Modified: sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java?rev=1784700&r1=1784699&r2=1784700&view=diff
==============================================================================
--- sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java (original)
+++ sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/DefaultSlingScript.java Tue Feb 28 08:26:34 2017
@@ -629,7 +629,7 @@ class DefaultSlingScript implements Slin
         };
     }
 
-    private Bindings verifySlingBindings(final SlingBindings slingBindings) throws IOException {
+    Bindings verifySlingBindings(final SlingBindings slingBindings) throws IOException {
 
     	final Bindings bindings = new SimpleBindings();
 

Added: sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java?rev=1784700&view=auto
==============================================================================
--- sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java (added)
+++ sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java Tue Feb 28 08:26:34 2017
@@ -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 <a href="http://felix.apache.org/documentation/subprojects/apache-felix-web-console/extending-the-apache-felix-web-console/providing-web-console-plugins.html></a>
+ */
+@Component
+@Service
+@Properties({
+    @Property(name = WebConsoleConstants.PLUGIN_LABEL, value = ScriptingVariablesConsolePlugin.LABEL),
+    @Property(name = WebConsoleConstants.PLUGIN_TITLE, value = ScriptingVariablesConsolePlugin.TITLE),
+    @Property(name = "felix.webconsole.category", value = "Sling")
+})
+public class ScriptingVariablesConsolePlugin extends AbstractWebConsolePlugin {
+
+    protected static final String LABEL = "scriptingvariables";
+    protected static final String TITLE = "Scripting Variables";
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 261709110347150295L;
+    
+    private static final String JS_RES_PATH = "scriptingvariables/ui/scriptingvariables.js";
+    
+    /**
+     * The script engine manager.
+     */
+    @Reference
+    private ScriptEngineManager scriptEngineManager;
+
+    public ScriptingVariablesConsolePlugin() {
+    }
+
+    /**
+     * Automatically called from 
+     * <a href="https://github.com/apache/felix/blob/4a60744d0f88f351551e4cb4673eb60b8fbd21d3/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java#L510">AbstractWebConsolePlugin#spoolResource</a>
+     * 
+     * @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("<script type='text/javascript' src='").append(JS_RES_PATH).append("'></script>");
+        pw.append("<div id='content'>");
+        pw.append("<table class='content'  cellpadding='0' cellspacing='0' width='100%'>");
+        pw.append("<tr><th colspan='3' class='content container'>Sling Scripting Variables</th></tr>");
+        pw.append("<tr class='content'><td class='content' colspan='3'>Provide a resource path url and script engine (via extension) and then click on 'Retrieve Variables' to expose all script bindings variables which are available for that resource and script engine.</td></tr>"); 
+        pw.append("<tr class='content'>");
+        pw.append("<td class='content'>Resource Url (without selectors and extension)</td> ");
+        pw.append("<td class='content' colspan='2'><input type ='text' name='form.path' placeholder='path' required='required' ");
+        pw.append("class='input ui-state-default ui-corner-all inputText' size='50' pattern='^/{1}.*'></td></tr>");
+        pw.append("<tr class='content'>");
+        pw.append("<td class='content'>Script Engine</td> ");
+        pw.append("<td class='content' colspan='2'><select name='form.extension'>");
+        for (ScriptEngineFactory factory : scriptEngineManager.getEngineFactories()) {
+            for (String extension : factory.getExtensions()) {
+                pw.append("<option value='" + extension + "'>"+extension + " (" + factory.getEngineName() +")</option>");
+            }
+            pw.append("<option value=''>all (unfiltered)</option>");
+        }
+        pw.append("</select> ");
+        pw.append("<button type='button' id='submitButton'> Retrieve Variables </button></td></tr></table>");
+        pw.append("<div id='response'></div>");
+    }
+}

Propchange: sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingVariablesConsolePlugin.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java?rev=1784700&view=auto
==============================================================================
--- sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java (added)
+++ sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java Tue Feb 28 08:26:34 2017
@@ -0,0 +1,162 @@
+/*
+ * 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.scripting.api.BindingsValuesProvider;
+import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
+import org.apache.felix.utils.json.JSONWriter;
+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");
+        JSONWriter jsonWriter = new JSONWriter(response.getWriter());
+        jsonWriter.array();
+        // 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 {
+                writeBindingsToJsonWriter(jsonWriter, selectedScriptEngine.getFactory(), request, response);
+            }
+        } else {
+            for (ScriptEngineFactory engineFactory : scriptEngineManager.getEngineFactories()) {
+                writeBindingsToJsonWriter(jsonWriter, engineFactory, request, response);
+            }
+        }
+        jsonWriter.endArray();
+    }
+
+    private void writeBindingsToJsonWriter(JSONWriter jsonWriter, ScriptEngineFactory engineFactory, SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
+        jsonWriter.object();
+        jsonWriter.key("engine");
+        jsonWriter.value(engineFactory.getEngineName());
+        jsonWriter.key("extensions");
+        jsonWriter.value(engineFactory.getExtensions());
+        Bindings bindings = getBindingsByEngine(engineFactory, request, response);
+        jsonWriter.key("bindings");
+        jsonWriter.array();
+        for (Map.Entry<String, Object> entry : bindings.entrySet()) {
+            jsonWriter.object();
+            jsonWriter.key("name");
+            jsonWriter.value(entry.getKey());
+            jsonWriter.key("class");
+            jsonWriter.value(entry.getValue().getClass().getName());
+            jsonWriter.endObject();
+        }
+        jsonWriter.endArray();
+        jsonWriter.endObject();
+    }
+
+    /**
+     * Gets the {@link Bindings} object for the given {@link ScriptEngineFactory}.
+     * It only considers the default context "request".
+     * 
+     * @see <a href="https://issues.apache.org/jira/browse/SLING-3038">binding contexts(SLING-3083)</a>
+     * 
+     * @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 {
+        String context = SlingScriptAdapterFactory.BINDINGS_CONTEXT; // use default context only
+        final Collection<BindingsValuesProvider> 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;
+    }
+}

Propchange: sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/SlingBindingsVariablesListJsonServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/scriptingvariables.js
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/scriptingvariables.js?rev=1784700&view=auto
==============================================================================
--- sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/scriptingvariables.js (added)
+++ sling/trunk/bundles/scripting/core/src/main/resources/scriptingvariables/ui/scriptingvariables.js Tue Feb 28 08:26:34 2017
@@ -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 += "<br/><div class='ui-widget-header ui-corner-top buttonGroup' style='height: 15px'>"
+				+ "<span style='float: left; margin-left: 1em;'>Variables for engine '" + engineSection.engine +"' (extensions: "+ engineSection.extensions.join() +")</span>"
+				+ "</div>"
+				+ "<table class='nicetable ui-widget'>" + "<tbody>"
+				+ "<thead><tr><th class='ui-widget-header'>Name </th> <th class='ui-widget-header'>Class</th></tr></thead>";
+			
+			for (var variableIndex = 0; variableIndex < engineSection.bindings.length; variableIndex++) {
+				myTable += produceTableRow(engineSection.bindings[variableIndex], variableIndex);
+			}
+			myTable += "</table>";
+		}
+		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 "<tr class='" + (i % 2 === 0 ? "even" : "odd") + " ui-state-default'>"
+				+ "<td>" + variable.name + "</td>"
+				+ "<td><code>" + variable.class + "</code></td>"
+				+ "</tr>";
+	}
+})();
+