package hudson;


import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import hudson.remoting.FastPipedInputStream;
import hudson.remoting.FastPipedOutputStream;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.rules.ExternalResource;

/**
 * Test that uses a connected channel.
 *
 * @author Kohsuke Kawaguchi
 */
public final class ChannelRule extends ExternalResource {
    /**
     * Two channels that are connected to each other, but shares the same classloader.
     */
    public Channel french;
    public Channel british;
    private ExecutorService executors;

    @Override protected void before() throws Exception {
        executors = Executors.newCachedThreadPool();
        final FastPipedInputStream p1i = new FastPipedInputStream();
        final FastPipedInputStream p2i = new FastPipedInputStream();
        final FastPipedOutputStream p1o = new FastPipedOutputStream(p1i);
        final FastPipedOutputStream p2o = new FastPipedOutputStream(p2i);

        Future<Channel> f1 = executors.submit(new Callable<Channel>() {
            @Override
            public Channel call() throws Exception {
                return new ChannelBuilder("This side of the channel", executors).withMode(Channel.Mode.BINARY).build(p1i, p2o);
            }
        });
        Future<Channel> f2 = executors.submit(new Callable<Channel>() {
            @Override
            public Channel call() throws Exception {
                return new ChannelBuilder("The other side of the channel", executors).withMode(Channel.Mode.BINARY).build(p2i, p1o);
            }
        });
        french = f1.get();
        british = f2.get();
    }

    @Override protected void after() {
        try {
            french.close(); // this will automatically initiate the close on the other channel, too.
            french.join();
            british.join();
        } catch (IOException e) {
            // perhaps this exception is caused by earlier abnormal termination of the channel?
            /* for the record, this is the failure.
                    Nov 12, 2009 6:18:55 PM hudson.remoting.Channel$CloseCommand execute
                    SEVERE: close command failed on This side of the channel
                    java.io.IOException: Pipe is already closed
                        at hudson.remoting.FastPipedOutputStream.write(FastPipedOutputStream.java:127)
                        at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1838)
                        at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1747)
                        at java.io.ObjectOutputStream.writeNonProxyDesc(ObjectOutputStream.java:1249)
                        at java.io.ObjectOutputStream.writeClassDesc(ObjectOutputStream.java:1203)
                        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1387)
                        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
                        at java.io.ObjectOutputStream.writeFatalException(ObjectOutputStream.java:1538)
                        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
                        at hudson.remoting.Channel.send(Channel.java:413)
                        at hudson.remoting.Channel.close(Channel.java:717)
                        at hudson.remoting.Channel$CloseCommand.execute(Channel.java:676)
                        at hudson.remoting.Channel$ReaderThread.run(Channel.java:860)
                    Caused by: hudson.remoting.FastPipedInputStream$ClosedBy: The pipe was closed at...
                        at hudson.remoting.FastPipedInputStream.close(FastPipedInputStream.java:103)
                        at java.io.ObjectInputStream$PeekInputStream.close(ObjectInputStream.java:2305)
                        at java.io.ObjectInputStream$BlockDataInputStream.close(ObjectInputStream.java:2643)
                        at java.io.ObjectInputStream.close(ObjectInputStream.java:873)
                        at hudson.remoting.Channel$ReaderThread.run(Channel.java:866)
                    Nov 12, 2009 6:18:55 PM hudson.remoting.Channel$CloseCommand execute
                    INFO: close command created at
                    Command close created at
                        at hudson.remoting.Command.<init>(Command.java:58)
                        at hudson.remoting.Command.<init>(Command.java:47)
                        at hudson.remoting.Channel$CloseCommand.<init>(Channel.java:673)
                        at hudson.remoting.Channel$CloseCommand.<init>(Channel.java:673)
                        at hudson.remoting.Channel.close(Channel.java:717)
                        at hudson.remoting.Channel$CloseCommand.execute(Channel.java:676)
                        at hudson.remoting.Channel$ReaderThread.run(Channel.java:860)
                    Nov 12, 2009 6:18:55 PM hudson.remoting.Channel$ReaderThread run
                    SEVERE: I/O error in channel This side of the channel
                    java.io.EOFException
                        at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2554)
                        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1297)
                        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
                        at hudson.remoting.Channel$ReaderThread.run(Channel.java:849)

             */
            e.printStackTrace();
        } catch (InterruptedException x) {
            throw new AssertionError(x);
        }
        executors.shutdownNow();
    }
}
