public void interceptStream(int index, T stream) throws GuacamoleException {

        InterceptedStream<T> interceptedStream;
        String indexString = Integer.toString(index);

        // Atomically verify tunnel is open and add the given stream
        synchronized (tunnel) {

            // Do nothing if tunnel is not open
            if (!tunnel.isOpen())
                return;

            // Wrap stream
            interceptedStream = new InterceptedStream<T>(indexString, stream);

            // Replace any existing stream
            streams.put(interceptedStream);

        }

        // Produce/consume all stream data
        handleInterceptedStream(interceptedStream);

        // Wait for stream to close
        streams.waitFor(interceptedStream);

        // Throw any asynchronously-provided exception
        if (interceptedStream.hasStreamError())
            throw interceptedStream.getStreamError();

    }
Intercept the stream having the given index, producing or consuming its
data as appropriate. The given stream object will automatically be closed
when the stream ends. If there is no stream having the given index, then
the stream object will be closed immediately. This function will block
until all data has been handled and the stream is ended.

@param index
    The index of the stream to intercept.

@param stream
    The stream object which will produce or consume all data for the
    stream having the given index.

@throws GuacamoleException
    If an error occurs while intercepting the stream, or if the stream
    itself reports an error.
guacamole/src/main/java/org/apache/guacamole/tunnel/StreamInterceptingFilter.java