/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.integration.regression;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import com.google.common.collect.ImmutableList;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.component.OpenGammaComponentServer;
/**
* Runs {@link OpenGammaComponentServer} in a new process
. */
public final class ServerProcess implements AutoCloseable {
/** The server process. */
private final Process _process;
private ServerProcess(Process process) {
_process = process;
}
// TODO memory options
/**
* Creates a new process that runs {@link OpenGammaComponentServer}.
* @param workingDir The working directory for the process
* @param classpath The classpath argument for the process
* @param configFile The location of the server configuration file
* @param propertyOverrides Properties to override values in the configuration
* @param logbackConfig Property to set the logback configuration
* @return The process
*/
public static ServerProcess start(String workingDir,
String classpath,
String configFile,
Properties propertyOverrides,
String logbackConfig) {
ImmutableList.Builder<String> commandBuilder = ImmutableList.builder();
commandBuilder.add("java",
logbackConfig,
"-cp",
classpath,
"-Xmx2g",
"-XX:MaxPermSize=256M",
"com.opengamma.component.OpenGammaComponentServer",
configFile);
// can override properties in the config on the command line with prop1=value1 prop2=value2 ...
for (Map.Entry<Object, Object> entry : propertyOverrides.entrySet()) {
commandBuilder.add(entry.getKey() + "=" + entry.getValue());
}
ProcessBuilder processBuilder = new ProcessBuilder(commandBuilder.build()).directory(new File(workingDir));
Process process;
try {
process = processBuilder.start();
} catch (IOException e) {
throw new OpenGammaRuntimeException("Failed to start server process", e);
}
BlockingQueue<Boolean> startupQueue = new ArrayBlockingQueue<>(1);
consumeStream(process.getInputStream(), OpenGammaComponentServer.STARTUP_COMPLETE_MESSAGE, startupQueue, true, System.out);
consumeStream(process.getErrorStream(), OpenGammaComponentServer.STARTUP_FAILED_MESSAGE, startupQueue, false, System.err);
Boolean startupSuccess;
try {
// TODO timeout mechanism in case the server dies and doesn't log correctly. timer task that interrupts this thread?
startupSuccess = startupQueue.take();
} catch (InterruptedException e) {
// not going to happen
throw new OpenGammaRuntimeException("unexpected exception", e);
}
if (!startupSuccess) {
throw new OpenGammaRuntimeException("startup failed, aborting");
}
return new ServerProcess(process);
}
/**
* Starts a thread which consumes a stream line by line and puts a value onto a queue when it encounters
* a line starting with a particular value. If an exception occurs a value of false it put onto the queue.
* Every line read from the stream is written to a {@link PrintStream}.
* @param stream The stream to consume
* @param value The trigger value - the stream line must <em>startWith</em> this string
* @param queue The queue
* @param signalValue The value to put onto the queue when line is encountered in the stream
* @param output Every line read from the stream is written to this print stream
*/
private static void consumeStream(final InputStream stream,
final String value,
final BlockingQueue<Boolean> queue,
final Boolean signalValue,
final PrintStream output) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
String nextLine;
while ((nextLine = reader.readLine()) != null) {
output.println(nextLine);
if (nextLine.startsWith(value)) {
queue.put(signalValue);
}
}
} catch (IOException e) {
e.printStackTrace();
try {
queue.put(false);
} catch (InterruptedException e1) {
// not going to happen
}
} catch (InterruptedException e) {
// not going to happen
}
}
});
thread.setDaemon(true);
thread.start();
}
@Override
public void close() {
_process.destroy();
}
}