/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.wrapper;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.component.datamanagement.stateful.StatefulComponentDataManagementService;
import de.rcenvironment.core.component.execution.api.ComponentContext;
import de.rcenvironment.core.toolkitbridge.transitional.TextStreamWatcherFactory;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
import de.rcenvironment.core.utils.common.textstream.TextOutputReceiver;
import de.rcenvironment.core.utils.common.textstream.TextStreamWatcher;
import de.rcenvironment.core.utils.common.textstream.receivers.AbstractTextOutputReceiver;
import de.rcenvironment.core.utils.common.textstream.receivers.LoggingTextOutReceiver;
import de.rcenvironment.core.utils.common.validation.ValidationFailureException;
import de.rcenvironment.core.utils.executor.CommandLineExecutor;
/*
* Copyright (C) 2006-2011 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
/**
* An abstract base class for wrappers of command-line tools.
*
* @author Robert Mischke
*
* @param <C> the type of the configuration object passed to each tool invocation
* @param <R> the type of the result returned from each tool invocation
*/
public abstract class WrapperBase<C, R> {
/**
* A simple {@link TextOutputReceiver} adapter that forwards all captured text lines to the
* "stdout" monitoring channel.
*
* @author Robert Mischke
*
*/
protected final class StdoutMonitoringForwarder extends AbstractTextOutputReceiver {
@Override
public void addOutput(String line) {
monitoringListener.appendStdout(line);
}
}
/**
* A simple {@link TextOutputReceiver} adapter that forwards all captured text lines to the
* "stderr" monitoring channel.
*
* @author Robert Mischke
*/
protected final class StderrMonitoringForwarder extends AbstractTextOutputReceiver {
@Override
public void addOutput(String line) {
monitoringListener.appendStderr(line);
}
}
protected final MonitoringEventListener monitoringListener;
protected final StdoutMonitoringForwarder stdoutMonitoringForwarder;
protected final StderrMonitoringForwarder stderrMonitoringForwarder;
protected final Log log = LogFactory.getLog(getClass());
private final List<BasicWrapperHook> registeredHooks;
private final StatefulComponentDataManagementService fileReferenceHandler;
private ComponentContext componentContext;
protected WrapperBase(StatefulComponentDataManagementService fileReferenceHandler, MonitoringEventListener listener,
ComponentContext compInformation) {
this.monitoringListener = listener;
this.fileReferenceHandler = fileReferenceHandler;
this.stdoutMonitoringForwarder = new StdoutMonitoringForwarder();
this.stderrMonitoringForwarder = new StderrMonitoringForwarder();
this.registeredHooks = new ArrayList<BasicWrapperHook>();
this.componentContext = compInformation;
}
/**
* Adds a new lifecycle hook/listener.
*
* @param hook the hook to add
*/
public void registerHook(BasicWrapperHook hook) {
registeredHooks.add(hook);
}
/**
* Removes a lifecycle hook/listener.
*
* @param hook the hook to remove
*/
public void unregisterHook(BasicWrapperHook hook) {
registeredHooks.remove(hook);
}
/**
* Performs initialization steps that are only required once for repeated execution runs, for
* example network connections to the execution host.
*
* @throws IOException on I/O errors
* @throws ValidationFailureException on invalid configuration values
*/
public void setupStaticEnvironment() throws IOException, ValidationFailureException {
// empty by default; subclasses may (but don't have to) override
}
/**
* The counterpart to {@link #setupStaticEnvironment()}; responsible for cleaning up everything
* that is not meant to remain on the execution host.
*
* @throws IOException on I/O errors
*/
public void tearDownStaticEnvironment() throws IOException {
// empty by default; subclasses may (but don't have to) override
}
/**
* Performs a single tool invocation.
*
* @param runConfiguration the subclass-specific configuration object
* @return the subclass-specific result object
* @throws Exception on errors; TODO narrow down; does ComponentException fit here?
*/
public abstract R execute(C runConfiguration) throws Exception;
/**
* Convenience variant of {@link #execute(Object)} that sets a hook only for the duration of
* this single execution.
*
* @param runConfiguration the subclass-specific configuration object
* @param singleRunHook the hook to register only for this single execution
* @return the subclass-specific result object
* @throws Exception on errors; TODO narrow down; does ComponentException fit here?
*/
public R execute(C runConfiguration, BasicWrapperHook singleRunHook) throws Exception {
registerHook(singleRunHook);
try {
return execute(runConfiguration);
} finally {
unregisterHook(singleRunHook);
}
}
/**
* Uploads a set of files to the work directory. In the given map, the keys are the
* workdir-relative filenames, and the values are the abstract content references.
*
* @param fileMap the filename-to-reference map
* @param executor the executor to use
* @throws IOException on I/O errors
*/
protected void uploadInputFiles(Map<String, String> fileMap, CommandLineExecutor executor) throws IOException {
for (Entry<String, String> entry : fileMap.entrySet()) {
String filename = entry.getKey();
String reference = entry.getValue();
uploadFileFromReference(filename, reference, executor);
}
}
/**
* @param filename the workdir-relative filename to create the remote file as
* @param reference the abstract content reference
* @param executor the command executor to use
* @throws IOException on I/O errors
*/
private void uploadFileFromReference(String filename, String reference, CommandLineExecutor executor) throws IOException {
// TODO could be optimized with streaming pipe-through from DM to target
File tempFile = TempFileServiceAccess.getInstance().createTempFileFromPattern("upload." + filename + "-*.tmp");
tempFile.deleteOnExit(); // safeguard
try {
getDataManagementService().copyReferenceToLocalFile(reference, tempFile, componentContext.getDefaultStorageNodeId());
// monitoringListener.appendUserInformation("Uploading " + filename);
executor.uploadFileToWorkdir(tempFile, filename);
} finally {
FileUtils.deleteQuietly(tempFile);
}
}
/**
* @param filename the workdir-relative filename to copy the remote file from
* @param reference the abstract content reference
* @param executor the command executor to use
* @throws IOException on I/O errors
*/
protected String downloadFileToReference(String filename, CommandLineExecutor executor) throws IOException {
// TODO optimize by adding stream methods
File targetFile = TempFileServiceAccess.getInstance().createTempFileFromPattern("download." + filename + "-*.tmp");
targetFile.deleteOnExit(); // safeguard
try {
executor.downloadFileFromWorkdir(filename, targetFile);
return getDataManagementService().createTaggedReferenceFromLocalFile(targetFile, filename);
} finally {
FileUtils.deleteQuietly(targetFile);
}
}
protected void prepareAndTestEnvironment(CommandLineExecutor executor) throws InterruptedException, IOException {
// FIXME this was originally a setup method; implement actual testing of expected state
log.debug("Environment pre-test starting");
executor.start("echo \"Effective Workdir: `pwd`\"", null);
InputStream ansysStdout = executor.getStdout();
InputStream ansysStderr = executor.getStderr();
final TextStreamWatcher stdoutWatcher =
TextStreamWatcherFactory.create(ansysStdout, new LoggingTextOutReceiver("Environment pre-test Stdout"));
final TextStreamWatcher stderrWatcher =
TextStreamWatcherFactory.create(ansysStderr, new LoggingTextOutReceiver("Environment pre-test Stderr"));
stdoutWatcher.start();
stderrWatcher.start();
executor.waitForTermination();
stdoutWatcher.waitForTermination();
stderrWatcher.waitForTermination();
log.debug("Environment pre-test complete");
}
protected StatefulComponentDataManagementService getDataManagementService() {
return fileReferenceHandler;
}
}