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 }