001: package com.canoo.webtest.extension.applet.runner;
002:
003: /*
004: * Copyright (C) The Apache Software Foundation. All rights reserved.
005: *
006: * This software is published under the terms of the Apache Software
007: * License version 1.1, a copy of which has been included with this
008: * distribution in the LICENSE.APL file.
009: */
010:
011: import com.canoo.webtest.util.Checker;
012: import org.apache.log4j.Logger;
013: import org.apache.log4j.Priority;
014:
015: import java.io.IOException;
016: import java.io.OutputStream;
017:
018: /**
019: * An OutputStream that flushes out to a Logger. <p> Note that no data is written out to the logger until the stream is
020: * flushed or closed.
021: * <p> Example:<pre>
022: * // make sure everything sent to System.err is logged
023: * System.setErr(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.WARN), true));
024: * // make sure everything sent to System.out is also logged
025: * System.setOut(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.INFO), true));
026: * </pre>
027: *
028: * @author <a href="mailto://Jim.Moore@rocketmail.com">Jim Moore</a>
029: * @author Denis N. Antonioli
030: */
031: public class LoggingOutputStream extends OutputStream {
032: static final String LINE_SEPARATOR = System
033: .getProperty("line.separator");
034:
035: /**
036: * Used to maintain the contract of {@link #close()}.
037: */
038: private boolean fHasBeenClosed;
039:
040: /**
041: * The internal buffer where data is stored.
042: */
043: private byte fBuf[];
044:
045: /**
046: * The number of valid bytes in the buffer. This value is always in the range <tt>0</tt> through <tt>buf.length</tt>;
047: * elements <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid byte data.
048: */
049: private int fCount;
050:
051: /**
052: * Remembers the size of the buffer for speed.
053: */
054: private int fBufLength;
055:
056: /**
057: * The default number of bytes in the buffer.
058: */
059: public static final int DEFAULT_BUFFER_LENGTH = 2048;
060:
061: /**
062: * The logger to write to.
063: */
064: private final Logger fLogger;
065:
066: /**
067: * The priority to use when writing to the Category.
068: */
069: private final Priority fPriority;
070: /**
071: * The last framework class for Log4j. Log4j generates a stack trace and uses the first entry after the named class as
072: * the location in a log entry.
073: */
074: private final String fLastFrameworkClassName;
075:
076: {
077: fBuf = new byte[DEFAULT_BUFFER_LENGTH];
078: fBufLength = fBuf.length;
079: }
080:
081: /**
082: * Creates the LoggingOutputStream to flush to the given Category.
083: *
084: * @param lastFrameworkClass The last class for log4j to ignore in a stack trace.
085: * @param log The Logger to write to.
086: * @param priority The Priority to use when writing to the Logger.
087: * @throws IllegalArgumentException if one of the argument is null.
088: */
089: public LoggingOutputStream(final Class lastFrameworkClass,
090: Logger log, Priority priority) {
091: Checker.assertNonNull(lastFrameworkClass,
092: "lastFrameworkClass cannot be null");
093: Checker.assertNonNull(log, "log cannot be null");
094: Checker.assertNonNull(priority, "priority cannot be null");
095:
096: fPriority = priority;
097: fLogger = log;
098: fLastFrameworkClassName = lastFrameworkClass.getName();
099: }
100:
101: /**
102: * Closes this output stream and releases any system resources associated with this stream. The general contract of
103: * <code>close</code> is that it closes the output stream. A closed stream cannot perform output operations and cannot
104: * be reopened.
105: */
106: public void close() {
107: flush();
108: fHasBeenClosed = true;
109: }
110:
111: String getLastFrameworkClassName() {
112: return fLastFrameworkClassName;
113: }
114:
115: Priority getPriority() {
116: return fPriority;
117: }
118:
119: Logger getLogger() {
120: return fLogger;
121: }
122:
123: int getCount() {
124: return fCount;
125: }
126:
127: /**
128: * Writes the specified byte to this output stream. The general contract for <code>write</code> is that one byte is
129: * written to the output stream. The byte to be written is the eight low-order bits of the argument <code>b</code>. The
130: * 24 high-order bits of <code>b</code> are ignored.
131: *
132: * @param b the <code>byte</code> to write
133: * @throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output
134: * stream has been closed.
135: */
136: public void write(final int b) throws IOException {
137: if (fHasBeenClosed) {
138: throw new IOException("The stream has been closed.");
139: }
140:
141: // don't log nulls
142: if (b == 0) {
143: return;
144: }
145:
146: // would this be writing past the buffer?
147: if (fCount == fBufLength) {
148: // grow the buffer
149: final int newBufLength = fBufLength + DEFAULT_BUFFER_LENGTH;
150: final byte[] newBuf = new byte[newBufLength];
151:
152: System.arraycopy(fBuf, 0, newBuf, 0, fBufLength);
153:
154: fBuf = newBuf;
155: fBufLength = fBuf.length;
156: }
157:
158: fBuf[fCount++] = (byte) b;
159: }
160:
161: /**
162: * Flushes this output stream and forces any buffered output bytes to be written out. The general contract of
163: * <code>flush</code> is that calling it is an indication that, if any bytes previously written have been buffered by
164: * the implementation of the output stream, such bytes should immediately be written to their intended destination.
165: */
166: public void flush() {
167: if (fCount == 0) {
168: return;
169: }
170:
171: // don't print out blank lines; flushing from PrintStream puts out these
172: if (fCount == LINE_SEPARATOR.length()) {
173: if (((char) fBuf[0]) == LINE_SEPARATOR.charAt(0)
174: && ((fCount == 1) || // <- Unix & Mac, -> Windows
175: ((fCount == 2) && ((char) fBuf[1]) == LINE_SEPARATOR
176: .charAt(1)))) {
177: reset();
178: return;
179: }
180: }
181:
182: fLogger.log(fLastFrameworkClassName, fPriority, new String(
183: fBuf, 0, fCount), null);
184:
185: reset();
186: }
187:
188: private void reset() {
189: // not resetting the buffer -- assuming that if it grew that big
190: // it will likely grow similarly again
191: fCount = 0;
192: }
193:
194: }
|