Commit 1d65fd37 by Chris Coughlin

Enhanced ExternalROIFinder's pipe API - now handles log messages from external process

parent e662a31a
......@@ -29,6 +29,8 @@ import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* ExternalROIFinder - demonstrates one way to call external ROI applications by redirecting their standard input
......@@ -54,6 +56,11 @@ public class ExternalROIFinder implements ROIFinder {
*/
private TimeUnit timeoutUnits = TimeUnit.SECONDS;
private Pattern isMyriadMsgPattern = Pattern.compile("^Myriad:.*", Pattern.CASE_INSENSITIVE);
private Pattern isROIPattern = Pattern.compile("(?<true>true)|(?<false>false)", Pattern.CASE_INSENSITIVE);
private Pattern isLogPattern = Pattern.compile("(?<debug>\\[*debug\\]*)|(?<info>\\[*info\\]*)|(?<warn>\\[*warn\\]*)|(?<error>\\[*error\\]*)",
Pattern.CASE_INSENSITIVE);
/**
* Constructor for an empty ROIFinder (serialization)
*/
......@@ -97,10 +104,18 @@ public class ExternalROIFinder implements ROIFinder {
stdin.close();
String line;
while ((line = in.readLine()) != null) {
log.info("External Response: " + line);
line = line.toLowerCase();
if (line.startsWith("myriad:")) {
result = line.contains("true");
if (isMyriadMessage(line)) {
// Found an output message directed to Myriad
if (isLogMessage(line)) {
// Appears to be a log message - determine type and file appropriately
handleLogMessage(line);
} else if (isROIMessage(line)) {
// Appears to be an ROI determination
result = ROIfound(line);
} else {
// Handle unknown messages with a warning
log.warn("Unknown message received: " + line);
}
}
}
boolean completed = processRunner.getProcess().waitFor(timeout, timeoutUnits);
......@@ -112,11 +127,93 @@ public class ExternalROIFinder implements ROIFinder {
} catch (InterruptedException ie) {
log.warn(ie.getMessage());
}
log.info("Data contains flaw: " + result);
return result;
}
/**
* Determines whether the specified message appears to be directed to Myriad, i.e. starts with "Myriad:".
* @param input message to check
* @return true if the message matches the regex ^Myriad (case insensitive).
*/
boolean isMyriadMessage(String input) {
return isMyriadMsgPattern.matcher(input).matches();
}
/**
* Determines whether the specified message appears to be a logging message, i.e. a Myriad message with one of
* DEBUG, INFO, WARN, or ERROR (case insensitive) defined.
* @param input message to check
* @return true if the message matches the regex ^Myriad (case insensitive).
*/
boolean isLogMessage(String input) {
return isMyriadMessage(input) && isLogPattern.matcher(input).find();
}
/**
* Determines whether the specified message appears to be a Region Of Interest determination, i.e. starts with
* "Myriad:" and has one of TRUE or FALSE (case insensitive).
* @param input message to check
* @return true if the message appears to be an ROI call
*/
boolean isROIMessage(String input) {
return isMyriadMessage(input) && isROIPattern.matcher(input).find();
}
/**
* Determines whether the specified message indicates a Region of Interest was found by the external ROI finder.
* @param input message to check
* @return true if the external ROI finder found an ROI, false if not or if the message was invalid
*/
boolean ROIfound(String input) {
if (isMyriadMessage(input)) {
Matcher matcher = isROIPattern.matcher(input);
if (matcher.find()) {
String group = matcher.group();
if (group != null && group.length() > 0) {
String g = group.toLowerCase();
if (g.contains("true")) {
return true;
} else if (g.contains("false")) {
return false;
}
}
}
}
return false;
}
/**
* Determines the type of log message and records it in Myriad's log, e.g. a string of the form
*
* Myriad: 10:13:07,341 |-WARN in c.q.l.core.rolling.TimeBasedRollingPolicy@1165897474 - Subcomponent did not start.
*
* is recorded as a warning in Myriad's log file.
*
* @param input message to check
*/
private void handleLogMessage(String input) {
if (isMyriadMessage(input)) {
Matcher matcher = isLogPattern.matcher(input);
if (matcher.find()) {
String group = matcher.group();
if (group != null && group.length() > 0) {
String g = group.toLowerCase();
String msg = "External process: " + input.substring(matcher.start());
if (g.contains("debug")) {
log.debug(msg);
} else if (g.contains("info")) {
log.info(msg);
} else if (g.contains("warn")) {
log.warn(msg);
} else if (g.contains("error")) {
log.error(msg);
}
}
}
}
}
/**
* Returns the number of seconds, milliseconds, etc. before the external process times out.
*
* @return value of timeout
......
/*
* com.emphysic.myriad.core.data.roi.ExternalROIFinderTest
*
* Copyright (c) 2016 Emphysic LLC.
* Copyright (c) 2017 Emphysic LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -32,8 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
......@@ -118,6 +117,85 @@ public class ExternalROIFinderTest {
assertModelsEqual(erf, read2);
}
@Test
public void testIsMyriadMessage() throws Exception {
String[] shouldMatch = {
"MYRIAD:123",
"Myriad: Purple Monkey Dishwasher",
"myriad:",
"MYRIAD: "
};
String[] shouldnotMatch = {
"MRYIAD:",
" Myriad:",
"myriad",
" MYRIAD:"
};
ExternalROIFinder tst = new ExternalROIFinder();
for (String s : shouldMatch) {
assertTrue(tst.isMyriadMessage(s));
}
for (String s : shouldnotMatch) {
assertFalse(tst.isMyriadMessage(s));
}
}
@Test
public void testIsLogMessage() throws Exception {
String logMessages[] = {
"10:13:07,341 |-ERROR in c.q.l.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy",
"10:13:07,341 |-WARN in c.q.l.core.rolling.TimeBasedRollingPolicy@1165897474 - Subcomponent did not start.",
"10:13:07,359 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG",
"log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.",
"[debug] execute contextualize"
};
ExternalROIFinder tst = new ExternalROIFinder();
for (String s : logMessages) {
assertFalse(tst.isLogMessage(s));
assertTrue(tst.isLogMessage("Myriad:" + s));
}
}
@Test
public void testIsROIMessage() throws Exception {
String roiMessages[] = {
"TRUE",
"FALSE",
" TrUe Blue",
"~~~~ false pretense ~~~"
};
ExternalROIFinder tst = new ExternalROIFinder();
for (String s : roiMessages) {
assertFalse(tst.isROIMessage(s));
assertTrue(tst.isROIMessage("Myriad: " + s));
}
}
@Test
public void testROIFound() throws Exception {
String trueMessages[] = {
"TRUE",
"true",
"!!!!True!!!!",
" True"
};
String falseMessages[] = {
"FALSE",
"false",
">>> False! <<<",
"--- FALSE"
};
ExternalROIFinder tst = new ExternalROIFinder();
for (String s : trueMessages) {
assertFalse(tst.ROIfound(s));
assertTrue(tst.ROIfound("myriad: " + s));
}
for (String s : falseMessages) {
assertFalse(tst.ROIfound(s));
assertFalse(tst.ROIfound("myriad: " + s));
}
}
/**
* Verify that two external process configurations are equivalent.
* @param expected expected configuration
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment