gov.sandia.cognition.io.ProcessLauncher Maven / Gradle / Ivy
/*
* File: ProcessLauncher.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Framework Lite
*
* Copyright September 20, 2006, Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000, there is a non-exclusive license for use of this work by
* or on behalf of the U.S. Government. Export of this program may require a
* license from the United States Government. See CopyrightHistory.txt for
* complete details.
*
*
*/
package gov.sandia.cognition.io;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Launches a process as a separate thread and monitors the stdout and stderr,
* throwing events when they update and exit
*
* @author Kevin R. Dixon
* @since 1.0
*
*/
public class ProcessLauncher
extends Thread
{
/**
* Actual command line passed to the Runtime.exec() command
*/
private String actualCommand;
/**
* Internal process that is spawned from the command
*/
private Process process;
/**
* Listeners that are interested in the events fired by this object
*/
private LinkedList listeners;
/**
* Thread that consumes stdout
*/
private StreamGobbler stdout;
/**
* Thread that consumes stderr
*/
private StreamGobbler stderr;
/**
* Thread class that asynchronously process the outputs from the process,
* and throws events when a new line is encountered
*/
private class StreamGobbler extends Thread
{
/**
* Stream to monitor
*/
InputStream stream;
/**
* Type of events to fire
*/
ProcessLauncherEvent.EventType type;
/**
* Creates a new instance of StreamGobbler
* @param stream
* Stream to monitor
* @param type
* Type of events to fire
*/
StreamGobbler(
InputStream stream,
ProcessLauncherEvent.EventType type )
{
this.stream = stream;
this.type = type;
}
/**
* {@inheritDoc}
*/
@Override
public void run()
{
BufferedReader stdInput = new BufferedReader(
new InputStreamReader( this.stream ) );
try
{
String line;
while ((line = stdInput.readLine()) != null)
{
ProcessLauncher.this.fireEvent(
new ProcessLauncherEvent( this.type, line, null ) );
}
}
catch (Exception e)
{
throw new RuntimeException( e );
}
}
}
/**
* Creates a new instance of ProcessLauncher
* @param command
* String command, and all its arguments, to launch
*/
public ProcessLauncher(
String command )
{
String osName = System.getProperty( "os.name" );
if (osName.equals( "Windows XP" ))
{
this.setActualCommand( "cmd.exe /C \"" + command + "\"" );
}
else
{
this.setActualCommand( command );
}
this.setProcess( null );
this.stdout = null;
this.stderr = null;
this.listeners = new LinkedList();
}
/**
* {@inheritDoc}
*/
@Override
public void run()
{
try
{
Logger.getLogger(ProcessLauncher.class.getName()).info("Executing: " + this.getActualCommand());
this.setProcess( Runtime.getRuntime().exec( this.getActualCommand() ) );
this.stdout = new StreamGobbler(
this.getProcess().getInputStream(), ProcessLauncherEvent.EventType.STDOUT );
this.stdout.start();
this.stderr = new StreamGobbler(
this.getProcess().getErrorStream(), ProcessLauncherEvent.EventType.STDERR );
this.stderr.start();
this.getProcess().waitFor();
this.fireEvent( new ProcessLauncherEvent(
ProcessLauncherEvent.EventType.FINISHED, null, this.getProcess() ) );
}
catch (Exception e)
{
throw new RuntimeException( e );
}
}
/**
* Adds the listener to the event queue
* @param listener
* Object that wishes to receive update events
*/
public void addListener(
ProcessLauncherListener listener )
{
this.listeners.add( listener );
}
/**
* Removes the given object from the event queue
* @param listener
* Object that no longer wants to receive update events
*/
public void removeListener(
ProcessLauncherListener listener )
{
this.listeners.remove( listener );
}
/**
* Passes the event to all the subscribed listeners
* @param event
*/
private void fireEvent(
ProcessLauncherEvent event )
{
for (ProcessLauncherListener listener : this.listeners)
{
listener.processLauncherEvent( event );
}
}
/**
* Stops the process. The does not work on Windows, as we have to
* envelope the desired command using "cmd.exe /c". So the stop process
* call actually cancels cmd.exe, and happily lets the desired command
* continue running
*/
public void stopProcess()
{
if (this.getProcess() != null)
{
this.getProcess().destroy();
this.setProcess( null );
}
if (this.stdout != null)
{
this.stdout.interrupt();
this.stdout = null;
}
if (this.stderr != null)
{
this.stderr.interrupt();
this.stdout = null;
}
this.interrupt();
}
/**
* Getter for process
* @return
* Internal process that is spawned from the command
*/
public Process getProcess()
{
return this.process;
}
/**
* Setter for process
* @param process
* Internal process that is spawned from the command
*/
protected void setProcess(
Process process )
{
this.process = process;
}
/**
* Getter for actualCommand
* @return
* Actual command line passed to the Runtime.exec() command
*/
public String getActualCommand()
{
return this.actualCommand;
}
/**
* Setter for actualCommand
* @param actualCommand
* Actual command line passed to the Runtime.exec() command
*/
protected void setActualCommand(
String actualCommand )
{
this.actualCommand = actualCommand;
}
}