001    //
002    // Copyright 1999,2000,2004 Crispin Perdue <cris@perdues.com>
003    // 
004    // This is free software, and comes with ABSOLUTELY NO WARRANTY.
005    // You may distribute it under the terms of the Library GNU Public License
006    // Version 2.
007    //
008    
009    package com.perdues;
010    
011    import java.io.*;
012    import java.util.logging.Logger;
013    import java.util.logging.Level;
014    import java.nio.ByteBuffer;
015    import java.nio.channels.Channels;
016    import java.nio.channels.ReadableByteChannel;
017    import java.nio.channels.WritableByteChannel;
018    
019    
020    /**
021      A BytePump copies from an InputStream to an OutputStream until end of
022      file or until an Exception or other Throwable occurs.  This provides
023      "glue" to supply data from an InputStream to an OutputStream. 
024      Instances can be configured to close the OutputStream upon thread
025      termination.  Normally they do not close, so you can concatenate
026      outputs and use System.out or System.err for output.  For some kinds
027      of underlying InputStreams it is possible to unblock a read
028      operation and terminate this thread with an interrupt.  Some
029      InputStreams however can block indefinitely even if interrupted.
030      */
031    public class BytePump extends Thread {
032    
033      /**
034        Creates a BytePump that will copy from the InputStream to the
035        OutputStream until it reaches the end of the InputStream or throws
036        an Exception.  If the I/O operations unblock on a thread interrupt,
037        this will terminate on a thread interrupt.  Marks the thread as a
038        Daemon so its existence cannot stop the JVM from exiting.  You will
039        need to call the <tt>start</tt> method to make it go.
040        */
041      public BytePump(InputStream in, OutputStream out) {
042        this.in = in;
043        this.out = out;
044        setDaemon(true);
045      }
046      
047      /**
048        If this thread terminated because of a Throwable,
049        it makes the value available through this method.
050      */
051      public synchronized Throwable getExitCause() {
052        return exitCause;
053      }
054      
055      /**
056        Setting closeOnExit causes this to reliably close the OutputStream
057        whenever the thread terminates.  It is initially off.
058        */
059      public void setCloseOnExit(boolean request) {
060        closeOnExit = request;
061      }
062      
063      /**
064        Returns the closeOnExit status.
065        */
066      public boolean isCloseOnExit() {
067        return closeOnExit;
068      }
069    
070    
071      /**
072        The thread automatically runs this method.  As usual for subclasses
073        of Thread, this is not intended to be invoked directly by client
074        code, use <tt>start</tt> instead.
075        */
076      public void run() {
077        ByteBuffer buf = ByteBuffer.allocate(1000);
078        ReadableByteChannel inch = Channels.newChannel(in);
079        WritableByteChannel ouch = Channels.newChannel(out);
080        
081        try {
082          while (inch.read(buf)>=0) {
083            buf.flip();
084            ouch.write(buf);
085            buf.clear();
086            out.flush();
087          }
088        } catch (Throwable ex) {
089          exitCause = ex;
090          if (ex instanceof ThreadDeath) {
091            throw (ThreadDeath)ex;
092          }
093        } finally {
094          logger.log(Level.FINE, "terminated {0}", this);
095          if (closeOnExit) {
096            try {
097              out.close();
098            } catch(IOException ex) {}
099          }
100        }
101      }
102      
103      /** COMMENTED OUT main (for testing)
104      public static void main(String[] args) {
105        BytePump p = new BytePump(System.in, System.out);
106        p.start();
107        try { Thread.sleep(30*1000); } catch(InterruptedException ex) {}
108        p.interrupt();
109        try { p.join(); } catch(InterruptedException ex) {}
110      }
111      */
112      
113      // Controls whether to close the output side from within
114      // this class on thread termination.
115      private boolean closeOnExit = false;
116      
117      private Throwable exitCause = null;
118    
119      private InputStream in;
120      private OutputStream out;
121    
122      private static Logger logger =
123        Logger.getLogger(BytePump.class.getName());
124    
125    }