package edu.stanford.nlp.util;
import java.util.*;
import java.io.*;
/**
* Useful methods for running shell commands, getting the process ID, checking
* memory usage, etc.
*
* @author Bill MacCartney
* @author Steven Bethard ({@link #run})
*/
public class SystemUtils {
/**
* Runtime exception thrown by execute.
*/
public static class ProcessException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ProcessException(String string) {
super(string);
}
public ProcessException(Throwable cause) {
super(cause);
}
}
/**
* Start the process defined by the ProcessBuilder, and run until complete.
*
* Process output and errors will be written to System.out and System.err,
* respectively.
*
* @param builder The ProcessBuilder defining the process to run.
*/
public static void run(ProcessBuilder builder) {
run(builder, null, null);
}
/**
* Start the process defined by the ProcessBuilder, and run until complete.
*
* @param builder The ProcessBuilder defining the process to run.
* @param output Where the process output should be written. If null, the
* process output will be written to System.out.
* @param error Where the process error output should be written. If null,
* the process error output will written to System.err.
*/
public static void run(ProcessBuilder builder, Writer output, Writer error) {
try {
Process process = builder.start();
consume(process, output, error);
int result = process.waitFor();
if (result != 0) {
String msg = "process %s exited with value %d";
throw new ProcessException(String.format(msg, builder.command(), result));
}
} catch (InterruptedException e) {
throw new ProcessException(e);
} catch (IOException e) {
throw new ProcessException(e);
}
}
/**
* Helper method that consumes the output and error streams of a process.
*
* This should avoid deadlocks where, e.g. the process won't complete because
* it is waiting for output to be read from stdout or stderr.
*
* @param process A running process.
* @param outputWriter Where to write output. If null, System.out is used.
* @param errorWriter Where to write error output. If null, System.err is used.
*/
private static void consume(Process process, Writer outputWriter, Writer errorWriter)
throws IOException, InterruptedException {
if (outputWriter == null) {
outputWriter = new OutputStreamWriter(System.out);
}
if (errorWriter == null) {
errorWriter = new OutputStreamWriter(System.err);
}
WriterThread outputThread = new WriterThread(process.getInputStream(), outputWriter);
WriterThread errorThread = new WriterThread(process.getErrorStream(), errorWriter);
outputThread.start();
errorThread.start();
outputThread.join();
errorThread.join();
}
/**
* Thread that reads from an Reader and writes to a Writer.
*
* Used as a helper for {@link #consume} to avoid deadlocks.
*/
private static class WriterThread extends Thread {
private Reader reader;
private Writer writer;
public WriterThread(InputStream inputStream, Writer writer) {
this.reader = new InputStreamReader(inputStream);
this.writer = writer;
}
@Override
public void run() {
char[] buffer = new char[4096];
while (true) {
try {
int read = this.reader.read(buffer);
if (read == -1) {
break;
}
this.writer.write(buffer, 0, read);
this.writer.flush();
} catch (IOException e) {
throw new ProcessException(e);
}
Thread.yield();
}
}
}
/**
* Helper class that acts as a output stream to a process
*/
public static class ProcessOutputStream extends OutputStream
{
private Process process;
private Thread outWriterThread;
private Thread errWriterThread;
public ProcessOutputStream(String[] cmd) throws IOException {
this(new ProcessBuilder(cmd), new PrintWriter(System.out), new PrintWriter(System.err));
}
public ProcessOutputStream(String[] cmd, Writer writer) throws IOException {
this(new ProcessBuilder(cmd), writer, writer);
}
public ProcessOutputStream(String[] cmd, Writer output, Writer error) throws IOException {
this(new ProcessBuilder(cmd), output, error);
}
public ProcessOutputStream(ProcessBuilder builder, Writer output, Writer error) throws IOException {
this.process = builder.start();
errWriterThread = new StreamGobbler(process.getErrorStream(), error);
outWriterThread = new StreamGobbler(process.getInputStream(), output);
errWriterThread.start();
outWriterThread.start();
}
public void flush() throws IOException
{
process.getOutputStream().flush();
}
public void write(int b) throws IOException
{
process.getOutputStream().write(b);
}
public void close() throws IOException
{
process.getOutputStream().close();
try {
errWriterThread.join();
outWriterThread.join();
process.waitFor();
} catch (InterruptedException e) {
throw new ProcessException(e);
}
}
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code> array. If there is any regular output or error
* output, it is appended to the given <code>StringBuilder</code>s.
*/
public static void runShellCommand(String[] cmd,
StringBuilder outputLines,
StringBuilder errorLines)
throws IOException {
Process p = Runtime.getRuntime().exec(cmd);
if (outputLines != null) {
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
outputLines.append(line);
}
}
if (errorLines != null) {
BufferedReader err =
new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line;
while ((line = err.readLine()) != null) {
errorLines.append(line);
}
}
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code>. If there is any regular output or error output,
* it is appended to the given <code>StringBuilder</code>s.
*/
public static void runShellCommand(String cmd,
StringBuilder outputLines,
StringBuilder errorLines)
throws IOException {
runShellCommand(new String[] {cmd}, outputLines, errorLines);
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code> array. If there is any regular output, it is
* appended to the given <code>StringBuilder</code>. If there is any error
* output, it is swallowed (!).
*/
public static void runShellCommand(String[] cmd,
StringBuilder outputLines)
throws IOException {
runShellCommand(cmd, outputLines, null);
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code>. If there is any regular output, it is appended
* to the given <code>StringBuilder</code>. If there is any error output, it
* is swallowed (!).
*/
public static void runShellCommand(String cmd,
StringBuilder outputLines)
throws IOException {
runShellCommand(new String[] {cmd}, outputLines, null);
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code> array. If there is any output, it is swallowed
* (!).
*/
public static void runShellCommand(String[] cmd)
throws IOException {
runShellCommand(cmd, null, null);
}
/**
* Runs the shell command which is specified, along with its arguments, in the
* given <code>String</code>. If there is any output, it is swallowed (!).
*/
public static void runShellCommand(String cmd)
throws IOException {
runShellCommand(new String[] {cmd}, null, null);
}
/**
* Returns the process ID, via an awful hack.
*/
public static int getPID() throws IOException {
// note that we ask Perl for "ppid" -- process ID of parent -- that's us
String[] cmd =
new String[] {"perl", "-e", "print getppid() . \"\\n\";"};
StringBuilder out = new StringBuilder();
runShellCommand(cmd, out);
return Integer.parseInt(out.toString());
}
/**
* Returns the process ID, via an awful hack, or else -1.
*/
public static int getPIDNoExceptions() {
try {
return SystemUtils.getPID();
} catch (IOException e) {
return -1;
}
}
/**
* Returns the number of megabytes (MB) of memory in use.
*/
public static int getMemoryInUse() {
Runtime runtime = Runtime.getRuntime();
long mb = 1024 * 1024;
long total = runtime.totalMemory();
long free = runtime.freeMemory();
return (int) ((total - free) / mb);
}
public static void main(String[] args) throws Exception {
StringBuilder out = new StringBuilder();
runShellCommand("date", out);
System.out.println("The date is " + out);
int pid = getPID();
System.out.println("The PID is " + pid);
System.out.println("The memory in use is " + getMemoryInUse() + "MB");
List<String> foo = new ArrayList<>();
for (int i = 0; i < 5000000; i++) {
foo.add("0123456789");
}
System.out.println("The memory in use is " + getMemoryInUse() + "MB");
foo = null;
System.gc();
System.out.println("The memory in use is " + getMemoryInUse() + "MB");
}
}