001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util.externalprocess;
011:
012: import java.io.BufferedOutputStream;
013: import java.io.BufferedInputStream;
014: import java.io.InputStream;
015: import java.io.OutputStream;
016: import java.io.IOException;
017:
018: import org.mmbase.util.logging.Logger;
019: import org.mmbase.util.logging.Logging;
020:
021: /**
022: * Thread which continuously reads from a input stream and pushes the read data
023: * to an output stream which is immediately flushed afterwards.
024: *
025: * @author Nico Klasens (Finalist IT Group)
026: * @version $Id: StreamCopyThread.java,v 1.4 2005/01/30 16:46:39 nico Exp $
027: * @since MMBase-1.6
028: */
029: class StreamCopyThread extends Thread {
030:
031: /** MMBase logging system */
032: private static Logger log = Logging
033: .getLoggerInstance(StreamCopyThread.class.getName());
034:
035: /**
036: * The number of milliseconds to wait before writing
037: */
038: protected static final long WAIT_DELAY = 25;
039:
040: /**
041: * Default buffer size.
042: */
043: public static final int BUFFER_SIZE = 1024;
044:
045: /**
046: * The stream from which to pipe the data.
047: */
048: private InputStream inputStream;
049:
050: /**
051: * The stream to pipe the data to.
052: */
053: private OutputStream outputStream;
054:
055: /**
056: * This thread writes to the external process
057: */
058: private boolean processInput = false;
059:
060: /**
061: * Stream copying finished
062: */
063: private boolean finished = false;
064:
065: /**
066: * Create a thread to copy bytes fro one strea to the other
067: *
068: * @param name the name of the new thread
069: * @param in the stream from which to pipe the data
070: * @param out the stream to pipe the data to
071: * @param pInput This thread writes to the external process
072: */
073: public StreamCopyThread(String name, InputStream in,
074: OutputStream out, boolean pInput) {
075: this (null, name, in, out, pInput);
076: }
077:
078: /**
079: * Create a thread to copy bytes fro one stream to the other
080: *
081: * @param group ThreadGroup where this thread belongs to
082: * @param name the name of the new thread
083: * @param in the stream from which to pipe the data
084: * @param out the stream to pipe the data to
085: * @param bSize the size of the buffer in which data is piped
086: * @param pInput This thread writes to the external process
087: */
088: public StreamCopyThread(ThreadGroup group, String name,
089: InputStream in, OutputStream out, boolean pInput) {
090: super (group, name);
091: processInput = pInput;
092: outputStream = out;
093: inputStream = in;
094: setDaemon(true);
095: }
096:
097: /**
098: * @see java.lang.Runnable#run()
099: */
100: public void run() {
101: BufferedInputStream reader = new BufferedInputStream(
102: inputStream);
103: BufferedOutputStream writer = null;
104: if (outputStream != null) {
105: writer = new BufferedOutputStream(outputStream);
106: }
107: try {
108: /*
109: * Without the sleep call, the subprocess doesn't get enough time
110: * to get started before we try to read its output, so we got
111: * nothing. This is a kludge, but it achieves the desired effect.
112: * Finding a more elegant solution is an exercise for the student,
113: * as those horrible college math book authors loved to say.
114: */
115: try {
116: Thread.sleep(WAIT_DELAY);
117: } catch (InterruptedException e) {
118: // we only wanted a delay
119: }
120:
121: int size = 0;
122: //this buffer has nothing to do with the OS buffer
123: byte[] buffer = new byte[StreamCopyThread.BUFFER_SIZE];
124:
125: while ((size = reader.read(buffer)) != -1) {
126: if (writer != null) {
127: writer.write(buffer, 0, size);
128: writer.flush();
129: }
130: //log.debug("StreamCopy " + this.getName() + " read " + size + " bytes from input and wrote to output." );
131:
132: //Maybe we should reset variables
133: //size = 0;
134: //buffer = new byte[BUFFER_SIZE];
135: }
136: } catch (IOException x) {
137: // ignore
138: } finally {
139: /*
140: This thread only closes the stream to the process.
141: This way , the external process knows that we are finished writing.
142: Closing the stdout and sterr is not critical, but it is still nice
143: to close all the resources
144: This thread is not responsible for closing the stream of the java process.
145: */
146: if (processInput) {
147: try {
148: if (writer != null) {
149: writer.close();
150: }
151: } catch (IOException e) {
152: //ignore
153: }
154: } else {
155: try {
156: if (reader != null) {
157: reader.close();
158: }
159: } catch (IOException e) {
160: //ignore
161: }
162: }
163: complete();
164: }
165: }
166:
167: /**
168: * Returns whether this thread has finished copying bytes
169: *
170: * @return <code>true</code> if finished and <code>false</code> otherwise
171: */
172: public synchronized boolean finished() {
173: return finished;
174: }
175:
176: /**
177: * By calling this method the calling thread will wait until this thread is
178: * finished copying bytes
179: */
180: public synchronized void waitFor() {
181: while (!finished) {
182: try {
183: wait();
184: } catch (InterruptedException e) {
185: }
186: }
187: }
188:
189: /**
190: * This method is called when the copying of bytes is done and notifies a
191: * waiting thread
192: */
193: protected synchronized void complete() {
194: finished = true;
195: notify();
196: log.debug("StreamCopy " + this .getName() + " finished.");
197: }
198: }
|