/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.phoenix; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.text.MessageFormat; import java.util.Date; /** * PipeWriter plugs together two pairs of input and output streams by * providing readers for input streams and writing through to * appropriate output streams. Both output streams are annotated on a * per-line basis. * * @author Sun Microsystems, Inc. * * @since 2.0 */ class PipeWriter implements Runnable { /** stream used for buffering lines */ private final ByteArrayOutputStream bufOut; /** count since last separator */ private int cLast; /** current chunk of input being compared to lineSeparator.*/ private final byte[] currSep; private final PrintWriter out; private final InputStream in; private final String execString; private final Object[] date = new Object[]{ new Date() }; private static final String lineSeparator = System.getProperty("line.separator"); private static final int lineSeparatorLength = lineSeparator.length(); private static final String format = "{0,date} {0,time}"; private static final MessageFormat formatter = new MessageFormat(format); /** * Create a new PipeWriter object. All methods of PipeWriter, * except plugTogetherPair, are only accessible to PipeWriter * itself. Synchronization is unnecessary on functions that will * only be used internally in PipeWriter. * * @param in input stream from which pipe input flows * @param out output stream to which log messages will be sent * @param name name of the process * @param tag name of the stream (out or err) */ private PipeWriter (InputStream in, OutputStream out, String name, String tag) { this.in = in; this.out = new PrintWriter(out); bufOut = new ByteArrayOutputStream(); currSep = new byte[lineSeparatorLength]; /* set unique pipe/pair annotations */ execString = ":" + name + ':' + tag + ':'; } /** * Create a thread to listen and read from input stream, in. buffer * the data that is read until a marker which equals lineSeparator * is read. Once such a string has been discovered; write out an * annotation string followed by the buffered data and a line * separator. */ public void run() { byte[] buf = new byte[256]; int count; try { /* read bytes till there are no more. */ while ((count = in.read(buf)) != -1) { write(buf, 0, count); } /* flush internal buffer... may not have ended on a line * separator, we also need a last annotation if * something was left. */ String lastInBuffer = bufOut.toString(); bufOut.reset(); if (lastInBuffer.length() > 0) { out.println (createAnnotation() + lastInBuffer); out.flush(); // add a line separator // to make output nicer } } catch (IOException e) { } } /** * Write a subarray of bytes. Pass each through write byte method. */ private void write(byte b[], int off, int len) throws IOException { if (len < 0) { throw new ArrayIndexOutOfBoundsException(len); } for (int i = 0; i < len; ++ i) { write(b[off + i]); } } /** * Write a byte of data to the stream. If we have not matched a * line separator string, then the byte is appended to the internal * buffer. If we have matched a line separator, then the currently * buffered line is sent to the output writer with a prepended * annotation string. */ private void write(byte b) throws IOException { int i = 0; /* shift current to the left */ for (i = 1 ; i < (currSep.length); i ++) { currSep[i-1] = currSep[i]; } currSep[i-1] = b; bufOut.write(b); /* enough characters for a separator? */ if ( (cLast >= (lineSeparatorLength - 1)) && (lineSeparator.equals(new String(currSep))) ) { cLast = 0; /* write prefix through to underlying byte stream */ out.print(createAnnotation() + bufOut.toString()); out.flush(); bufOut.reset(); if (out.checkError()) { throw new IOException ("PipeWriter: IO Exception when"+ " writing to output stream."); } } else { cLast++; } } /** * Create an annotation string to be printed out after * a new line and end of stream. */ private String createAnnotation() { /* construct prefix for log messages: * date/time stamp... */ StringBuffer text = new StringBuffer(); ((Date) date[0]).setTime(System.currentTimeMillis()); formatter.format(date, text, null); text.append(execString); return text.toString(); } /** * Allow plugging together two pipes at a time, to associate * output from an exec'ed process. * * @param name name of the process * @param in input stream from which pipe input comes * @param out output stream to which log messages will be sent * @param in1 input stream from which pipe input comes * @param out1 output stream to which log messages will be sent */ static void plugTogetherPair(String name, InputStream in, OutputStream out, InputStream in1, OutputStream out1) { /* start threads to read output from child process */ Thread outThread = new Thread(new PipeWriter(in, out, name, "out"), name + "-out"); outThread.setDaemon(true); outThread.start(); Thread errThread = new Thread(new PipeWriter(in1, out1, name, "err"), name + "-err"); errThread.setDaemon(true); errThread.start(); } }