/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
+------------------------------+
File: StreamGobbler.java
Originator: Allen Baker (2007.09.09 21:19)
LayoutRev: 5
================================================================================================================================== */
/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;
/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
/*::
======================================================================================================================== *//**
Instances of this class run as independent threads, intercept an InputStream, and redirect it to cOut and optionally to
another OutputStream.<P>
I took the idea for this class, information about how to use it, and the term "StreamGobbler" from an article called
"When Runtime.exec() won't", which I found at:
<A href="http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html">
When Runtime.exec() won't
</A>
StreamGobblers are particularly useful for intercepting and emptying stdout and stderr when an external program is
executed from inside a Java program. (From the external program's perspective, stdout and stderr are OutputStreams. But
from the perspective of the Java program that is running the external program, the external program's stdout and stderr
are InputStreams.) The reason StreamGobbler is useful in this scenario is because some native platforms only provide a
limited buffer size for the standard input and output streams, and failure to promptly write the input stream or read
the output stream of the external program may cause the external program to block, and even deadlock. I can personally
testify that this happens and that without the information found in the article cited above, these bugs are very hard to
figure out.<P>
Here is an example of how to use StreamGobbler when executing external programs from Java:
<BLOCKQUOTE>
<PRE id="unindent">
public static <Type> int runExternalProgram(Type pCommand) throws Exception
{
XString cmd = XString.toXString(pCommand);
Process proc = null;
Runtime rt = Runtime.getRuntime();
=======================================================================================
The commandline to run the program is dependent on the os.
---------------------------------------------------------------------------------------
switch (OS())
{
case Util.OS_WINDOWS9X:
proc = rt.exec("command.com /c " + cmd);
break;
case Util.OS_WINDOWS:
proc = rt.exec("cmd.exe /c " + cmd);
break;
case Util.OS_UNIX:
proc = rt.exec(cmd.string());
break;
}
=======================================================================================
Create an FileOutputStream to send error messages to
---------------------------------------------------------------------------------------
OutputStream errorFile = new FileOutputStream("/data/ERRORS.txt");
=======================================================================================
Send stderr messages to cOut and to errorFile
---------------------------------------------------------------------------------------
StreamGobbler stderrGobbler = new StreamGobbler(proc.getErrorStream(), " stderr", errorFile);
stderrGobbler.start();
=======================================================================================
Send stdout messages to cOut
---------------------------------------------------------------------------------------
StreamGobbler stdoutGobbler = new StreamGobbler(proc.getInputStream(), " stdout");
stdoutGobbler.start();
======================================================================================
Wait until all streams have been gobbled up
--------------------------------------------------------------------------------------
StreamGobbler.waitForGobbling();
======================================================================================
Wait for the process to exit and then return its exit code.
--------------------------------------------------------------------------------------
return proc.waitFor();
}
</PRE>
</BLOCKQUOTE>
<P>
<DL>
<DT>
<B>
Example usage:
</B>
<DD>
<BLOCKQUOTE>
<PRE id="unindent">
no example provided
</PRE>
</BLOCKQUOTE>
</DD>
</DT>
<DT>
<B>
View Source:
</B>
<DD>
<A href="StreamGobbler.java.html">
StreamGobbler.java
</A>
</DD>
</DT>
<DT>
<B>
Author:
</B>
<DD>
<A href="mailto:sourcecode.v01@cosmicabyss.com">
Allen Baker
</A>
</DD>
</DT>
</DL>
*//*
======================================================================================================================== */
public class StreamGobbler extends Thread
{
/*: :METHOD:000:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method creates a StreamGobbler class object that gobbles the input from a specified
InputStream, gives a specified name to the InputStream being gobbled, and does not redirect the
InputStream to an OutputStream.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#000">View source</A>
@param
pInputStream is the InputStream to intercept and gobble input data from.
@param
pInputStreamName is the name of the InputStream that is being gobbled. This name is attached to
any input data that is echoed to cOut.
*//*
---------------------------------------------------------------------------------------------------- */
public StreamGobbler(InputStream pInputStream, String pInputStreamName)
{
this(pInputStream, pInputStreamName, null);
}
/*: :METHOD:001:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method creates a StreamGobbler class object that gobbles the input from a specified
InputStream, gives a specified name to the InputStream being gobbled, and redirects the InputStream
to a specified OutputStream.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#001">View source</A>
@param
pInputStream is the InputStream to intercept and gobble input data from.
@param
pInputStreamName is the name of the InputStream that is being gobbled. This name is attached to
any input data that is echoed to cOut.
@param
pOutputStream is the OutputStream to which all data intercepted from the InputStream is sent.
*//*
---------------------------------------------------------------------------------------------------- */
public StreamGobbler(InputStream pInputStream, String pInputStreamName, OutputStream pOutputStream)
{
this.iInputStream = pInputStream;
this.iInputStreamName = pInputStreamName;
this.iOutputStream = pOutputStream;
}
/*: :METHOD:002:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method is the action component of a StreamGobbler instance and it runs on its own thread.
(Every StreamGobbler has its own thread.) It is the code that does the actual gobbling of
InputStream data. If an OutputStream was assigned to this StreamGobbler instance, then this method
sends the data it intercepts from the InputStream to that OutputStream. It also tags all intercepted
data with the InputStream name and sends it to cOut. It runs until there is no more data to be read
from the InputStream and then exits which kills the thread it is running on.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#002">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
public void run()
{
inc();
try
{
PrintWriter pw = null;
if (iOutputStream != null) pw = new PrintWriter(iOutputStream);
InputStreamReader isr = new InputStreamReader(iInputStream);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
{
if (pw != null)
{
pw.println(line);
}
// cOut.println(iInputStreamName + ": " + line);
System.out.println(line);
}
if (pw != null)
{
pw.flush();
}
dec();
}
catch (IOException ioe)
{
dec();
ioe.printStackTrace();
}
}
/*: :METHOD:003:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method sets the ConsoleStream for this object.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#003">View source</A>
@return
A reference to this object
@param
pConsole is a ConsoleStream through which this objects sends its output.
*//*
---------------------------------------------------------------------------------------------------- */
public StreamGobbler setOut(ConsoleStream pConsole)
{
cOut = pConsole;
return this;
}
/*: :METHOD:004:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This boilerplate method is used for testing an instantiated object of this class and may include any
code the developer chooses.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#004">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
public StreamGobbler test() throws Exception
{
cOut.titledPrintf
(
"\"HELLO WORLD!\"",
"%s %s %s",
"I'm an object of the", CLASS_NAME, "class, and I approved this message."
);
return this;
}
/*:.
==============================================================================================================
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Protected ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
============================================================================================================== */
/*:.
==============================================================================================================
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Private ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
============================================================================================================== */
/*.
==========================================================================================
Class Constants
CLASS_NAME:
The name of this class
DFLT_LINE_LEN:
The default line length for word wrapping
------------------------------------------------------------------------------------------ */
private static final XString CLASS_NAME = new XString(StreamGobbler.class.getName());
private static final int DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
/*.
==========================================================================================
Class variables
cOut:
Console output.
cCount:
The number of StreamGobblers currently running.
cStarted:
A flag indicating if any StreamGobblers have been started or not.
------------------------------------------------------------------------------------------ */
private static ConsoleStream cOut = ConsoleStream.getSingleton();
private static int cCount = 0;
private static boolean cStarted = false;
/*.
==========================================================================================
Instance variables
iInputStream:
The stream to be gobbled up
iInputStreamName:
The name of the stream to be gobbled up. This is just used for identifying the
source stream when the data being gobbled is sent to cOut.
iOutputStream:
The stream to which gobbled data is sent (redirected). This stream is in addition to
cOut. While all gobbled data is always sent to cOut, iOutputStream may be set to
either an actual OutputStream or to null.
------------------------------------------------------------------------------------------ */
private InputStream iInputStream = null;
private String iInputStreamName = null;
private OutputStream iOutputStream = null;
/*: :METHOD:005:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method increments the count of threads that are gobbling
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#005">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
private static synchronized void inc()
{
cCount++;
cStarted = true;
}
/*: :METHOD:006:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method decrements the count of threads that are gobbling
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#006">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
private static synchronized void dec()
{
cCount--;
}
/*: :METHOD:007:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method says if any threads are currently gobbling.
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#007">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
private static synchronized boolean stillGobbling()
{
return (cCount > 0);
}
/*: :METHOD:008:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method says how many threads are gobbling
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#008">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
private static synchronized int count()
{
return (cCount);
}
/*: :METHOD:009:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method says if any threads have started gobbling yet
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#009">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
private static boolean started()
{
return (cStarted);
}
/*:.
==============================================================================================================
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Inner Classes ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
============================================================================================================== */
/*:.
==============================================================================================================
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Public Static Methods ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
============================================================================================================== */
/*: :METHOD:010:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method doesn't return until all gobbling has stopped
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#010">View source</A>
*//*
---------------------------------------------------------------------------------------------------- */
public static void waitForGobbling()
{
while ( ! StreamGobbler.started())
{
Util.sleep(1);
}
while (StreamGobbler.stillGobbling())
{
Util.sleep(1);
}
}
/*: :METHOD:011:BOOKMARK:
====================================================================================================
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
==================================================================================================== *//**
This method allows this class file to be unit tested as a standalone application. It's the method
that's called when the class is invoked from the command line by using the java application launcher
- "java". Main() is not a required method, but the practice of putting one in each class and
wrapping class test code within it allows easy unit testing of the class; and main does not need to
be removed when testing is complete.
<P>
<DL>
<DT>
<B>
Command line usage:
</B>
<DD>
Java cosmicabyss.com.lib.StreamGobbler
</DD>
</DT>
</DL>
<P><B>Implementation: </B><A HREF="StreamGobbler.java.html#011">View source</A>
@param
pArgs contains the command line arguments with which this class was invoked as an application.
*//*
---------------------------------------------------------------------------------------------------- */
public static void main(String[] pArgs) throws Exception
{
/*.
==========================================================================================
Greetings !
------------------------------------------------------------------------------------------ */
cOut.banner(CLASS_NAME);
/*.
==========================================================================================
Create an object and send its output to the ConsoleStream
------------------------------------------------------------------------------------------ */
XString cmd = XString.toXString("dir *.java");
Process proc = null;
Runtime rt = Runtime.getRuntime();
/*.
==========================================================================================
The commandline to run the program is dependent on the os.
------------------------------------------------------------------------------------------ */
switch (Util.OS())
{
case Util.OS_WINDOWS9X:
proc = rt.exec("command.com /c " + cmd);
break;
case Util.OS_WINDOWS:
proc = rt.exec("cmd.exe /c " + cmd);
break;
case Util.OS_UNIX:
proc = rt.exec(cmd.string());
break;
}
/*.
==========================================================================================
Send stderr messages to cOut
------------------------------------------------------------------------------------------ */
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), " stderr");
errorGobbler.start();
/*.
==========================================================================================
Send stdout messages to cOut
------------------------------------------------------------------------------------------ */
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), " stdout");
outputGobbler.start();
/*.
==========================================================================================
Wait until all streams have been gobbled up
------------------------------------------------------------------------------------------ */
StreamGobbler.waitForGobbling();
/*.
==========================================================================================
Wait for the process to exit and then return its exit code.
------------------------------------------------------------------------------------------ */
cOut.println("Process exited with return code: " + proc.waitFor());
}
} // class StreamGobbler
/*:.
==============================================================================================================
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Notes ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
============================================================================================================== */
Copyright © 2012 Allen Baker
|