/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.console.view; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import com.globant.katari.console.application.ScriptingEngine; /** Handles the execution of code sent by the console's client code. * @author juan.pereyra@globant.com */ public class ScriptExecutionController extends AbstractController { /** Request parameter key for the code to be executed. */ private static final String SCRIPT_CODE_REQ_PARAM = "script"; /** Content type to be used in the response. */ private static final String JSON_CONTENT_TYPE = "application/json"; /** Scripting engine used to execute the code. */ private ScriptingEngine scriptingEngine; /** Builds a new instance of the controller. * @param theScriptingEngine Scripting engine used to execute the code. It * can't be null. */ public ScriptExecutionController(final ScriptingEngine theScriptingEngine) { Validate.notNull(theScriptingEngine, "The the scripting engine cannot be null."); scriptingEngine = theScriptingEngine; } /** {@inheritDoc} */ @Override protected ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response) throws IOException { String code = request.getParameter(SCRIPT_CODE_REQ_PARAM); ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream error = new ByteArrayOutputStream(); scriptingEngine.execute(code, output, error); String result = "{\"output\":\"" + escapeForJson(output.toString()) + "\",\"error\":\"" + escapeForJson(error.toString()) + "\"}"; response.setContentType(JSON_CONTENT_TYPE); response.getWriter().print(result); response.flushBuffer(); return null; } /** Escapes non-JSON characters. * * Inspired by <a * href="http://code.google.com/p/json-simple/">JSON-Simple</a>. * @param input The text to be escaped. It can be null or empty, in * both cases the result will be an empty string. * @return An escaped version of the input string. It can return an empty * string if the input is either null or empty. */ private String escapeForJson(final String input) { if (null == input || StringUtils.isEmpty(input)) { return ""; } StringBuffer output = new StringBuffer(); for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); switch(ch) { case '"': output.append("\\\""); break; case '\\': output.append("\\\\"); break; case '\b': output.append("\\b"); break; case '\f': output.append("\\f"); break; case '\n': output.append("\\n"); break; case '\r': output.append("\\r"); break; case '\t': output.append("\\t"); break; case '/': output.append("\\/"); break; default: // Reference: http://www.unicode.org/versions/Unicode5.1.0/ boolean isUnicodeChar = (ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF'); if (isUnicodeChar) { String ss = Integer.toHexString(ch); output.append("\\u"); for (int k = 0; k < 4 - ss.length(); k++) { output.append('0'); } output.append(ss.toUpperCase()); } else { output.append(ch); } } } return output.toString(); } }