001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Jim Moore
020: * Contributor(s): Nicolas Modrzyk
021: */package org.continuent.sequoia.common.util;
022:
023: import java.io.IOException;
024: import java.io.OutputStream;
025:
026: import org.apache.log4j.Category;
027: import org.apache.log4j.Priority;
028:
029: /**
030: * An OutputStream that flushes out to a Category.
031: * <p>
032: * Note that no data is written out to the Category until the stream is flushed
033: * or closed.
034: * <p>
035: *
036: * @author <a href="mailto://Jim.Moore@rocketmail.com">Jim Moore</a>
037: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk</a>
038: *
039: * @see Category
040: */
041: public class LoggingOutputStream extends OutputStream {
042: protected static final String LINE_SEPARATOR = System
043: .getProperty("line.separator");
044: /**
045: * Used to maintain the contract of {@link #close()}.
046: */
047: protected boolean hasBeenClosed = false;
048: /**
049: * The internal buffer where data is stored.
050: */
051: protected byte[] buf;
052: /**
053: * The number of valid bytes in the buffer. This value is always in the range
054: * <tt>0</tt> through <tt>buf.length</tt>; elements <tt>buf[0]</tt>
055: * through <tt>buf[count-1]</tt> contain valid byte data.
056: */
057: protected int count;
058: /**
059: * Remembers the size of the buffer for speed.
060: */
061: private int bufLength;
062: /**
063: * The default number of bytes in the buffer. =2048
064: */
065: public static final int DEFAULT_BUFFER_LENGTH = 2048;
066: /**
067: * The category to write to.
068: */
069: protected Category category;
070: /**
071: * The priority to use when writing to the Category.
072: */
073: protected Priority priority;
074:
075: private LoggingOutputStream() {
076: // illegal
077: }
078:
079: /**
080: * Creates the LoggingOutputStream to flush to the given Category.
081: *
082: * @param cat the Category to write to
083: * @param priority the Priority to use when writing to the Category
084: * @exception IllegalArgumentException if cat == null or priority == null
085: */
086: public LoggingOutputStream(Category cat, Priority priority)
087: throws IllegalArgumentException {
088: if (cat == null) {
089: throw new IllegalArgumentException("cat == null");
090: }
091: if (priority == null) {
092: throw new IllegalArgumentException("priority == null");
093: }
094: this .priority = priority;
095: category = cat;
096: bufLength = DEFAULT_BUFFER_LENGTH;
097: buf = new byte[DEFAULT_BUFFER_LENGTH];
098: count = 0;
099: }
100:
101: /**
102: * Closes this output stream and releases any system resources associated
103: * with this stream. The general contract of <code>close</code> is that it
104: * closes the output stream. A closed stream cannot perform output operations
105: * and cannot be reopened.
106: */
107: public void close() {
108: flush();
109: hasBeenClosed = true;
110: }
111:
112: /**
113: * Writes the specified byte to this output stream. The general contract for
114: * <code>write</code> is that one byte is written to the output stream. The
115: * byte to be written is the eight low-order bits of the argument <code>b</code>.
116: * The 24 high-order bits of <code>b</code> are ignored.
117: *
118: * @param b the <code>byte</code> to write
119: * @exception IOException if an I/O error occurs. In particular, an <code>IOException</code>
120: * may be thrown if the output stream has been closed.
121: */
122: public void write(final int b) throws IOException {
123: if (hasBeenClosed) {
124: throw new IOException("The stream has been closed.");
125: }
126: // don't log nulls
127: if (b == 0) {
128: return;
129: }
130: // would this be writing past the buffer?
131: if (count == bufLength) {
132: // grow the buffer
133: final int newBufLength = bufLength + DEFAULT_BUFFER_LENGTH;
134: final byte[] newBuf = new byte[newBufLength];
135: System.arraycopy(buf, 0, newBuf, 0, bufLength);
136: buf = newBuf;
137: bufLength = newBufLength;
138: }
139: buf[count] = (byte) b;
140: count++;
141: }
142:
143: /**
144: * Flushes this output stream and forces any buffered output bytes to be
145: * written out. The general contract of <code>flush</code> is that calling
146: * it is an indication that, if any bytes previously written have been
147: * buffered by the implementation of the output stream, such bytes should
148: * immediately be written to their intended destination.
149: */
150: public void flush() {
151: if (count == 0) {
152: return;
153: }
154: // don't print out blank lines; flushing from PrintStream puts out these
155: if (count == LINE_SEPARATOR.length()) {
156: if (((char) buf[0]) == LINE_SEPARATOR.charAt(0)
157: && ((count == 1) || // <-
158: // Unix
159: // &
160: // Mac,
161: // ->
162: // Windows
163: ((count == 2) && ((char) buf[1]) == LINE_SEPARATOR
164: .charAt(1)))) {
165: reset();
166: return;
167: }
168: }
169: final byte[] theBytes = new byte[count];
170: System.arraycopy(buf, 0, theBytes, 0, count);
171:
172: // ADDED: We don't want blank lines at all
173: String bytes = new String(theBytes).trim();
174: int line = -1;
175: while ((line = bytes.indexOf(LINE_SEPARATOR)) != -1) {
176: bytes = bytes.substring(0, line)
177: + bytes.substring(line + LINE_SEPARATOR.length());
178: }
179: // END ADDED
180:
181: category.log(priority, bytes);
182: reset();
183: }
184:
185: private void reset() {
186: // not resetting the buffer -- assuming that if it grew that it
187: // will likely grow similarly again
188: count = 0;
189: }
190: }
|