Java tutorial
/* * Copyright (c) Mirth Corporation. All rights reserved. * * http://www.mirthcorp.com * * The software in this package is published under the terms of the MPL license a copy of which has * been included with this distribution in the LICENSE.txt file. */ package com.mirth.connect.server.util.javascript; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.log4j.Logger; import org.mozilla.javascript.Context; import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Undefined; import com.mirth.connect.donkey.model.message.ConnectorMessage; import com.mirth.connect.donkey.model.message.Message; import com.mirth.connect.donkey.model.message.RawMessage; import com.mirth.connect.donkey.model.message.Response; import com.mirth.connect.donkey.model.message.Status; import com.mirth.connect.model.Channel; import com.mirth.connect.model.ContextType; import com.mirth.connect.model.ServerEvent; import com.mirth.connect.model.ServerEvent.Level; import com.mirth.connect.server.MirthJavascriptTransformerException; import com.mirth.connect.server.builders.JavaScriptBuilder; import com.mirth.connect.server.controllers.ContextFactoryController; import com.mirth.connect.server.controllers.ControllerFactory; import com.mirth.connect.server.controllers.EventController; import com.mirth.connect.server.controllers.ScriptCompileException; import com.mirth.connect.server.controllers.ScriptController; import com.mirth.connect.server.userutil.Attachment; import com.mirth.connect.server.util.CompiledScriptCache; import com.mirth.connect.server.util.ServerUUIDGenerator; import com.mirth.connect.userutil.ImmutableConnectorMessage; public class JavaScriptUtil { private static Logger logger = Logger.getLogger(JavaScriptUtil.class); private static CompiledScriptCache compiledScriptCache = CompiledScriptCache.getInstance(); private static final int SOURCE_CODE_LINE_WRAPPER = 5; private static ExecutorService executor = Executors.newCachedThreadPool(); private static ContextFactoryController contextFactoryController = ControllerFactory.getFactory() .createContextFactoryController(); private static volatile String globalScriptContextFactoryId = null; private static String serverId = ControllerFactory.getFactory().createConfigurationController().getServerId(); public static <T> T execute(JavaScriptTask<T> task) throws JavaScriptExecutorException, InterruptedException { Future<T> future = executor.submit(task); try { return future.get(); } catch (ExecutionException e) { throw new JavaScriptExecutorException(e.getCause()); } catch (InterruptedException e) { // synchronize with JavaScriptTask.executeScript() so that it will not initialize the context while we are halting the task synchronized (task) { future.cancel(true); Context context = task.getContext(); if (context != null && context instanceof MirthContext) { ((MirthContext) context).setRunning(false); } } // TODO wait for the task thread to complete before exiting? Thread.currentThread().interrupt(); throw e; } } public static String executeAttachmentScript(MirthContextFactory contextFactory, final RawMessage message, final String channelId, final String channelName, final List<Attachment> attachments) throws InterruptedException, JavaScriptExecutorException { String processedMessage = message.getRawData(); Object result = null; try { result = execute(new JavaScriptTask<Object>(contextFactory, ScriptController.ATTACHMENT_SCRIPT_KEY, channelId, channelName) { @Override public Object doCall() throws Exception { Logger scriptLogger = Logger.getLogger(ScriptController.ATTACHMENT_SCRIPT_KEY.toLowerCase()); try { Scriptable scope = JavaScriptScopeUtil.getAttachmentScope(getContextFactory(), scriptLogger, channelId, channelName, message, attachments); return JavaScriptUtil.executeScript(this, ScriptController.getScriptId(ScriptController.ATTACHMENT_SCRIPT_KEY, channelId), scope, null, null); } finally { Context.exit(); } } }); } catch (JavaScriptExecutorException e) { logScriptError(ScriptController.ATTACHMENT_SCRIPT_KEY, channelId, e.getCause()); throw e; } if (result != null) { String resultString = (String) Context.jsToJava(result, java.lang.String.class); if (resultString != null) { processedMessage = resultString; } } return processedMessage; } /** * Executes the JavaScriptTask associated with the postprocessor, if necessary. * * @param task * @param channelId * @return * @throws InterruptedException * @throws JavaScriptExecutorException */ public static String executeJavaScriptPreProcessorTask(JavaScriptTask<Object> task, String channelId) throws InterruptedException, JavaScriptExecutorException { String channelScriptId = ScriptController.getScriptId(ScriptController.PREPROCESSOR_SCRIPT_KEY, channelId); // Only execute the task if the channel or global scripts exist if (compiledScriptCache.getCompiledScript(channelScriptId) != null || compiledScriptCache.getCompiledScript(ScriptController.PREPROCESSOR_SCRIPT_KEY) != null) { return (String) execute(task); } else { return null; } } /** * Executes the global and channel preprocessor scripts in order, building up the necessary * scope for the global preprocessor and adding the result back to it for the channel * preprocessor. * * @throws InterruptedException * @throws JavaScriptExecutorException * */ public static String executePreprocessorScripts(JavaScriptTask<Object> task, ConnectorMessage message, Map<String, Integer> destinationIdMap) throws Exception { String processedMessage = null; String globalResult = message.getRaw().getContent(); Logger scriptLogger = Logger.getLogger(ScriptController.PREPROCESSOR_SCRIPT_KEY.toLowerCase()); try { // Execute the global preprocessor and check the result Object result = null; if (compiledScriptCache.getCompiledScript(ScriptController.PREPROCESSOR_SCRIPT_KEY) != null) { // Pull the channel context factory out first since we're going to overwrite it MirthContextFactory contextFactory = task.getContextFactory(); MirthContextFactory globalScriptContextFactory = getGlobalScriptContextFactory(); try { Scriptable scope = JavaScriptScopeUtil.getPreprocessorScope(globalScriptContextFactory, scriptLogger, message.getChannelId(), message.getRaw().getContent(), new ImmutableConnectorMessage(message, true, destinationIdMap)); task.setContextFactory(globalScriptContextFactory); result = JavaScriptUtil.executeScript(task, ScriptController.PREPROCESSOR_SCRIPT_KEY, scope, null, null); } finally { Context.exit(); // Restore the channel context factory task.setContextFactory(contextFactory); } } if (result != null) { String resultString = (String) Context.jsToJava(result, java.lang.String.class); // Set the processed message in case something goes wrong in the channel processor. Also update the global result so the channel processor uses the updated message if (resultString != null) { processedMessage = resultString; globalResult = processedMessage; } } } catch (Exception e) { logScriptError(ScriptController.PREPROCESSOR_SCRIPT_KEY, message.getChannelId(), e); throw e; } try { // Execute the channel preprocessor and check the result Object result = null; String scriptId = ScriptController.getScriptId(ScriptController.PREPROCESSOR_SCRIPT_KEY, message.getChannelId()); if (compiledScriptCache.getCompiledScript(scriptId) != null) { try { // Update the scope with the result from the global processor Scriptable scope = JavaScriptScopeUtil.getPreprocessorScope(task.getContextFactory(), scriptLogger, message.getChannelId(), globalResult, new ImmutableConnectorMessage(message, true, destinationIdMap)); result = JavaScriptUtil.executeScript(task, scriptId, scope, null, null); } finally { Context.exit(); } } if (result != null) { String resultString = (String) Context.jsToJava(result, java.lang.String.class); // Set the processed message if there was a result. if (resultString != null) { processedMessage = resultString; } } } catch (Exception e) { logScriptError(ScriptController.PREPROCESSOR_SCRIPT_KEY, message.getChannelId(), e); throw e; } return processedMessage; } /** * Executes the JavaScriptTask associated with the postprocessor, if necessary. * * @param task * @param channelId * @return * @throws InterruptedException * @throws JavaScriptExecutorException */ public static Response executeJavaScriptPostProcessorTask(JavaScriptTask<Object> task, String channelId) throws InterruptedException, JavaScriptExecutorException { String channelScriptId = ScriptController.getScriptId(ScriptController.POSTPROCESSOR_SCRIPT_KEY, channelId); // Only execute the task if the channel or global scripts exist if (compiledScriptCache.getCompiledScript(channelScriptId) != null || compiledScriptCache.getCompiledScript(ScriptController.POSTPROCESSOR_SCRIPT_KEY) != null) { return (Response) execute(task); } else { return null; } } /** * Executes the channel postprocessor, followed by the global postprocessor. * * @param task * @param message * @return * @throws Exception */ public static Response executePostprocessorScripts(JavaScriptTask<Object> task, Message message) throws Exception { Logger scriptLogger = Logger.getLogger(ScriptController.POSTPROCESSOR_SCRIPT_KEY.toLowerCase()); Response channelResponse = null; try { String scriptId = ScriptController.getScriptId(ScriptController.POSTPROCESSOR_SCRIPT_KEY, message.getChannelId()); if (compiledScriptCache.getCompiledScript(scriptId) != null) { try { Scriptable scope = JavaScriptScopeUtil.getPostprocessorScope(task.getContextFactory(), scriptLogger, message.getChannelId(), message); channelResponse = getPostprocessorResponse( JavaScriptUtil.executeScript(task, scriptId, scope, null, null)); } finally { Context.exit(); } } } catch (Exception e) { logScriptError(ScriptController.POSTPROCESSOR_SCRIPT_KEY, message.getChannelId(), e); throw e; } Response response = channelResponse; try { if (compiledScriptCache.getCompiledScript(ScriptController.POSTPROCESSOR_SCRIPT_KEY) != null) { MirthContextFactory globalScriptContextFactory = getGlobalScriptContextFactory(); try { Scriptable scope = JavaScriptScopeUtil.getPostprocessorScope(globalScriptContextFactory, scriptLogger, message.getChannelId(), message, (channelResponse == null) ? null : new com.mirth.connect.userutil.Response(channelResponse)); task.setContextFactory(globalScriptContextFactory); Response globalResponse = getPostprocessorResponse(JavaScriptUtil.executeScript(task, ScriptController.POSTPROCESSOR_SCRIPT_KEY, scope, null, null)); if (globalResponse != null) { response = globalResponse; } } finally { Context.exit(); } } } catch (Exception e) { logScriptError(ScriptController.POSTPROCESSOR_SCRIPT_KEY, message.getChannelId(), e); throw e; } return response; } private static Response getPostprocessorResponse(Object result) { Response response = null; // Convert result of JavaScript execution to Response object if (result instanceof com.mirth.connect.userutil.Response) { response = convertToDonkeyResponse(result); } else if (result instanceof NativeJavaObject) { Object object = ((NativeJavaObject) result).unwrap(); if (object instanceof com.mirth.connect.userutil.Response) { response = convertToDonkeyResponse(object); } else { // Assume it's a string, and return a successful response // TODO: is it okay that we use Status.SENT here? response = new Response(Status.SENT, object.toString()); } } else if ((result != null) && !(result instanceof Undefined)) { // This branch will catch all objects that aren't Response, NativeJavaObject, Undefined, or null // Assume it's a string, and return a successful response // TODO: is it okay that we use Status.SENT here? response = new Response(Status.SENT, result.toString()); } return response; } public static Response convertToDonkeyResponse(Object response) { com.mirth.connect.userutil.Response userResponse = (com.mirth.connect.userutil.Response) response; return new Response(convertToDonkeyStatus(userResponse.getStatus()), userResponse.getMessage(), userResponse.getStatusMessage(), userResponse.getError()); } public static Status convertToDonkeyStatus(com.mirth.connect.userutil.Status status) { switch (status) { case RECEIVED: return Status.RECEIVED; case FILTERED: return Status.FILTERED; case TRANSFORMED: return Status.TRANSFORMED; case SENT: return Status.SENT; case QUEUED: return Status.QUEUED; case ERROR: return Status.ERROR; case PENDING: return Status.PENDING; default: return null; } } /** * Executes channel level deploy scripts. * * @param scriptId * @param scriptType * @param channelId * @throws InterruptedException * @throws JavaScriptExecutorException */ public static void executeChannelDeployScript(MirthContextFactory contextFactory, final String scriptId, final String scriptType, final String channelId, final String channelName) throws InterruptedException, JavaScriptExecutorException { try { execute(new JavaScriptTask<Object>(contextFactory, scriptType, channelId, channelName) { @Override public Object doCall() throws Exception { Logger scriptLogger = Logger.getLogger(scriptType.toLowerCase()); try { Scriptable scope = JavaScriptScopeUtil.getDeployScope(getContextFactory(), scriptLogger, channelId, channelName); JavaScriptUtil.executeScript(this, scriptId, scope, channelId, null); return null; } finally { Context.exit(); } } }); } catch (JavaScriptExecutorException e) { logScriptError(scriptId, channelId, e.getCause()); throw e; } } /** * Executes channel level undeploy scripts. * * @param scriptId * @param scriptType * @param channelId * @throws InterruptedException * @throws JavaScriptExecutorException */ public static void executeChannelUndeployScript(MirthContextFactory contextFactory, final String scriptId, final String scriptType, final String channelId, final String channelName) throws InterruptedException, JavaScriptExecutorException { try { execute(new JavaScriptTask<Object>(contextFactory, scriptType, channelId, channelName) { @Override public Object doCall() throws Exception { Logger scriptLogger = Logger.getLogger(scriptType.toLowerCase()); try { Scriptable scope = JavaScriptScopeUtil.getUndeployScope(getContextFactory(), scriptLogger, channelId, channelName); JavaScriptUtil.executeScript(this, scriptId, scope, channelId, null); return null; } finally { Context.exit(); } } }); } catch (JavaScriptExecutorException e) { logScriptError(scriptId, channelId, e.getCause()); throw e; } } /** * Executes global level deploy scripts. * * @param scriptId * @throws InterruptedException * @throws JavaScriptExecutorException */ public static void executeGlobalDeployScript(final String scriptId) throws InterruptedException, JavaScriptExecutorException { try { execute(new JavaScriptTask<Object>(getGlobalScriptContextFactory(), "Global Deploy") { @Override public Object doCall() throws Exception { Logger scriptLogger = Logger.getLogger(scriptId.toLowerCase()); try { Scriptable scope = JavaScriptScopeUtil.getDeployScope(getContextFactory(), scriptLogger); JavaScriptUtil.executeScript(this, scriptId, scope, null, null); return null; } finally { Context.exit(); } } }); } catch (Exception e) { if (!(e instanceof JavaScriptExecutorException)) { e = new JavaScriptExecutorException(e); } logScriptError(scriptId, null, e.getCause()); throw (JavaScriptExecutorException) e; } } /** * Executes global level undeploy scripts. * * @param scriptId * @throws InterruptedException * @throws JavaScriptExecutorException */ public static void executeGlobalUndeployScript(final String scriptId) throws InterruptedException, JavaScriptExecutorException { try { execute(new JavaScriptTask<Object>(getGlobalScriptContextFactory(), "Global Undeploy") { @Override public Object doCall() throws Exception { Logger scriptLogger = Logger.getLogger(scriptId.toLowerCase()); try { Scriptable scope = JavaScriptScopeUtil.getUndeployScope(getContextFactory(), scriptLogger); JavaScriptUtil.executeScript(this, scriptId, scope, null, null); return null; } finally { Context.exit(); } } }); } catch (Exception e) { if (!(e instanceof JavaScriptExecutorException)) { e = new JavaScriptExecutorException(e); } logScriptError(scriptId, null, e.getCause()); throw (JavaScriptExecutorException) e; } } private static MirthContextFactory getGlobalScriptContextFactory() throws Exception { MirthContextFactory contextFactory = contextFactoryController.getGlobalScriptContextFactory(); if (!contextFactory.getId().equals(globalScriptContextFactoryId)) { synchronized (JavaScriptUtil.class) { contextFactory = contextFactoryController.getGlobalScriptContextFactory(); if (!contextFactory.getId().equals(globalScriptContextFactoryId)) { ControllerFactory.getFactory().createScriptController().compileGlobalScripts(contextFactory); globalScriptContextFactoryId = contextFactory.getId(); } } } return contextFactory; } /** * Logs out a script error with the script type and the script level (channelId or global). * * @param scriptType * @param channelId * @param e */ private static void logScriptError(String scriptType, String channelId, Throwable t) { EventController eventController = ControllerFactory.getFactory().createEventController(); String error = "Error executing " + scriptType + " script from channel: "; if (StringUtils.isNotEmpty(channelId)) { error += channelId; } else { error += "Global"; } ServerEvent event = new ServerEvent(serverId, error); event.setLevel(Level.ERROR); event.getAttributes().put(ServerEvent.ATTR_EXCEPTION, ExceptionUtils.getStackTrace(t)); eventController.dispatchEvent(event); logger.error(error, t); } /** * Executes the script with the given scriptId and scope. * * @param scriptId * @param scope * @return * @throws Exception */ public static Object executeScript(JavaScriptTask<Object> task, String scriptId, Scriptable scope, String channelId, String connectorName) throws Exception { Script compiledScript = compiledScriptCache.getCompiledScript(scriptId); if (compiledScript == null) { return null; } try { logger.debug("executing script: id=" + scriptId); return task.executeScript(compiledScript, scope); } catch (Exception e) { if (e instanceof RhinoException) { String script = compiledScriptCache.getSourceScript(scriptId); String sourceCode = getSourceCode(script, ((RhinoException) e).lineNumber(), 0); e = new MirthJavascriptTransformerException((RhinoException) e, channelId, connectorName, 0, null, sourceCode); } throw e; } } /* * Generates and returns the compiled global scope script. */ public static Script getCompiledGlobalSealedScript(Context context) { return compileScript(context, JavaScriptBuilder.generateGlobalSealedScript()); } /* * Returns a compiled Script object from a String. */ private static Script compileScript(Context context, String script) { return compileScript(context, script, ServerUUIDGenerator.getUUID()); } private static Script compileScript(Context context, String script, String scriptId) { return context.compileString(script, scriptId, 1, null); } public static void compileChannelScripts(MirthContextFactory contextFactory, Channel channel) throws ScriptCompileException { try { String deployScriptId = ScriptController.getScriptId(ScriptController.DEPLOY_SCRIPT_KEY, channel.getId()); String undeployScriptId = ScriptController.getScriptId(ScriptController.UNDEPLOY_SCRIPT_KEY, channel.getId()); String preprocessorScriptId = ScriptController.getScriptId(ScriptController.PREPROCESSOR_SCRIPT_KEY, channel.getId()); String postprocessorScriptId = ScriptController.getScriptId(ScriptController.POSTPROCESSOR_SCRIPT_KEY, channel.getId()); if (channel.isEnabled()) { compileAndAddScript(channel.getId(), contextFactory, deployScriptId, channel.getDeployScript(), ContextType.CHANNEL_DEPLOY); compileAndAddScript(channel.getId(), contextFactory, undeployScriptId, channel.getUndeployScript(), ContextType.CHANNEL_UNDEPLOY); // Only compile and run preprocessor if it's not the default if (!compileAndAddScript(channel.getId(), contextFactory, preprocessorScriptId, channel.getPreprocessingScript(), ContextType.CHANNEL_PREPROCESSOR)) { logger.debug("removing " + preprocessorScriptId); removeScriptFromCache(preprocessorScriptId); } // Only compile and run post processor if it's not the default if (!compileAndAddScript(channel.getId(), contextFactory, postprocessorScriptId, channel.getPostprocessingScript(), ContextType.CHANNEL_POSTPROCESSOR)) { logger.debug("removing " + postprocessorScriptId); removeScriptFromCache(postprocessorScriptId); } } else { removeScriptFromCache(deployScriptId); removeScriptFromCache(undeployScriptId); removeScriptFromCache(postprocessorScriptId); } } catch (Exception e) { throw new ScriptCompileException("Failed to compile scripts for channel " + channel.getId() + ".", e); } } public static void recompileChannelScript(MirthContextFactory contextFactory, String channelId, String scriptKey) throws ScriptCompileException { try { recompileGeneratedScript(contextFactory, ScriptController.getScriptId(scriptKey, channelId)); } catch (Exception e) { throw new ScriptCompileException("Failed to compile scripts for channel " + channelId + ".", e); } } public static void compileGlobalScripts(MirthContextFactory contextFactory, Map<String, String> globalScripts) throws Exception { for (Entry<String, String> entry : globalScripts.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); try { // In 2.x templates with the Channel context were allowed in the global postprocessor, so for now we're keeping that the same. if (!compileAndAddScript(null, contextFactory, key, value, ScriptController.getContextType(key))) { logger.debug("removing global " + key.toLowerCase()); removeScriptFromCache(key); } } catch (Exception e) { logger.error("Error compiling global script: " + key, e); throw e; } } } /* * Encapsulates a JavaScript script into the doScript() function, compiles it, and adds it to * the compiled script cache. */ public static boolean compileAndAddScript(String channelId, MirthContextFactory contextFactory, String scriptId, String script, ContextType contextType) throws Exception { return compileAndAddScript(channelId, contextFactory, scriptId, script, contextType, null); } public static boolean compileAndAddScript(String channelId, MirthContextFactory contextFactory, String scriptId, String script, ContextType contextType, Set<String> scriptOptions) throws Exception { return compileAndAddScript(channelId, contextFactory, scriptId, script, contextType, scriptOptions, JavaScriptBuilder.generateDefaultKeyScript(ScriptController.getScriptKey(scriptId), ScriptController.isScriptGlobal(scriptId))); } public static boolean compileAndAddScript(String channelId, MirthContextFactory contextFactory, String scriptId, String script, ContextType contextType, Set<String> scriptOptions, String defaultScript) throws Exception { // Note: If the defaultScript is NULL, this means that the script should // always be inserted without being compared. boolean scriptInserted = false; String generatedScript = null; Context context = JavaScriptScopeUtil.getContext(contextFactory); try { logger.debug("compiling script " + scriptId); generatedScript = JavaScriptBuilder.generateScript(channelId, script, scriptOptions, contextType); Script compiledScript = compileScript(context, generatedScript, scriptId); String decompiledScript = context.decompileScript(compiledScript, 0); String decompiledDefaultScript = null; if (defaultScript != null) { String generatedDefaultScript = JavaScriptBuilder.generateScript(channelId, defaultScript, scriptOptions, contextType); Script compiledDefaultScript = compileScript(context, generatedDefaultScript, scriptId); decompiledDefaultScript = context.decompileScript(compiledDefaultScript, 0); } if ((defaultScript == null) || !decompiledScript.equals(decompiledDefaultScript)) { logger.debug("adding script " + scriptId); compiledScriptCache.putCompiledScript(scriptId, compiledScript, generatedScript); scriptInserted = true; } else { compiledScriptCache.removeCompiledScript(scriptId); } } catch (EvaluatorException e) { if (e instanceof RhinoException) { String sourceCode = getSourceCode(generatedScript, ((RhinoException) e).lineNumber(), 0); MirthJavascriptTransformerException mjte = new MirthJavascriptTransformerException( (RhinoException) e, null, null, 0, scriptId, sourceCode); throw new Exception(mjte); } else { throw new Exception(e); } } finally { Context.exit(); } return scriptInserted; } public static boolean recompileGeneratedScript(MirthContextFactory contextFactory, String scriptId) throws Exception { boolean scriptInserted = false; if (scriptId != null) { String generatedScript = compiledScriptCache.getSourceScript(scriptId); if (generatedScript != null) { Context context = JavaScriptScopeUtil.getContext(contextFactory); try { logger.debug("compiling script " + scriptId); Script compiledScript = compileScript(context, generatedScript, scriptId); logger.debug("adding script " + scriptId); compiledScriptCache.putCompiledScript(scriptId, compiledScript, generatedScript); scriptInserted = true; } catch (EvaluatorException e) { if (e instanceof RhinoException) { String sourceCode = getSourceCode(generatedScript, ((RhinoException) e).lineNumber(), 0); MirthJavascriptTransformerException mjte = new MirthJavascriptTransformerException( (RhinoException) e, null, null, 0, scriptId, sourceCode); throw new Exception(mjte); } else { throw new Exception(e); } } finally { Context.exit(); } } } return scriptInserted; } public static void removeScriptFromCache(String scriptId) { if (compiledScriptCache.getCompiledScript(scriptId) != null) { compiledScriptCache.removeCompiledScript(scriptId); } } public static void removeChannelScriptsFromCache(String channelId) { removeScriptFromCache(ScriptController.getScriptId(ScriptController.DEPLOY_SCRIPT_KEY, channelId)); removeScriptFromCache(ScriptController.getScriptId(ScriptController.UNDEPLOY_SCRIPT_KEY, channelId)); removeScriptFromCache(ScriptController.getScriptId(ScriptController.PREPROCESSOR_SCRIPT_KEY, channelId)); removeScriptFromCache(ScriptController.getScriptId(ScriptController.POSTPROCESSOR_SCRIPT_KEY, channelId)); removeScriptFromCache(ScriptController.getScriptId(ScriptController.ATTACHMENT_SCRIPT_KEY, channelId)); removeScriptFromCache(ScriptController.getScriptId(ScriptController.BATCH_SCRIPT_KEY, channelId)); } /** * Utility to get source code from script. Used to generate error report. * * @param script * @param errorLineNumber * @param offset * @return */ public static String getSourceCode(String script, int errorLineNumber, int offset) { String[] lines = script.split("\n"); int startingLineNumber = errorLineNumber - offset; /* * If the starting line number is 5 or less, set it to 6 so that it displays lines 1-11 * (0-10 in the array) */ if (startingLineNumber <= SOURCE_CODE_LINE_WRAPPER) { startingLineNumber = SOURCE_CODE_LINE_WRAPPER + 1; } int currentLineNumber = startingLineNumber - SOURCE_CODE_LINE_WRAPPER; StringBuilder source = new StringBuilder(); while ((currentLineNumber < (startingLineNumber + SOURCE_CODE_LINE_WRAPPER)) && (currentLineNumber < lines.length)) { source.append( System.getProperty("line.separator") + currentLineNumber + ": " + lines[currentLineNumber - 1]); currentLineNumber++; } return source.toString(); } }