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 019package org.apache.commons.exec; 020 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import 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-defined implementation. 030 * 031 * @version $Id: LogOutputStream.java 1636056 2014-11-01 21:12:52Z ggregory $ 032 */ 033public abstract class LogOutputStream 034 extends OutputStream { 035 036 /** Initial buffer size. */ 037 private static final int INTIAL_SIZE = 132; 038 039 /** Carriage return */ 040 private static final int CR = 0x0d; 041 042 /** Linefeed */ 043 private static final int LF = 0x0a; 044 045 /** the internal buffer */ 046 private final ByteArrayOutputStream buffer = new ByteArrayOutputStream( 047 INTIAL_SIZE); 048 049 private boolean skip = false; 050 051 private final int level; 052 053 /** 054 * Creates a new instance of this class. 055 * Uses the default level of 999. 056 */ 057 public LogOutputStream() { 058 this(999); 059 } 060 061 /** 062 * Creates a new instance of this class. 063 * 064 * @param level loglevel used to log data written to this stream. 065 */ 066 public LogOutputStream(final int level) { 067 this.level = level; 068 } 069 070 /** 071 * Write the data to the buffer and flush the buffer, if a line separator is 072 * detected. 073 * 074 * @param cc data to log (byte). 075 * @see java.io.OutputStream#write(int) 076 */ 077 @Override 078 public void write(final int cc) throws IOException { 079 final byte c = (byte) cc; 080 if (c == '\n' || c == '\r') { 081 if (!skip) { 082 processBuffer(); 083 } 084 } else { 085 buffer.write(cc); 086 } 087 skip = c == '\r'; 088 } 089 090 /** 091 * Flush this log stream. 092 * 093 * @see java.io.OutputStream#flush() 094 */ 095 @Override 096 public void flush() { 097 if (buffer.size() > 0) { 098 processBuffer(); 099 } 100 } 101 102 /** 103 * Writes all remaining data from the buffer. 104 * 105 * @see java.io.OutputStream#close() 106 */ 107 @Override 108 public void close() throws IOException { 109 if (buffer.size() > 0) { 110 processBuffer(); 111 } 112 super.close(); 113 } 114 115 /** 116 * @return the trace level of the log system 117 */ 118 public int getMessageLevel() { 119 return level; 120 } 121 122 /** 123 * Write a block of characters to the output stream 124 * 125 * @param b the array containing the data 126 * @param off the offset into the array where data starts 127 * @param len the length of block 128 * @throws java.io.IOException if the data cannot be written into the stream. 129 * @see java.io.OutputStream#write(byte[], int, int) 130 */ 131 @Override 132 public void write(final byte[] b, final int off, final int len) 133 throws IOException { 134 // find the line breaks and pass other chars through in blocks 135 int offset = off; 136 int blockStartOffset = offset; 137 int remaining = len; 138 while (remaining > 0) { 139 while (remaining > 0 && b[offset] != LF && b[offset] != CR) { 140 offset++; 141 remaining--; 142 } 143 // either end of buffer or a line separator char 144 final int blockLength = offset - blockStartOffset; 145 if (blockLength > 0) { 146 buffer.write(b, blockStartOffset, blockLength); 147 } 148 while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) { 149 write(b[offset]); 150 offset++; 151 remaining--; 152 } 153 blockStartOffset = offset; 154 } 155 } 156 157 /** 158 * Converts the buffer to a string and sends it to {@code processLine}. 159 */ 160 protected void processBuffer() { 161 processLine(buffer.toString()); 162 buffer.reset(); 163 } 164 165 /** 166 * Logs a line to the log system of the user. 167 * 168 * @param line 169 * the line to log. 170 */ 171 protected void processLine(final String line) { 172 processLine(line, level); 173 } 174 175 /** 176 * Logs a line to the log system of the user. 177 * 178 * @param line the line to log. 179 * @param logLevel the log level to use 180 */ 181 protected abstract void processLine(final String line, final int logLevel); 182}