    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     *  contributor license agreements.  See the NOTICE file distributed with
    004     *  this work for additional information regarding copyright ownership.
    005     *  The ASF licenses this file to You under the Apache License, Version 2.0
    006     *  (the "License"); you may not use this file except in compliance with
    007     *  the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     *  Unless required by applicable law or agreed to in writing, software
    012     *  distributed under the License is distributed on an "AS IS" BASIS,
    013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     *  See the License for the specific language governing permissions and
    015     *  limitations under the License.
    016     *
    017     */
    018
    019    package org.apache.commons.exec;
    020
    021    import java.io.ByteArrayOutputStream;
    022    import java.io.IOException;
    023    import java.io.OutputStream;
    024
    025    /**
    026     * Base class to connect a logging system to the output and/or
    027     * error stream of then external process. The implementation
    028     * parses the incoming data to construct a line and passes
    029     * the complete line to an user-defiend implementation.
    030     */
    031    public abstract class LogOutputStream
    032      extends OutputStream {
    033
    034        /** Initial buffer size. */
    035        private static final int INTIAL_SIZE = 132;
    036
    037        /** Carriage return */
    038        private static final int CR = 0x0d;
    039
    040        /** Linefeed */
    041        private static final int LF = 0x0a;
    042
    043        /** the internal buffer */
    044        private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
    045          INTIAL_SIZE);
    046
    047        private boolean skip = false;
    048
    049        private final int level;
    050
    051        /**
    052         * Creates a new instance of this class.
    053         * Uses the default level of 999.
    054         */
    055        public LogOutputStream() {
    056            this(999);
    057        }
    058
    059        /**
    060         * Creates a new instance of this class.
    061         *
    062         * @param level loglevel used to log data written to this stream.
    063         */
    064        public LogOutputStream(final int level) {
    065            this.level = level;
    066        }
    067
    068        /**
    069         * Write the data to the buffer and flush the buffer, if a line separator is
    070         * detected.
    071         *
    072         * @param cc data to log (byte).
    073         * @see java.io.OutputStream#write(int)
    074         */
    075        public void write(final int cc) throws IOException {
    076            final byte c = (byte) cc;
    077            if ((c == '\n') || (c == '\r')) {
    078                if (!skip) {
    079                    processBuffer();
    080                }
    081            } else {
    082                buffer.write(cc);
    083            }
    084            skip = (c == '\r');
    085        }
    086
    087        /**
    088         * Flush this log stream.
    089         *
    090         * @see java.io.OutputStream#flush()
    091         */
    092        public void flush() {
    093            if (buffer.size() > 0) {
    094                processBuffer();
    095            }
    096        }
    097
    098        /**
    099         * Writes all remaining data from the buffer.
    100         *
    101         * @see java.io.OutputStream#close()
    102         */
    103        public void close() throws IOException {
    104            if (buffer.size() > 0) {
    105                processBuffer();
    106            }
    107            super.close();
    108        }
    109
    110        /**
    111         * @return the trace level of the log system
    112         */
    113        public int getMessageLevel() {
    114            return level;
    115        }
    116
    117        /**
    118         * Write a block of characters to the output stream
    119         *
    120         * @param b the array containing the data
    121         * @param off the offset into the array where data starts
    122         * @param len the length of block
    123         * @throws java.io.IOException if the data cannot be written into the stream.
    124         * @see java.io.OutputStream#write(byte[], int, int)
    125         */
    126        public void write(final byte[] b, final int off, final int len)
    127                throws IOException {
    128            // find the line breaks and pass other chars through in blocks
    129            int offset = off;
    130            int blockStartOffset = offset;
    131            int remaining = len;
    132            while (remaining > 0) {
    133                while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
    134                    offset++;
    135                    remaining--;
    136                }
    137                // either end of buffer or a line separator char
    138                int blockLength = offset - blockStartOffset;
    139                if (blockLength > 0) {
    140                    buffer.write(b, blockStartOffset, blockLength);
    141                }
    142                while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
    143                    write(b[offset]);
    144                    offset++;
    145                    remaining--;
    146                }
    147                blockStartOffset = offset;
    148            }
    149        }
    150
    151        /**
    152         * Converts the buffer to a string and sends it to <code>processLine</code>.
    153         */
    154        protected void processBuffer() {
    155            processLine(buffer.toString());
    156            buffer.reset();
    157        }
    158
    159        /**
    160         * Logs a line to the log system of the user.
    161         *
    162         * @param line
    163         *            the line to log.
    164         */
    165        protected void processLine(final String line) {
    166            processLine(line, level);
    167        }
    168
    169        /**
    170         * Logs a line to the log system of the user.
    171         *
    172         * @param line the line to log.
    173         * @param level the log level to use
    174         */
    175        protected abstract void processLine(final String line, final int level);
    176    }