/* MP5LocalProcessController.java created 2008-02-18
*
*/
package org.signalml.method.mp5;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.signalml.method.ComputationException;
import org.signalml.method.MethodExecutionTracker;
import static org.signalml.app.util.i18n.SvarogI18n._;
/** MP5LocalProcessController
*
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public class MP5LocalProcessController {
protected static final Logger logger = Logger.getLogger(MP5LocalProcessController.class);
private static final String ERROR = "ERROR";
private static final Pattern TESTED_MESSAGE_PATTERN = Pattern.compile("^TESTED\\s*([0-9]+)$$");
private static final Pattern ATOM_MESSAGE_PATTERN = Pattern.compile("^ATOM\\s*([0-9]+)\\s*([0-9]+)\\s*([0-9.]+)\\s*([0-9.]+)$");
private static final Pattern CHANNEL_MESSAGE_PATTERN = Pattern.compile("^CHANNEL\\s*([0-9]+)$");
private static final Pattern OFFSET_MESSAGE_PATTERN = Pattern.compile("^OFFSET\\s*([0-9]+)$");
private static final Pattern START_MESSAGE_PATTERN = Pattern.compile("^START\\s*([0-9]+)\\s*([0-9]+)\\s*(-?[0-9]+)\\s*(-?[0-9.]+)$");
private static final Pattern END_MESSAGE_PATTERN = Pattern.compile("^END$");
public boolean executeProcess(File workingDirectory, String mp5ExecutablePath, File configFile, MethodExecutionTracker tracker) throws ComputationException {
ProcessBuilder pb = new ProcessBuilder();
pb.command(
mp5ExecutablePath,
"-x",
configFile.getAbsolutePath()
);
pb.directory(workingDirectory);
pb.redirectErrorStream(true);
logger.debug("Using mp5 working directory [" + pb.directory().getAbsolutePath() + "]");
List<String> command = pb.command();
for (String s : command) {
logger.debug("Using mp5 command [" + s + "]");
}
Process process;
try {
process = pb.start();
logger.debug("Process started");
} catch (IOException ex) {
throw new ComputationException(ex);
}
tracker.setMessage(_("Calculating"));
BufferedReader feedbackReader = null;
try {
feedbackReader = new BufferedReader(new InputStreamReader(process.getInputStream()), 1);
String line;
boolean msgUnderstood;
do {
line = feedbackReader.readLine(); // XXX this may prevent prompt abort, rethink
logger.debug("Process returned line [" + (line != null ? line : "(null)") + "]");
if (line != null && !line.isEmpty()) {
line = line.trim();
if (!line.isEmpty()) {
try {
msgUnderstood = processMessage(line, tracker);
} catch (ComputationException ex) {
logger.debug("Execution exception");
process.destroy();
try {
process.waitFor();
} catch (InterruptedException ex2) {
// ignore
}
logger.debug("Process terminated");
throw ex;
}
if (!msgUnderstood) {
logger.warn("Message line not understood [" + line + "]");
}
}
}
if (tracker.isRequestingAbort() || tracker.isRequestingSuspend()) {
logger.debug("Termination request received");
process.destroy();
try {
process.waitFor();
} catch (InterruptedException ex2) {
// ignore
}
logger.debug("Process terminated");
return false;
}
} while (line != null);
} catch (IOException ex) {
logger.error("IOException reading from report stream", ex);
throw new ComputationException(ex);
} finally {
if (feedbackReader != null) {
try {
feedbackReader.close();
} catch (IOException ex) {
}
}
}
try {
process.waitFor();
} catch (InterruptedException e) {
// ignore
}
// end of the process (should we ignore the last message)
int exitValue = process.exitValue();
logger.debug("Process may have finished, exit code is [" + exitValue + "]");
if (exitValue != 0) {
logger.warn("MP5 process exited with an error [" + exitValue + "]");
throw new ComputationException("error.mp5.exitedWithErrorCode", new Object[] { exitValue });
}
return true;
}
private boolean processMessage(String line, MethodExecutionTracker tracker) throws ComputationException {
if (line.startsWith(ERROR)) {
String[] parts = line.split("\\s+");
if (parts.length < 2) {
return false;
}
String[] args = Arrays.copyOfRange(parts, 2, parts.length);
throw new ComputationException(parts[1], args);
}
Matcher matcher;
matcher = TESTED_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
int done = Integer.parseInt(matcher.group(1));
tracker.setTicker(3, done);
return true;
}
matcher = ATOM_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
double done;
done = Double.parseDouble(matcher.group(4));
int dictionaryCnt = Integer.parseInt(matcher.group(2));
synchronized (tracker) {
tracker.setTicker(2, (int) Math.round(done * 100));
tracker.setTickerLimit(3, dictionaryCnt);
tracker.setTicker(3, 0);
}
return true;
}
matcher = OFFSET_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
// do nothing
return true;
}
matcher = CHANNEL_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
int channel = Integer.parseInt(matcher.group(1));
tracker.setTicker(1, channel);
return true;
}
matcher = START_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
int channelCount = Integer.parseInt(matcher.group(2));
synchronized (tracker) {
int[] limits = tracker.getTickerLimits();
tracker.setTickerLimits(new int[] { limits[0], channelCount, 100 * 100, 1 });
int[] tickers = tracker.getTickers();
tracker.setTickers(new int[] { tickers[0], 0, 0, 0 });
}
return true;
}
matcher = END_MESSAGE_PATTERN.matcher(line);
if (matcher.matches()) {
synchronized (tracker) {
int[] tickerLimits = tracker.getTickerLimits();
int[] tickers = tracker.getTickers();
tracker.setTickers(new int[] { tickers[0], tickerLimits[1], tickerLimits[2], tickerLimits[3] });
}
return true;
}
return false;
}
}