/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scheduler.common.util.logforwarder.util; import java.io.IOException; import java.io.OutputStream; import org.apache.log4j.Category; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** * An OutputStream that flushes out to a Category. * @author Jim Moore * @see Category */ public class LoggingOutputStream extends OutputStream { /** * System dependent line return char(s). */ static final byte[] lineSepBytes = System.lineSeparator().getBytes(); /** * The default number of bytes in the buffer. =2048 */ public static final int DEFAULT_BUFFER_LENGTH = 2048; /** * Used to maintain the contract of #close(). */ protected boolean hasBeenClosed = false; /** * The internal buffer where data is stored. */ protected byte[] buf; /** * The number of valid bytes in the buffer. This value is always * in the range <tt>0</tt> through <tt>buf.length</tt>; elements * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid * byte data. */ protected int count; /** * Remembers the size of the buffer for speed. */ private int bufLength; /** * The category to write to. */ protected Logger logger; /** * The priority to use when writing to the Category. */ protected Level level; private LoggingOutputStream() { // illegal } /** * Creates the LoggingOutputStream to flush to the given Category. * @param log the Logger to write to * @param level the Level to use when writing to the Logger * @throws IllegalArgumentException if cat == null or priority == null */ public LoggingOutputStream(Logger log, Level level) throws IllegalArgumentException { if (log == null) { throw new IllegalArgumentException("cat == null"); } if (level == null) { throw new IllegalArgumentException("priority == null"); } this.level = level; logger = log; bufLength = DEFAULT_BUFFER_LENGTH; buf = new byte[DEFAULT_BUFFER_LENGTH]; count = 0; } public LoggingOutputStream(Logger log) throws IllegalArgumentException { this(log, Level.ALL); } /** * Closes this output stream and releases any system resources * associated with this stream. The general contract of * <code>close</code> * is that it closes the output stream. A closed stream cannot perform * output operations and cannot be reopened. */ @Override public void close() { flush(); hasBeenClosed = true; } /** * Writes the specified byte to this output stream. The general * contract for <code>write</code> is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argument <code>b</code>. The 24 * high-order bits of <code>b</code> are ignored. * * @param b the <code>byte</code> to write * @throws IOException if an I/O error occurs. In particular, * an <code>IOException</code> may be thrown if the * output stream has been closed. */ @Override public void write(final int b) throws IOException { if (hasBeenClosed) { throw new IOException("The stream has been closed."); } // would this be writing past the buffer? if (count == bufLength) { // grow the buffer final int newBufLength = bufLength + DEFAULT_BUFFER_LENGTH; final byte[] newBuf = new byte[newBufLength]; System.arraycopy(buf, 0, newBuf, 0, bufLength); buf = newBuf; bufLength = newBufLength; } buf[count] = (byte) b; count++; } /** * Flushes this output stream and forces any buffered output bytes * to be written out. The general contract of <code>flush</code> is * that calling it is an indication that, if any bytes previously * written have been buffered by the implementation of the output * stream, such bytes should immediately be written to their * intended destination. */ @Override public void flush() { if (count == 0) { return; } // don't print out blank lines; flushing from PrintStream puts out these if (count == lineSepBytes.length) { boolean isLineSep = true; for (int c = 0; c < lineSepBytes.length; c++) { if (buf[c] != lineSepBytes[c]) { isLineSep = false; break; } } if (isLineSep) { reset(); return; } } final byte[] theBytes = new byte[count]; System.arraycopy(buf, 0, theBytes, 0, count); //logger.log(level, (new String(theBytes)).trim()); logger.log(level, new String(theBytes)); reset(); } private void reset() { // not resetting the buffer -- assuming that if it grew then it // will likely grow similarly again count = 0; } }