/******************************************************************************* * Copyright (c) 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - Jeff Briggs, Henry Hughes, Ryan Morse *******************************************************************************/ package org.eclipse.linuxtools.systemtap.structures.runnable; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.eclipse.linuxtools.systemtap.structures.listeners.IGobblerListener; /** * A separate thread to listen to an InputStream and pull all the data * out of it. When data is found a new event is fired share the data with * any <code>IDataListener</code> that is listening. * @author Ryan Morse */ public class StreamGobbler implements Runnable { public StreamGobbler(InputStream is) { if(null != is) { this.is = is; line = new StringBuilder(); listeners = new ArrayList<>(); } } /** * Spawns the new thread that this class will run in. From the Runnable * interface spawning the new thread automatically calls the run() method. * This must be called by the implementing class in order to start the * StreamGobbler. */ //Make sure to call this method to start the StreamGobbler public void start() { reader = new Thread(this, "StreamGobbler"); //$NON-NLS-1$ reader.start(); } /** * Checks to see if the gobbler is still running. * @return boolean representing whether or not it is sill running */ public boolean isRunning() { return (null != reader); } /** * The main method of this class. It monitors the provided thread to see * when new data is available and then appends it to its current list of * data. When a new line is read it will fire a DataEvent for listeners * to get a hold of the data. */ @Override public void run() { if (reader != Thread.currentThread()) return; try { int val = is.read(); while(val != -1) { line.append((char)val); if ('\n' == val) this.fireNewDataEvent(); val = is.read(); } } catch (IOException ioe) {} // If stream closed before thread shuts down } /** * Stops the gobbler from monitoring the stream, and fires one last data event * to make sure that listeners have the entire contents of what was read in * from the stream. */ public synchronized void stop() { if (reader != null){ try { // Wait for the reader thread to finish. reader.join(); } catch (InterruptedException e) { // The thread was interrupted; nothing to do; finish stopping. } reader = null; } notify(); // Fire one last time to ensure listeners have gotten everything. this.fireNewDataEvent(); } /** * Gets rid of all internal references to objects. */ public void dispose() { if(isRunning()) stop(); line = null; reader = null; is = null; } /** * Fires new events to everything that is monitoring this stream. Then clears * the current line of data. */ private void fireNewDataEvent() { this.fireNewDataEvent(line.toString()); line.delete(0, line.length()); } public void fireNewDataEvent(String l) { synchronized (listeners) { for(int i = 0; i < listeners.size(); i++){ listeners.get(i).handleDataEvent(l); } } } /** * Registers the provided listener to get data events. * @param l A listener that needs to monitor the stream. */ public void addDataListener(IGobblerListener l) { synchronized (listeners) { if(l != null && !listeners.contains(l)){ listeners.add(l); } } } /** * Unregisters the provided listener from getting new data events. * @param l A listener that is monitoring the stream and should be removed */ public void removeDataListener(IGobblerListener l) { synchronized (listeners) { if(listeners.contains(l)) listeners.remove(l); } } private List<IGobblerListener> listeners; private StringBuilder line; private Thread reader; private InputStream is; }