/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.components.script.execution; import java.io.IOException; 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.common.registry.ScriptExecutorFactoryRegistry; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.api.ComponentException; import de.rcenvironment.core.component.datamanagement.api.ComponentDataManagementService; import de.rcenvironment.core.component.execution.api.ComponentContext; import de.rcenvironment.core.component.execution.api.ThreadHandler; import de.rcenvironment.core.component.executor.SshExecutorConstants; import de.rcenvironment.core.component.model.spi.DefaultComponent; import de.rcenvironment.core.utils.common.LogUtils; import de.rcenvironment.core.utils.scripting.ScriptLanguage; /** * Component to execute all kind of script languages. * * @author Sascha Zur */ public class ScriptComponent extends DefaultComponent { private ScriptExecutorFactoryRegistry scriptExecutorRegistry; private ComponentContext componentContext; private ScriptExecutor executor; private String script; private ScriptLanguage scriptLanguage; private ScriptComponentHistoryDataItem historyDataItem; private String scriptFileRef; private Log log = LogFactory.getLog(ScriptComponent.class); private volatile boolean canceled; @Override public void setComponentContext(ComponentContext componentContext) { this.componentContext = componentContext; } @Override public boolean treatStartAsComponentRun() { return componentContext.getInputs().isEmpty(); } @Override public void start() throws ComponentException { canceled = false; scriptExecutorRegistry = componentContext.getService(ScriptExecutorFactoryRegistry.class); String language = componentContext.getConfigurationValue("scriptLanguage"); setExecutor(scriptExecutorRegistry.requestScriptExecutor(ScriptLanguage.getByName(language))); scriptLanguage = ScriptLanguage.getByName(language); script = componentContext.getConfigurationValue(SshExecutorConstants.CONFIG_KEY_SCRIPT); executor.prepareExecutor(componentContext); if (treatStartAsComponentRun()) { processInputs(); } } @Override public void processInputs() throws ComponentException { initializeNewHistoryDataItem(); executor.prepareNewRun(scriptLanguage, script, historyDataItem); if (historyDataItem != null) { if (scriptFileRef == null) { try { scriptFileRef = componentContext.getService(ComponentDataManagementService.class) .createTaggedReferenceFromString(componentContext, script); historyDataItem.setScriptFileReference(scriptFileRef); } catch (IOException e) { String errorMessage = "Failed to store Python script into the data management" + "; it will not be available in the workflow data browser"; String errorId = LogUtils.logExceptionWithStacktraceAndAssignUniqueMarker(LogFactory.getLog(ScriptComponent.class), errorMessage, e); componentContext.getLog().componentError(errorMessage, e, errorId); } } } try { executor.runScript(); } catch (ComponentException e) { // A ComponentException is thrown if the script execution failed as well as if the script execution was canceled. To distinguish // between both cases, we catch the exception and re-throw it, if the execution was not canceled by the user. if (canceled) { return; } throw e; } executor.postRun(); executor.deleteTempFiles(); writeFinalHistoryDataItem(); } @Override public void onStartInterrupted(ThreadHandler executingThreadHandler) { cancelScriptExecution(executingThreadHandler); } @Override public void onProcessInputsInterrupted(ThreadHandler executingThreadHandler) { cancelScriptExecution(executingThreadHandler); } private void cancelScriptExecution(ThreadHandler executingThreadHandler) { canceled = true; if (executor == null) { // FIXME we need to delay the cancellation request, currently it is simple ignored in this case log.error("Cannot cancel the execution, as the script executor (Script Component) is not propertly prepared."); return; } if (executor.isCancelable()) { this.executor.cancelScript(); } else { executingThreadHandler.interrupt(); } } public void setExecutor(ScriptExecutor executor) { this.executor = executor; } @Override public void completeStartOrProcessInputsAfterFailure() throws ComponentException { writeFinalHistoryDataItem(); } @Override public void tearDown(FinalComponentState state) { if (executor != null) { executor.deleteTempFiles(); } } private void initializeNewHistoryDataItem() { if (Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { historyDataItem = new ScriptComponentHistoryDataItem(); } } private void writeFinalHistoryDataItem() { if (historyDataItem != null && Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { componentContext.writeFinalHistoryDataItem(historyDataItem); } } @Override public void reset() throws ComponentException { super.reset(); executor.reset(); } }