/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.scheduler.task.executors;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Serializable;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.ow2.proactive.scheduler.common.task.dataspaces.RemoteSpace;
import org.ow2.proactive.scheduler.common.task.flow.FlowAction;
import org.ow2.proactive.scheduler.common.task.flow.FlowScript;
import org.ow2.proactive.scheduler.common.task.util.SerializationUtil;
import org.ow2.proactive.scheduler.rest.ds.IDataSpaceClient;
import org.ow2.proactive.scheduler.task.TaskResultImpl;
import org.ow2.proactive.scheduler.task.client.SchedulerNodeClient;
import org.ow2.proactive.scheduler.task.containers.ScriptExecutableContainer;
import org.ow2.proactive.scheduler.task.context.TaskContext;
import org.ow2.proactive.scheduler.task.context.TaskContextVariableExtractor;
import org.ow2.proactive.scheduler.task.exceptions.TaskException;
import org.ow2.proactive.scheduler.task.executors.forked.env.ForkedTaskVariablesManager;
import org.ow2.proactive.scheduler.task.utils.VariablesMap;
import org.ow2.proactive.scripting.Script;
import org.ow2.proactive.scripting.ScriptHandler;
import org.ow2.proactive.scripting.ScriptLoader;
import org.ow2.proactive.scripting.ScriptResult;
import org.ow2.proactive.utils.PAProperties;
import com.google.common.base.Stopwatch;
/**
* Run a task through a script handler.
*
* Responsible for:
* - running the different scripts
* - variable propagation
* - replacement for variables
* - getting the task result or user code exceptions
*/
public class InProcessTaskExecutor implements TaskExecutor {
private static final String NODES_FILE_DIRECTORY_NAME = ".pa_nodes";
private final ForkedTaskVariablesManager forkedTaskVariablesManager = new ForkedTaskVariablesManager();
private final TaskContextVariableExtractor taskContextVariableExtractor = new TaskContextVariableExtractor();
/**
* Writes a nodes file to disk.
*
* @param taskContext The task taskContext from which to extract the nodes file from.
* @return The absolute path of the nodes file.
* @throws IOException
*/
private static String writeNodesFile(TaskContext taskContext) throws IOException {
List<String> nodesHosts = taskContext.getNodesHosts();
if (nodesHosts.isEmpty()) {
return "";
} else {
File directory;
if (taskContext.getNodeDataSpaceURIs().getScratchURI() == null ||
taskContext.getNodeDataSpaceURIs().getScratchURI().isEmpty()) {
directory = new File(".");
} else {
directory = new File(taskContext.getNodeDataSpaceURIs().getScratchURI());
}
File nodesFile = new File(directory, NODES_FILE_DIRECTORY_NAME + "_" + taskContext.getTaskId());
Writer outputWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(nodesFile),
PAProperties.getFileEncoding()));
for (String nodeHost : nodesHosts) {
outputWriter.append(nodeHost).append(System.lineSeparator());
}
outputWriter.close();
return nodesFile.getAbsolutePath();
}
}
/**
* Executes a task inside a task context.
*
* @param taskContext Task context to execute.
* @param output Standard output sink.
* @param error Error sink.
* @return Returns the task result.
*/
@Override
public TaskResultImpl execute(TaskContext taskContext, PrintStream output, PrintStream error) {
ScriptHandler scriptHandler = ScriptLoader.createLocalHandler();
String nodesFile = null;
SchedulerNodeClient schedulerNodeClient = null;
RemoteSpace userSpaceClient = null;
RemoteSpace globalSpaceClient = null;
try {
nodesFile = writeNodesFile(taskContext);
VariablesMap variables = new VariablesMap();
variables.setInheritedMap(taskContextVariableExtractor.extractVariables(taskContext, nodesFile, false));
variables.setScopeMap(taskContextVariableExtractor.extractScopeVariables(taskContext));
Map<String, String> resultMetadata = new HashMap<>();
Map<String, String> thirdPartyCredentials = forkedTaskVariablesManager.extractThirdPartyCredentials(taskContext);
schedulerNodeClient = forkedTaskVariablesManager.createSchedulerNodeClient(taskContext);
userSpaceClient = forkedTaskVariablesManager.createDataSpaceNodeClient(taskContext,
schedulerNodeClient,
IDataSpaceClient.Dataspace.USER);
globalSpaceClient = forkedTaskVariablesManager.createDataSpaceNodeClient(taskContext,
schedulerNodeClient,
IDataSpaceClient.Dataspace.GLOBAL);
forkedTaskVariablesManager.addBindingsToScriptHandler(scriptHandler,
taskContext,
variables,
thirdPartyCredentials,
schedulerNodeClient,
userSpaceClient,
globalSpaceClient,
resultMetadata);
Stopwatch stopwatch = Stopwatch.createUnstarted();
TaskResultImpl taskResult;
try {
stopwatch.start();
Serializable result = execute(taskContext,
output,
error,
scriptHandler,
thirdPartyCredentials,
variables);
stopwatch.stop();
taskResult = new TaskResultImpl(taskContext.getTaskId(),
result,
null,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
} catch (Throwable e) {
stopwatch.stop();
e.printStackTrace(error);
taskResult = new TaskResultImpl(taskContext.getTaskId(),
e,
null,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
executeFlowScript(taskContext.getControlFlowScript(), scriptHandler, output, error, taskResult);
taskResult.setPropagatedVariables(SerializationUtil.serializeVariableMap(variables.getPropagatedVariables()));
taskResult.setMetadata(resultMetadata);
return taskResult;
} catch (Throwable e) {
e.printStackTrace(error);
return new TaskResultImpl(taskContext.getTaskId(), e);
} finally {
if (nodesFile != null && !nodesFile.isEmpty()) {
FileUtils.deleteQuietly(new File(nodesFile));
}
}
}
/**
* Executes a task.
*
* @param taskContext Task context to execute
* @param output Standard output sink.
* @param error Error sink.
* @param scriptHandler
* @param thirdPartyCredentials
* @param variables Environment variables.
* @return The result of the executed script.
* @throws Throwable
*/
private Serializable execute(TaskContext taskContext, PrintStream output, PrintStream error,
ScriptHandler scriptHandler, Map<String, String> thirdPartyCredentials, VariablesMap variables)
throws Throwable {
if (taskContext.getPreScript() != null) {
Script<?> script = taskContext.getPreScript();
forkedTaskVariablesManager.replaceScriptParameters(script, thirdPartyCredentials, variables, error);
ScriptResult preScriptResult = scriptHandler.handle(script, output, error);
if (preScriptResult.errorOccured()) {
throw new TaskException("Failed to execute pre script: " + preScriptResult.getException().getMessage(),
preScriptResult.getException());
}
}
Script<Serializable> script = ((ScriptExecutableContainer) taskContext.getExecutableContainer()).getScript();
forkedTaskVariablesManager.replaceScriptParameters(script, thirdPartyCredentials, variables, error);
ScriptResult<Serializable> scriptResult = scriptHandler.handle(script, output, error);
if (scriptResult.errorOccured()) {
throw new TaskException("Failed to execute task: " + scriptResult.getException().getMessage(),
scriptResult.getException());
}
if (taskContext.getPostScript() != null) {
forkedTaskVariablesManager.replaceScriptParameters(taskContext.getPostScript(),
thirdPartyCredentials,
variables,
error);
ScriptResult postScriptResult = scriptHandler.handle(taskContext.getPostScript(), output, error);
if (postScriptResult.errorOccured()) {
throw new TaskException("Failed to execute post script: " +
postScriptResult.getException().getMessage(), postScriptResult.getException());
}
}
return scriptResult.getResult();
}
private void executeFlowScript(FlowScript flowScript, ScriptHandler scriptHandler, PrintStream output,
PrintStream error, TaskResultImpl taskResult) {
if (flowScript != null) {
try {
scriptHandler.addBinding(FlowScript.resultVariable, taskResult.value());
} catch (Throwable throwable) {
scriptHandler.addBinding(FlowScript.resultVariable, throwable);
}
ScriptResult<FlowAction> flowScriptResult = scriptHandler.handle(flowScript, output, error);
if (flowScriptResult.errorOccured()) {
error.println(flowScriptResult.getException().getMessage());
taskResult.setException(flowScriptResult.getException());
taskResult.setAction(FlowAction.getDefaultAction(flowScript));
} else {
taskResult.setAction(flowScriptResult.getResult());
}
}
}
}