| /****************************************************************************** |
| * 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 - Initial Implementation |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.ptp.remotetools.utils.stream; |
| |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import org.eclipse.ptp.remotetools.utils.messages.Messages; |
| |
| /** |
| * Connects an inputstream to a stream listener. |
| * <p> |
| * This is a general facility that allows to forward data received from an |
| * inputstream to a listener object. Note that only one listener is connected |
| * to the observer. |
| * |
| * @author Daniel Felix Ferber |
| * @since 1.0 |
| */ |
| public class StreamObserver extends Thread { |
| /* |
| * TODO: Open issues |
| * - Does access to 'killed' attribute need synchronized access? |
| * - On sockets, often one byte is read one after the other, instead of |
| * blocks of bytes. Maybe adding some delay might force to read |
| * more than one byte. |
| */ |
| /** |
| * Stream that is been observed and read. |
| */ |
| private InputStream input; |
| |
| /** |
| * Signals that bridge should stop. |
| */ |
| private boolean killed; |
| |
| /** |
| * Name to identify bridge when debugging. |
| */ |
| private String name; |
| |
| /** |
| * Listener that is called when data is received. |
| */ |
| private IStreamListener listener; |
| |
| static final int BUFFER_SIZE = 100; |
| |
| public StreamObserver(InputStream input, IStreamListener listener, |
| String name) { |
| this.name = name; |
| this.input = input; |
| this.listener = listener; |
| setName(name); |
| } |
| |
| public StreamObserver(InputStream input, IStreamListener listener) { |
| this.name = this.getClass().getName(); |
| this.input = input; |
| this.listener = listener; |
| setName(name); |
| } |
| |
| /** |
| * Stop observing data and terminate thread. |
| */ |
| public synchronized void kill() { |
| killed = true; |
| } |
| |
| void log(String s) { |
| // System.err.println(name + ": " + s); |
| } |
| |
| void streamClosed() { |
| log(Messages.StreamObserver_0); |
| listener.streamClosed(); |
| } |
| |
| void streamError(Exception e) { |
| log(Messages.StreamObserver_1 + e.getMessage()); |
| listener.streamError(e); |
| } |
| |
| void newBytes(byte buffer[], int length) { |
| log(Messages.StreamObserver_2 + Integer.toString(length) + Messages.StreamObserver_3); |
| listener.newBytes(buffer, length); |
| } |
| |
| /** |
| * Run in background. |
| */ |
| public void run() { |
| byte buffer[] = new byte[250]; |
| log(Messages.StreamObserver_4); |
| while (!killed) { |
| try { |
| int bytes = input.read(buffer); |
| if (bytes > 0) { |
| newBytes(buffer, bytes); |
| } else if (bytes == -1) { |
| streamClosed(); |
| break; |
| } |
| } catch (IOException e) { |
| if (killed) { |
| streamClosed(); |
| break; |
| } |
| if (e instanceof EOFException) { |
| streamClosed(); |
| break; |
| } else { |
| streamError(e); |
| break; |
| } |
| } catch (NullPointerException e) { |
| /* |
| * When the stream is closed, Java may raise a |
| * NullPointerException. This case is handled like a |
| * IOException, as above. |
| */ |
| if (killed) { |
| streamClosed(); |
| break; |
| } else { |
| streamError(e); |
| break; |
| } |
| } |
| } |
| log(Messages.StreamObserver_5); |
| } |
| } |