/* * 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.console.internal; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; 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.commons.fileupload.FileItem; import org.apache.commons.io.IOUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; 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.DefaultVariableResolver; import org.apache.felix.webconsole.SimpleWebConsolePlugin; import org.apache.felix.webconsole.WebConsoleUtil; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.api.scripting.SlingScript; import org.apache.sling.commons.json.JSONException; import org.apache.sling.commons.json.io.JSONWriter; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component @Service @Property(name = "felix.webconsole.label", value = ScriptConsolePlugin.NAME) public class ScriptConsolePlugin extends SimpleWebConsolePlugin { private Logger log = LoggerFactory.getLogger(getClass()); public static final String NAME = "scriptconsole"; private static final String TITLE = "%script.title"; private static final String[] CSS = {"/res/ui/codemirror/lib/codemirror.css","/res/ui/script-console.css"}; private final String TEMPLATE; private BundleContext bundleContext; @Reference private ScriptEngineManager scriptEngineManager; public ScriptConsolePlugin() { super(NAME, TITLE, processFileNames(CSS)); TEMPLATE = readTemplateFile("/templates/script-console.html"); } @SuppressWarnings("unchecked") @Override protected void renderContent(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final PrintWriter pw = response.getWriter(); DefaultVariableResolver varResolver = (DefaultVariableResolver) WebConsoleUtil.getVariableResolver(request); varResolver.put("__scriptConfig__",getScriptConfig()); pw.println(TEMPLATE); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final String contentType = getContentType(req); resp.setContentType(contentType); if (contentType.startsWith("text/")) { resp.setCharacterEncoding("UTF-8"); } final String script = getCodeValue(req); final SlingBindings bindings = new SlingBindings(); final PrintWriter pw = resp.getWriter(); //Populate bindings bindings.put(SlingBindings.REQUEST, req); bindings.put(SlingBindings.READER, new StringReader(script)); bindings.put(SlingBindings.RESPONSE, resp); bindings.put(SlingBindings.OUT, pw); //Also expose the bundleContext to simplify scripts interaction with the //enclosing OSGi container bindings.put("bundleContext", bundleContext); final String lang = WebConsoleUtil.getParameter(req, "lang"); final Resource resource = new RuntimeScriptResource(lang, script); final boolean webClient = "webconsole".equals(WebConsoleUtil.getParameter(req,"client")); SlingScript slingScript = resource.adaptTo(SlingScript.class); try { log.debug("Executing script {}",script); slingScript.eval(bindings); } catch (Throwable t){ if(!webClient){ resp.setStatus(500); } pw.println(exceptionToString(t)); log.warn("Error in executing script",t); } } private String getCodeValue(HttpServletRequest req) throws IOException { String script = WebConsoleUtil.getParameter(req, "code"); if(script == null){ script = getContentFromFilePart(req, "code"); } if(script == null){ throw new IllegalArgumentException("'code' parameter not passed"); } return script; } private String getContentType(HttpServletRequest req) { String passedContentType = WebConsoleUtil.getParameter(req,"responseContentType"); if(passedContentType != null){ return passedContentType; } return req.getPathInfo().endsWith(".json") ? "application/json" : "text/plain"; } private String exceptionToString(Throwable t) { StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); return sw.toString(); } private static String[] processFileNames(String[] cssFiles) { String[] css = new String[cssFiles.length]; for(int i = 0; i < cssFiles.length; i++){ css[i] = '/' + NAME + CSS[i]; } return css; } private String getScriptConfig() { try { return getScriptConfig0(); } catch (JSONException e) { throw new RuntimeException(e); } } private String getScriptConfig0() throws JSONException { StringWriter sw = new StringWriter(); JSONWriter jw = new JSONWriter(sw); jw.setTidy(true); jw.array(); for(ScriptEngineFactory sef : scriptEngineManager.getEngineFactories()){ jw.object(); if(sef.getExtensions().isEmpty()){ continue; } jw.key("langName").value(sef.getLanguageName()); jw.key("langCode").value(sef.getExtensions().get(0)); //Language mode as per CodeMirror names String mode = determineMode(sef.getExtensions()); if(mode != null){ jw.key("mode").value(mode); } jw.endObject(); } jw.endArray(); return sw.toString(); } private String determineMode(List<String> extensions) { if(extensions.contains("groovy")){ return "groovy"; }else if (extensions.contains("esp")){ return "javascript"; } return null; } private String getContentFromFilePart(HttpServletRequest req, String paramName) throws IOException { String value = WebConsoleUtil.getParameter(req, paramName); if(value != null){ return value; } final Map params = (Map) req.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD ); if ( params == null ){ return null; } FileItem[] codeFile = getFileItems(params,paramName); if(codeFile.length == 0){ return null; } InputStream is = null; try{ is = codeFile[0].getInputStream(); StringWriter sw = new StringWriter(); IOUtils.copy(is,sw,"utf-8"); return sw.toString(); }finally{ IOUtils.closeQuietly(is); } } private FileItem[] getFileItems(Map params, String name) { final List files = new ArrayList(); FileItem[] items = (FileItem[]) params.get(name); if (items != null) { for (int i = 0; i < items.length; i++) { if (!items[i].isFormField() && items[i].getSize() > 0) { files.add(items[i]); } } } return (FileItem[]) files.toArray(new FileItem[files.size()]); } @Activate public void activate(BundleContext bundleContext) { super.activate(bundleContext); this.bundleContext = bundleContext; } @Deactivate public void deactivate() { super.deactivate(); } }