/* Schmant, the build tool, http://www.schmant.org
* Copyright (C) 2007-2009 Karl Gustafsson
*
* This file is a part of Schmant.
*
* Schmant is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Schmant is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.schmant.support.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This is an abstract base class that may be inherited by
* {@link ProcessOutputListener}:s. This class implements the polling mechanism
* for reading output from a process' stdout or stderr channels (one of them).
* Subclasses implement a mechanism for storing that output somewhere.
* <p>
* If the monitored process generates a lot of output and the polling interval
* is set too high, the monitored process can freeze because it has filled its
* output buffer. In that case, set a shorter polling interval.
* @author Karl Gustafsson
* @since 0.5
*/
public abstract class AbstractProcessOutputListener implements ProcessOutputListener
{
/** The default poll interval. */
public static final long DEFAULT_POLL_INTERVAL = 200L;
private BufferedReader m_inReader;
/** The poll interval in milliseconds. */
private final long m_pollInterval;
private final AtomicBoolean m_done = new AtomicBoolean(false);
/**
* Create a process output listener with the default polling interval.
* {@link #setStream(InputStream)} must be called before using this object.
*/
protected AbstractProcessOutputListener()
{
m_pollInterval = DEFAULT_POLL_INTERVAL;
}
/**
* Create a process output listener with the supplied polling interval.
* {@link #setStream(InputStream)} must be called before using this object.
* @param pollInterval The polling interval in milliseconds.
*/
protected AbstractProcessOutputListener(long pollInterval)
{
m_pollInterval = pollInterval;
}
/**
* Must be set before the thread is started.
* @param is The stream to listen to.
*/
public void setStream(InputStream is)
{
m_inReader = new BufferedReader(new InputStreamReader(is));
}
/**
* Set by the owning thread when this thread should shut down.
*/
public void flagDone()
{
m_done.set(true);
}
/**
* Subclasses override this if they want to do any initialization when the
* listener thread is starting.
* <p>
* Long running initialization should probably be performed when the output
* listener is created instead since the process to listen to will be
* running when this method is called.
* <p>
* This implementation does nothing.
*/
protected void setup()
{
// Nothing
}
/**
* Subclasses implement this to handle one line of output from the monitored
* process.
* @param l The line.
*/
protected abstract void processLine(String l);
/**
* This closes the input stream. Subclasses should make sure to call {@code
* super.close()} if overriding this method.
* @throws IOException On I/O errors
*/
protected void close() throws IOException
{
m_inReader.close();
}
public void run()
{
try
{
setup();
while (true)
{
try
{
while (m_inReader.ready())
{
processLine(m_inReader.readLine());
}
}
catch (IOException e)
{
e.printStackTrace();
}
if (m_done.get())
{
// Ok, we're done
return;
}
try
{
Thread.sleep(m_pollInterval);
}
catch (InterruptedException e)
{
// Clear interrupted flag
Thread.interrupted();
// Die
return;
}
}
}
finally
{
try
{
close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
/**
* This may be implemented by subclasses to return the entire output of the
* monitored process if they keep it in memory.
* @return {@code null}. Override for other behavior.
*/
public String getOutput()
{
return null;
}
}