/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.components.script.execution.jython;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.util.HashMap;
import javax.script.ScriptException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.components.script.common.ScriptComponentHistoryDataItem;
import de.rcenvironment.components.script.common.registry.ScriptExecutor;
import de.rcenvironment.components.script.execution.DefaultScriptExecutor;
import de.rcenvironment.core.component.api.ComponentException;
import de.rcenvironment.core.component.execution.api.ComponentContext;
import de.rcenvironment.core.component.scripting.WorkflowConsoleForwardingWriter;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.scripting.ScriptingService;
import de.rcenvironment.core.scripting.ScriptingUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.scripting.ScriptLanguage;
/**
*
* Implementation of {@link ScriptExecutor} to execute default script engines.
*
* @author Mark Geiger
* @author Sascha Zur
*/
public class JythonScriptExecutor extends DefaultScriptExecutor {
protected static ScriptingService scriptingService;
private static final String QUOTE = "\"";
private static final String ESCAPESLASH = "\\\\";
private static final String SLASH = "/";
private static final Log LOGGER = LogFactory.getLog(JythonScriptExecutor.class);
protected String preHeader;
protected String header;
protected String body;
protected String foot;
protected String jythonPath = "";
protected String workingPath = "";
@Override
public boolean prepareExecutor(ComponentContext componentContext) throws ComponentException {
boolean successful = super.prepareExecutor(componentContext);
try {
// this method determins the locaton of the jython.jar
jythonPath = ScriptingUtils.getJythonPath();
} catch (IOException e) {
throw new ComponentException("Internal error: Failed to intialize Jython", e);
}
if (jythonPath == null) {
throw new ComponentException("Internal error: Failed to intialize Jython");
}
File scripts = new File(tempDir, "scripts");
File file =
new File(scripts, "script.tmp");
workingPath = scripts.getParentFile().getAbsolutePath().toString();
workingPath = workingPath.replaceAll(ESCAPESLASH, SLASH);
tempFiles.add(file);
stateMap = new HashMap<>();
scriptingService = componentContext.getService(ScriptingService.class);
typedDatumFactory = componentContext.getService(TypedDatumService.class).getFactory();
return successful;
}
@Override
public void prepareNewRun(ScriptLanguage scriptLanguage, String userScript,
ScriptComponentHistoryDataItem dataItem) throws ComponentException {
historyDataItem = dataItem;
scriptEngine = scriptingService.createScriptEngine(scriptLanguage);
body = "";
foot = "";
header = ScriptingUtils.prepareHeaderScript(stateMap, componentContext, tempDir, tempFiles);
loadScript(userScript);
foot =
"\nRCE_Dict_OutputChannels = RCE.get_output_internal()\nRCE_CloseOutputChannelsList = RCE.get_closed_outputs_internal()\n"
+ StringUtils.format("sys.stdout.write('%s')\nsys.stderr.write('%s')\nsys.stdout.flush()\nsys.stderr.flush()",
WorkflowConsoleForwardingWriter.CONSOLE_END, WorkflowConsoleForwardingWriter.CONSOLE_END);
}
private void loadScript(String userScript) throws ComponentException {
// wrappingScript = wrappingScript + userScript;
body = userScript;
if (body == null || body.length() == 0) {
throw new ComponentException("No Python script configured");
}
}
@Override
public void runScript() throws ComponentException {
// As the Jython script engine is not thread safe (console outputs of multiple script
// executions are mixed), we must ensure that at most one script is executed at the same
// time
synchronized (ScriptingUtils.SCRIPT_EVAL_LOCK_OBJECT) {
prepareOutputForRun();
try {
// include two important paths wich the header script need
scriptEngine.eval("RCE_Bundle_Jython_Path = " + QUOTE + jythonPath + QUOTE);
scriptEngine.eval("RCE_Temp_working_path = " + QUOTE + workingPath + QUOTE);
// execute the headerScript, this defines the RCE_Channel class and some important
// imports, variables and
// its changig the working directory.
scriptEngine.eval(header);
} catch (IOError | ScriptException e) {
throw new ComponentException("Failed to execute script that is wrapped around the actual script", e);
}
try {
// execute the script, written by the user.
scriptEngine.eval(body);
} catch (IOError | ScriptException e) {
throw new ComponentException("Failed to execute script", e);
}
try {
// this script defines the outputChannel, so that all outputs sent with
// RCE.write_output() work properly.
scriptEngine.eval(foot);
((WorkflowConsoleForwardingWriter) scriptEngine.getContext().getWriter()).awaitPrintingLinesFinished();
((WorkflowConsoleForwardingWriter) scriptEngine.getContext().getErrorWriter()).awaitPrintingLinesFinished();
} catch (IOError | ScriptException e) {
throw new ComponentException("Failed to execute script that is wrapped around the actual script", e);
} catch (InterruptedException e) {
componentContext.getLog().componentError("Waiting for script output was interrupted. Some output might be missing");
LOGGER.error("Waiting for stdout or stderr of Jython script execution was interrupted");
}
}
}
@Override
public boolean postRun() throws ComponentException {
ScriptingUtils.writeAPIOutput(stateMap, componentContext, scriptEngine, workingPath, historyDataItem);
try {
closeConsoleWriters();
} catch (IOException e) {
LOGGER.error("Failed to close stdout or stderr writer", e);
}
return true;
}
@Override
public void setWorkingPath(String workingPath) {
this.workingPath = workingPath;
}
@Override
public void cancelScript() {
// We cannot cancel the execution of the script. Instead the thread needs to be interrupted.
}
@Override
public boolean isCancelable() {
// We cannot cancel the execution of the script. Instead the thread needs to be interrupted.
return false;
}
}