/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.debug.internal.core; import java.io.IOException; import java.io.InputStream; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.Platform; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IStreamListener; import org.eclipse.debug.core.model.IFlushableStreamMonitor; /** * Monitors the output stream of a system process and notifies * listeners of additions to the stream. * * The output stream monitor reads system out (or err) via * and input stream. */ public class OutputStreamMonitor implements IFlushableStreamMonitor { /** * The stream being monitored (connected system out or err). */ private InputStream fStream; /** * A collection of listeners */ private ListenerList fListeners= new ListenerList(1); /** * Whether content is being buffered */ private boolean fBuffered = true; /** * The local copy of the stream contents */ private StringBuffer fContents; /** * The thread which reads from the stream */ private Thread fThread; /** * The size of the read buffer */ private static final int BUFFER_SIZE= 8192; /** * Whether or not this monitor has been killed. * When the monitor is killed, it stops reading * from the stream immediately. */ private boolean fKilled= false; /** * Creates an output stream monitor on the * given stream (connected to system out or err). */ public OutputStreamMonitor(InputStream stream) { fStream= stream; fContents= new StringBuffer(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStreamMonitor#addListener(org.eclipse.debug.core.IStreamListener) */ public void addListener(IStreamListener listener) { fListeners.add(listener); } /** * Causes the monitor to close all * communications between it and the * underlying stream by waiting for the thread to terminate. */ protected void close() { if (fThread != null) { Thread thread= fThread; fThread= null; try { thread.join(); } catch (InterruptedException ie) { } fListeners.removeAll(); } } /** * Notifies the listeners that text has * been appended to the stream. */ private void fireStreamAppended(String text) { getNotifier().notifyAppend(text); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStreamMonitor#getContents() */ public String getContents() { return fContents.toString(); } /** * Continually reads from the stream. *

* This method, along with the startReading * method is used to allow OutputStreamMonitor * to implement Runnable without publicly * exposing a run method. */ private void read() { byte[] bytes= new byte[BUFFER_SIZE]; int read = 0; while (read >= 0) { try { if (fKilled) { break; } read= fStream.read(bytes); if (read > 0) { String text= new String(bytes, 0, read); if (isBuffered()) { fContents.append(text); } fireStreamAppended(text); } } catch (IOException ioe) { DebugPlugin.log(ioe); return; } catch (NullPointerException e) { // killing the stream monitor while reading can cause an NPE // when reading from the stream if (!fKilled && fThread != null) { DebugPlugin.log(e); } return; } } try { fStream.close(); } catch (IOException e) { DebugPlugin.log(e); } } protected void kill() { fKilled= true; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStreamMonitor#removeListener(org.eclipse.debug.core.IStreamListener) */ public void removeListener(IStreamListener listener) { fListeners.remove(listener); } /** * Starts a thread which reads from the stream */ protected void startMonitoring() { if (fThread == null) { fThread= new Thread(new Runnable() { public void run() { read(); } }, DebugCoreMessages.getString("OutputStreamMonitor.label")); //$NON-NLS-1$ fThread.start(); } } /** * @see org.eclipse.debug.core.model.IFlushableStreamMonitor#setBuffered(boolean) */ public void setBuffered(boolean buffer) { fBuffered = buffer; } /** * @see org.eclipse.debug.core.model.IFlushableStreamMonitor#flushContents() */ public void flushContents() { fContents.setLength(0); } /** * @see IFlushableStreamMonitor#isBuffered() */ public boolean isBuffered() { return fBuffered; } private ContentNotifier getNotifier() { return new ContentNotifier(); } class ContentNotifier implements ISafeRunnable { private IStreamListener fListener; private String fText; /** * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ public void handleException(Throwable exception) { DebugPlugin.log(exception); } /** * @see org.eclipse.core.runtime.ISafeRunnable#run() */ public void run() throws Exception { fListener.streamAppended(fText, OutputStreamMonitor.this); } public void notifyAppend(String text) { if (text == null) return; fText = text; Object[] copiedListeners= fListeners.getListeners(); for (int i= 0; i < copiedListeners.length; i++) { fListener = (IStreamListener) copiedListeners[i]; Platform.run(this); } fListener = null; fText = null; } } }