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