You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oodt.apache.org by ke...@apache.org on 2010/07/15 01:04:26 UTC

svn commit: r964250 - /incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/

Author: kelly
Date: Wed Jul 14 23:04:25 2010
New Revision: 964250

URL: http://svn.apache.org/viewvc?rev=964250&view=rev
Log:
WIP OODT-15
Import OODT Commons I/O features: log, byte-counting streams, null streams, round-robin fixed buffer streams, base64 enc/dec strams.

Added:
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64DecodingInputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64EncodingOutputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/CountingOutputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/FixedBufferOutputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Log.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogEvent.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogFilter.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogListener.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogWriter.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullInputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullOutputStream.java
    incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/WriterLogger.java

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64DecodingInputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64DecodingInputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64DecodingInputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64DecodingInputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,164 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import org.apache.oodt.commons.util.Base64;
+
+/** An input stream that decodes its data from the RFC-1512 base 64 format.
+ *
+ * Wrap this input stream around another input stream, and all the bytes will be converted
+ * from their base-64 format when you read from it.
+ *
+ * @author Kelly
+ */
+public class Base64DecodingInputStream extends FilterInputStream {
+	/** Construct a base-64 decoding input stream.
+	 *
+	 * @param inputStream The input stream to decode.
+	 */
+	public Base64DecodingInputStream(InputStream inputStream) {
+		super(inputStream);
+	}
+
+	/** Read the next byte.
+	 *
+	 * Decode more base-64 data and return the next decoded byte.
+	 *
+	 * @return The byte, or -1 on end of stream.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public int read() throws IOException {
+		if (in == null) throw new IOException("Can't read from a closed stream");
+
+		// If we've used up the decoded data buffer, read 4 more bytes and decode 'em.
+		if (buffer == null || index == buffer.length) {
+			byte[] streamBuf = new byte[4];
+			int toRead = 4;
+			int atIndex = 0;
+			int actuallyGot;
+			boolean firstRead = true;
+			while (toRead > 0) {
+				actuallyGot = in.read(streamBuf, atIndex, toRead);
+				if (actuallyGot == -1) {
+					if (firstRead) return -1;
+					else break;
+				}
+				firstRead = false;
+				atIndex += actuallyGot;
+				toRead -= actuallyGot;
+			}
+			buffer = Base64.decode(streamBuf);
+			if (buffer.length == 0) {
+				buffer = null;
+				return -1;
+			}
+			index = 0;
+		}
+		return buffer[index++] & 0xff;
+	}
+
+	/** Read a bunch of bytes.
+	 *
+	 * This decodes base-64 data from the underlying stream and puts the result into
+	 * the given array.
+	 *
+	 * @param b The buffer to fill with decoded base-64 data.
+	 * @param offset Where in the buffer to start filling.
+	 * @param length How many bytes to fill.
+	 * @return The actual number of decoded bytes.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public int read(byte[] b, int offset, int length) throws IOException {
+		if (b == null) throw new IllegalArgumentException("Can't read data into a null array");
+		if (offset < 0 || offset >= b.length)
+			throw new IndexOutOfBoundsException("Can't read data into an array with indexes 0.." + (b.length-1)
+				+ " at index " + offset);
+		if (length < 0) throw new IllegalArgumentException("Can't read a negative amount of data");
+		if (offset + length > b.length)
+			throw new IndexOutOfBoundsException("Can't read data past the right edge of an array");
+		if (in == null) throw new IOException("Can't read from a closed stream");
+
+		int c = read();
+		if (c == -1) return -1;
+		b[offset] = (byte) c;
+		int i = 1;
+		try {
+			for (; i < length; ++i) {
+				c = read();
+				if (c == -1) break;
+				b[offset + i] = (byte) c;
+			}
+		} catch (IOException ignore) {}
+		return i;
+	}
+
+	/** Skip bytes.
+	 *
+	 * This method skips and discards <var>n</var> decoded bytes on the input stream.
+	 *
+	 * @param n Number of bytes to skip.
+	 * @return Actual number of bytes skipped.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public long skip(long n) throws IOException {
+		if (in == null) throw new IOException("Can't skip past data on a closed stream");
+		int actuallySkipped = 0;
+		while (n > 0) {
+			if (read() == -1) return actuallySkipped;
+			--n;
+			++actuallySkipped;
+		}
+		return actuallySkipped;
+	}
+
+	/** Return bytes available for reading or skipping without blocking.
+	 *
+	 * @return The number of bytes that can be read from this stream or skipped over
+	 * on the stream without blocking.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public int available() throws IOException {
+		if (in == null) throw new IOException("Can't see how many bytes are available on a closed stream");
+		if (buffer != null && index < buffer.length)
+			return buffer.length - index;
+		return in.available() >= 4? 1 : 0;
+	}
+
+	/** Close this stream.
+	 *
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public void close() throws IOException {
+		if (in == null) throw new IOException("Can't close a closed stream");
+		in.close();
+		in = null;
+		buffer = null;
+	}
+
+	/** Buffer for decoded data.
+	 */
+	private byte[] buffer;
+
+	/** Where we'll next read out of the buffer.
+	 *
+	 * Since we always read 4 bytes at a time (a base-64 block), we can decode that
+	 * into as many as 3 bytes, so start out the index in an invalid location.
+	 */
+	private int index = 3;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64EncodingOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64EncodingOutputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64EncodingOutputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Base64EncodingOutputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,136 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.oodt.commons.util.Base64;
+
+/** An output stream that encodes its data into RFC-1512 base 64 format.
+ *
+ * Wrap this input stream around another output stream, and all the bytes will be
+ * converted into their base-64 format when you write to it.
+ *
+ * @author Kelly
+ */
+public class Base64EncodingOutputStream extends FilterOutputStream {
+	/** Construct a base-64 encoding output stream.
+	 *
+	 * @param outputStream The output stream to which to write.
+	 */
+	public Base64EncodingOutputStream(OutputStream outputStream) {
+		super(outputStream);
+	}
+
+	/** Write a byte of data.
+	 *
+	 * The byte will be encoded into base-64 format on the output.
+	 *
+	 * @param b The byte.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public void write(int b) throws IOException {
+		if (buffer == null)
+			throw new IOException("Can't write onto a closed stream");
+		buffer[index++] = (byte) b;
+		if (index == buffer.length) shipout();
+	}
+
+	/** Write a bunch of bytes.
+	 *
+	 * The given array of bytes will be encoded into base-64 on the output.
+	 *
+	 * @param b The array to write.
+	 * @param offset Where in the data to start writing.
+	 * @param length How many bytes to write.
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public void write(byte[] b, int offset, int length) throws IOException {
+		if (b == null) throw new IllegalArgumentException("Can't write a null array");
+		if (offset < 0 || offset >= b.length)
+			throw new IndexOutOfBoundsException("Can't get bytes at " + offset + " in array with indexes 0.."
+				+ (b.length - 1));
+		if (length < 0) throw new IllegalArgumentException("Can't write a negative amount of bytes");
+		if (offset + length > b.length)
+			throw new IndexOutOfBoundsException("Can't get bytes beyond edge of array");
+		if (buffer == null)
+			throw new IOException("Can't write onto a closed stream");
+		while (length > 0) {
+			int avail = buffer.length - index;
+			int amount = avail < length? avail : length;
+			System.arraycopy(b, offset, buffer, index, amount);
+			index += amount;
+			offset += amount;
+			length -= amount;
+			if (index == buffer.length) shipout();
+		}
+	}
+
+	/** Flush the stream.
+	 *
+	 * This causes any buffered bytes to be encoded and shipped out to the underlying
+	 * stream, which is also flushed.
+	 *
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public void flush() throws IOException {
+		if (buffer == null)
+			throw new IOException("Can't flush a closed stream");
+		shipout();
+		out.flush();
+	}
+
+	/** Close the stream.
+	 *
+	 * This writes out any unflushed data in base-64 format and closes the underlying
+	 * stream.
+	 *
+	 * @throws IOException If an I/O error occurs.
+	 */
+	public void close() throws IOException {
+		if (buffer == null)
+			throw new IOException("Can't close an already closed stream");
+		flush();
+		out.close();
+		out = null;
+		buffer = null;
+	}
+
+	/** Ship out a bunch of buffered data in base-64 format.
+	 *
+	 * This resets the index of the next byte to insert back to zero.
+	 *
+	 * @throws IOException If an I/O error occurs.
+	 */
+	private void shipout() throws IOException {
+		byte[] encoded = Base64.encode(buffer, 0, index);
+		out.write(encoded);
+		index = 0;
+	}
+
+	/** Size of the output data buffer.  Must be a multiple of 3.
+	 */
+	private static final int BUFFER_SIZE = 300;
+
+	/** Buffer for output data.
+	 */
+	private byte[] buffer = new byte[BUFFER_SIZE];
+
+	/** Where we are in the buffer.
+	 */
+	private int index = 0;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/CountingOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/CountingOutputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/CountingOutputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/CountingOutputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream that counts the number bytes it passes on.
+ *
+ * @author Kelly
+ * @version $Revision: 1.1.1.1 $
+ */
+public class CountingOutputStream extends FilterOutputStream {
+	/**
+	 * Creates a new <code>CountingOutputStream</code> instance.
+	 *
+	 * @param out Where to send bytes onto.
+	 */
+	public CountingOutputStream(OutputStream out) {
+		super(out);
+	}
+  
+	public void write(int b) throws IOException {
+		out.write(b);
+		++written;
+	}
+  
+	public void write(byte[] b) throws IOException {
+		out.write(b);
+		written += b.length;
+	}
+  
+	public void write(byte[] b, int offset, int length) throws IOException {
+		out.write(b, offset, length);
+		written += length;
+	}
+  
+	/**
+	 * Get the number of bytes written so far.
+	 *
+	 * @return a <code>long</code> value.
+	 */
+	public long getBytesWritten() {
+		return written;
+	}
+  
+	/** Number of bytes written so far. */
+	private long written = 0L;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/FixedBufferOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/FixedBufferOutputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/FixedBufferOutputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/FixedBufferOutputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,119 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** This stream writes its output into a byte buffer of fixed length.
+ *
+ * For a buffer of size <var>n</var>, only the last <var>n</var> bytes written are ever
+ * available.
+ *
+ * @author Kelly
+ */
+public class FixedBufferOutputStream extends OutputStream {
+	/** Construct a fixed buffer output stream.
+	 *
+	 * @param n Size of the buffer.
+	 */
+	public FixedBufferOutputStream(int n) {
+		if (n < 0) throw new IllegalArgumentException("Buffer size must be nonnegative");
+		buffer = new byte[n];
+		length = n;
+		size = 0;
+		start = 0;
+	}
+
+	public void write(int b) throws IOException {
+		checkIfClosed();
+		if (length == 0) return;
+		if (size < length)
+			buffer[size++] = (byte) b;
+		else {
+			buffer[start] = (byte) b;
+			start = (start + 1) % length;
+		}
+	}
+
+	public void write(byte[] a, int off, int len) throws IOException {
+		checkIfClosed();
+		if (a == null) throw new NullPointerException("Can't write from a null array");
+		else if ((off < 0) || (off > a.length) || (len < 0) || ((off + len) > a.length) || ((off + len) < 0)) {
+			throw new IndexOutOfBoundsException("Offset " + off + " and length " + len + " not within array bounds");
+		} else if (len == 0) {
+			return;
+		}
+		if (len > length) {
+			off += len - length;
+			len = length;
+		}
+		int capacity = length - size;
+		int insertionIndex = size < length? size : start;
+		int insertionLength = Math.min(length - insertionIndex, len);
+		int remaining = len - insertionLength;
+		System.arraycopy(a, off, buffer, insertionIndex, insertionLength);
+		if (remaining > 0) {
+			System.arraycopy(a, off + insertionLength, buffer, 0, remaining);
+			start = remaining;
+		} else if (capacity == 0)
+			start = insertionIndex + insertionLength;
+		size = Math.min(length, size + len);
+	}
+
+	public void flush() {
+		// Nothing need be done here
+	}
+
+	public void close() {
+		start = -1;
+	}
+
+	/** Get the buffer.
+	 *
+	 * This method constructs a new array whose contents is the data written.  Its
+	 * size is equal to the smaller of the number of bytes written or the size of the
+	 * fixed buffer passed to the constructor of this class.
+	 *
+	 * @return The buffer.
+	 */
+	public byte[] getBuffer() {
+		byte[] rc = new byte[Math.min(size, length)];
+		System.arraycopy(buffer, start, rc, 0, size - start);
+		System.arraycopy(buffer, 0, rc, size - start, start);
+		return rc;
+	}
+
+	/** Throw an exception if we've been closed.
+	 *
+	 * @throws IOException If this stream has been closed.
+	 */
+	private void checkIfClosed() throws IOException {
+		if (start == -1) throw new IOException("Can't write to closed stream");
+	}
+
+	/** Length of the buffer. */
+	private int length;
+
+	/** Current size of the data in the buffer. */
+	private int size;
+
+	/** Current start offset of the data in the buffer.  If negative, buffer is closed. */
+	private int start;
+
+	/** The buffer. */
+	private byte[] buffer;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Log.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Log.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Log.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/Log.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,280 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/** The log.
+ *
+ * This class represents the application- or applet-wide logging facility.  If your
+ * application needs to log messages, here's your class.
+ *
+ * <p>To log a message, you call one of the <code>get</code> methods of this class to
+ * yield a {@link LogWriter} object.  You can then call methods like {@link
+ * LogWriter#println(String)} to log a line of text.  A typical invocation is
+ *
+ * <pre>Log.get().println("Buffer of length " + length + " allocated.");</pre>
+ *
+ * <p>This logs the given text using the default source string, and under the default
+ * category, and timestamped with the current time.  You can reuse the
+ * <code>LogWriter</code>, but the timestamp, source, and category won't change.  You
+ * should get a fresh <code>LogWriter</code>.
+ *
+ * <p><strong>Sources</strong> identify independent origins of log messages, such as
+ * independent threads in a program, or independent programs.  Sources are just strings.
+ * If you don't specify a source, you get a default source.  You can set the default
+ * source with {@link #setDefaultSource}.  You <em>always</em> get a source with every
+ * message, even if it's a default source.  If you don't otherwise set a default source
+ * string, the source is "app".
+ *
+ * <p><strong>Categories</strong> identify different classes or priorites of messages.
+ * Categories can be simple strings like "warning" or "debug", or they can be complex
+ * objects.  You get to define your categories.  Your group ought to agree on categories,
+ * though.  If you don't specify a category, you get a default category.  You can set the
+ * default category with {@link #setDefaultCategory}.  If you don't call that method, the
+ * default category is the String object "message".
+ *
+ * <p><strong>Streams</strong> identify independent activities within a program, which
+ * often have transient lifespans.  They're <em>not</em> separate output streams, but
+ * instead are separate, named entities representing separate activities (although a
+ * {@link LogListener} may put messages into separate output streams identified by each
+ * stream).  Activity streams are identified by strings.  To indicate the start and stop
+ * of streams, call {@link #startStream} and {@link #stopStream}.  These send
+ * stream-started and stream-stopped events to the log listeners, who may choose to pay
+ * attention to them or ignore them.  You can use streams to indicate to the log the start
+ * and the stop of activities such as an analyst examining the system, or a server
+ * handling a particular client.
+ *
+ * <p>All messages logged with this class go to one or more {@link LogListener} objects.
+ * A LogListener accepts logging events, such as a message being logged, and does
+ * something with it, such as writing the message to a file.  Call {@link #addLogListener}
+ * to add a log listener.  Logging of a message, starting a stream, and stopping a stream
+ * all get turned into {@link LogEvent}s and are multicasted to every registered listener.
+ *
+ * <p>The logging facility bootstraps itself with one or more log listeners specified by
+ * the system property <code>org.apache.oodt.commons.io.Log.loggers</code>.  This property must be a
+ * space-separated list of complete class names.  The logging facility will create an
+ * object of each class listed and add it as if you had called
+ * <code>addLogListener</code>.  (You can specify system properties on the command line or
+ * in the applet tag.)
+ *
+ * @see LogListener
+ * @see LogWriter
+ * @author Kelly
+ */
+public class Log {
+	/** Currently registered LogListeners.
+	 */
+	private static Vector listeners;
+
+	static {
+		listeners = new Vector();
+		String loggers = System.getProperty("org.apache.oodt.commons.io.Log.loggers", "");
+		StringTokenizer tokenizer = new StringTokenizer(loggers);
+		while (tokenizer.hasMoreTokens()) {
+			String className = tokenizer.nextToken();
+			try {
+				Class clazz = Class.forName(className);
+				LogListener listener = (LogListener) clazz.newInstance();
+				addLogListener(listener);
+			} catch (Exception e) {
+				System.err.println("Can't create log listener object from class " + className + ": " + e);
+				System.exit(1);
+			}
+		}
+	}
+
+	/** Get a writer to log messages.
+	 *
+	 * The writer will log messages with the current time, default category, and
+	 * default source.  Messages will go into the general log.
+	 *
+	 * @return A writer with which you can log messages.
+	 */
+	public static LogWriter get() {
+		if (lastWriter != null && !lastWriter.isFlushed())
+			return lastWriter;
+		else
+			return get(new Date(), getDefaultSource(), getDefaultCategory());
+	}
+
+	/** Get a writer to log messages.
+	 *
+	 * The writer will log messages with the current time, specified category, and
+	 * default source.  Messages will go into the general log.
+	 *
+	 * @param category The messages' category.
+	 * @return A writer with which you can log messages.
+	 */
+	public static LogWriter get(Object category) {
+		return get(new Date(), getDefaultSource(), category);
+	}
+
+	/** Get a writer to log messages.
+	 *
+	 * The writer will log messages with the specified time, specified category, and
+	 * specified source.
+	 *
+	 * @param timestamp The time for messages logged with the returned writer.
+	 * @param source The source of the log message.
+	 * @param category The messages' category.
+	 * @return A writer with which you can log messages.
+	 */
+	public static synchronized LogWriter get(Date timestamp, String source, Object category) {
+		lastWriter = new LogWriter(timestamp, source, category);
+		return lastWriter;
+	}
+
+	/** Start a new log stream.
+	 *
+	 * This method notifies the {@link LogListener}s that a new logging stream has
+	 * started.
+	 *
+	 * @param stream The name of the stream.
+	 * @param timestamp The time the stream started.  To use the current time, pass a new {@link Date} object.
+	 * @param source A string identifying who or what started the stream.
+	 */
+	public static void startStream(String stream, Date timestamp, String source) {
+		LogEvent event = null;
+		for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
+			// Lazily create the event.
+			if (event == null)
+				event = new LogEvent(stream, timestamp, source);
+			((LogListener) e.nextElement()).streamStarted(event);
+		}
+	}
+
+	/** Stop a stream.
+	 *	    
+	 * This method notifies the {@link LogListener}s that a logging stream has stopped.
+	 *
+	 * @param stream The name of the stream that stopped.
+	 */
+	public static void stopStream(String stream) {
+		LogEvent event = null;
+		for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
+			// Lazily create the event.
+			if (event == null)
+				event = new LogEvent(stream);
+			((LogListener) e.nextElement()).streamStopped(event);
+		}
+	}
+
+	/** Log a message.
+	 *
+	 * The {@link LogWriter}s call this when they've built up a complete message and
+	 * want it multicasted to the {@link LogListener}s.
+	 *
+	 * @param timestamp The message's timestamp.
+	 * @param source The source label of the message.
+	 * @param category The message's category.
+	 * @param message The message.
+	 */
+	static void logMessage(Date timestamp, String source, Object category, String message) {
+		LogEvent event = null;
+		for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
+			// Lazily create the event.
+			if (event == null)
+				event = new LogEvent(timestamp, source, category, message);
+			((LogListener) e.nextElement()).messageLogged(event);
+		}
+	}
+
+	/** Set the default source.
+	 *
+	 * This sets the default source label used for logging.
+	 *
+	 * @param source The new default source label.
+	 */
+	public static void setDefaultSource(String source) {
+		if (source == null)
+			throw new IllegalArgumentException("Can't set a null default source");
+		defaultSource = source;
+	}
+
+	/** Get the default source.
+	 *
+	 * @return The default source label.
+	 */
+	public static String getDefaultSource() {
+		return defaultSource;
+	}
+
+	/** Set the default category.
+	 *
+	 * This sets the category object that's used by default for logging.
+	 *
+	 * @param category The new default category object.
+	 */
+	public static void setDefaultCategory(Object category) {
+		if (category == null)
+			throw new IllegalArgumentException("Can't set a null default category");
+		defaultCategory = category;
+	}
+
+	/** Get the default category.
+	 *
+	 * @return The default category object.
+	 */
+	public static Object getDefaultCategory() {
+		return defaultCategory;
+	}
+	
+	/** Add a log listener.
+	 *
+	 * The listener will be notified whenever a message is logged, a stream started,
+	 * or a stream stopped.
+	 *
+	 * @param listener The listener to add.
+	 */
+	public static void addLogListener(LogListener listener) {
+		if (listener == null)
+			throw new IllegalArgumentException("Can't add a null log listener");
+		listeners.addElement(listener);
+	}
+
+	/** Remove a log listener.
+	 *  
+	 * The listener won't receive anymore events unless it's added back.
+	 *
+	 * @param listener The listener to remove.
+	 */
+	public static void removeLogListener(LogListener listener) {
+		if (listener == null)
+			throw new IllegalArgumentException("Can't remove a null log listener");
+		listeners.removeElement(listener);
+	}
+
+	/** The default source label.
+	 */
+	private static String defaultSource = "app";
+
+	/** The default category object.
+	 */
+	private static Object defaultCategory = "message";
+
+	/** Last log writer created so it can be reused. */
+	private static LogWriter lastWriter;
+
+	/** Don't allow instantiation.
+	 *
+	 * When we convert to Java 2, this should throw UnsupportedOperationException.
+	 */
+	private Log() {}
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogEvent.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogEvent.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogEvent.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogEvent.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,172 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.util.Date;
+import java.util.EventObject;
+
+/** Logging event.
+ *
+ * A logging event is generated and delivered to all registered log listeners when a
+ * message was logged, a logging stream was started, or when a logging stream was stopped.
+ *
+ * <p>Use the various query methods to determine the details of the event.  The event
+ * source (from {@link EventObject#getSource}) is always a {@link String}.
+ *
+ * @see Log
+ * @see LogListener
+ * @author Kelly
+ */
+public class LogEvent extends EventObject {
+	/** Create a "message logged" event.
+	 *
+	 * @param timestamp The message's timestamp.
+	 * @param source The source label of the message.
+	 * @param category The message's category.
+	 * @param message The message.
+	 */
+	public LogEvent(Date timestamp, String source, Object category, String message) {
+		super(source);
+		type = MSG_LOGGED;
+		this.timestamp = timestamp;
+		this.category = category;
+		this.message = message;
+	}
+
+	/** Create a "stream started" event.
+	 *
+	 * @param stream The name of the stream.
+	 * @param timestamp The time the stream started.
+	 * @param source A string identifying who or what started the stream.
+	 */
+	public LogEvent(String stream, Date timestamp, String source) {
+		super(source);
+		type = STREAM_STARTED;
+		this.stream = stream;
+		this.timestamp = timestamp;
+	}
+
+	/** Create a "stream stopped" event.
+	 *
+	 * @param stream The name of the stream.
+	 */
+	public LogEvent(String stream) {
+		super(stream);
+		type = STREAM_STOPPED;
+		this.stream = stream;
+	}
+
+	/** Get the category.
+	 *
+	 * For message logged events, this is the category for the message. For all other
+	 * events, this returns null.
+	 *
+	 * @return The category, or null.
+	 */
+	public Object getCategory() {
+		return category;
+	}
+
+	/** Get the message.
+	 *  
+	 * For message logged events, this is the actual message text. For all other
+	 * events, this returns null.
+	 *
+	 *@return The message, or null.
+	 */
+	public String getMessage() {
+		return message;
+	}
+
+	/** Get the stream.
+	 *
+	 * For stream started and stopped events, this is the name of the stream that was
+	 * started or stopped.  For message logged events, this is null.
+	 *
+	 * @return The stream, or null.
+	 */
+	public String getStream() {
+		return stream;
+	}
+
+	/** Get the timestamp.
+	 *
+	 * For message logged and stream started events, this is the timestamp of the
+	 * event.  For stream stopped events, this is null.
+	 *
+	 * @return The timestamp, or null.
+	 */
+	public Date getTimestamp() {
+		return timestamp;
+	}
+
+	/** Return a string representation of this event.
+	 *
+	 * @return A string identifying the type of the event.
+	 */
+	public String toString() {
+		switch (type) {
+			case MSG_LOGGED:
+				return "Log Event (message logged)";
+			case STREAM_STARTED:
+				return "Log Event (stream started)";
+			case STREAM_STOPPED:
+				return "Log Event (stream stopped)";
+			default:
+				return "Unknown Log Event Type";
+		}
+	}
+
+	/** The timestamp: null if this is a "stream stopped" event.
+	 */
+	private Date timestamp;
+
+	/** The stream: null for logging.
+	 */
+	private String stream;
+
+	/** The category: null for stream started/stopped events.
+	 */
+	private Object category;
+
+	/** The message: nonnull only for "message logged" events.
+	 */
+	private String message;
+
+	/** The type of event this is.
+	 *
+	 * This gets one of the values <code>MSG_LOGGED</code>, <code>STREAM_STARTED</code>, or <code>STREAM_STOPPED</code>.
+	 */
+	private int type;
+
+	/** The "message logged" event type.
+	 *
+	 * @see #type
+	 */
+	private static final int MSG_LOGGED = 1;
+
+	/** The "stream started" event type.
+	 *
+	 * @see #type
+	 */
+	private static final int STREAM_STARTED = 2;
+
+	/** The "stream stopped" event type.
+	 *
+	 * @see #type
+	 */
+	private static final int STREAM_STOPPED = 3;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogFilter.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogFilter.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogFilter.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogFilter.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.util.Hashtable;
+
+/** A filter for log messages.
+ *
+ * This is a {@link LogListener} that filters out and passes through certain categories of
+ * log messages to another <code>LogListener</code>.  When you construct this listener,
+ * you pass a boolean flag that indicates its pass-through mode: if true, it passes log
+ * messages by default and filters out specified categories; if false, it filters out
+ * messages by default and passes through specified categories.
+ *
+ * <p>Note that this filter only affects events sent to {@link LogListener#messageLogged}.
+ * Events sent to {@link LogListener#streamStarted} and {@link LogListener#streamStopped}
+ * are passed through regardless.
+ *
+ * <p>Categories used by this filter should implement their {@link Object#hashCode} and
+ * {@link Object#equals} methods.
+ *
+ * @see Log
+ * @author Kelly
+ */
+public class LogFilter implements LogListener {
+	/** Create a log filter.
+	 *
+	 * The log filter passes through and filters out messages before sending them onto
+	 * the given log listener.
+	 *
+	 * @param listener What object will get filtered log messages.
+	 * @param passThrough If true, pass messages through by default and filter out the
+	 * specified <var>categories</var> of messages. If false, filter out messages by
+	 * default and pass through the specified <var>categories</var>.
+	 * @param categories Categories of messages to filter out (if
+	 * <var>passThrough</var> is true) or to pass through (if <var>passThrough</var>
+	 * is false).
+	 */
+	public LogFilter(LogListener listener, boolean passThrough, Object[] categories) {
+		if (listener == null)
+			throw new IllegalArgumentException("Can't filter messages to a null listener");
+		this.listener = listener;
+		this.passThrough = passThrough;
+		if (categories == null) return;
+		for (int i = 0; i < categories.length; ++i)
+			this.categories.put(categories[i], DUMMY);
+	}
+
+	/** Create a log filter.
+	 *  
+	 * The log filter passes through and filters out messages before sending them onto
+	 * the given log listener. The filter starts out empty (with no categories).
+	 *
+	 * @param listener What object will get filtered log messages.
+	 * @param passThrough If true, pass messages through by default. If false, filter out messages by default.
+	 */
+	public LogFilter(LogListener listener, boolean passThrough) {
+		this(listener, passThrough, /*categories*/null);
+	}
+
+	/** Add an additional category.
+	 *
+	 * If the filter is in pass-through mode, messages in this category will be
+	 * filtered out. If the filter is in filter mode, messages in this category will
+	 * be passed through.
+	 *
+	 * @param category The category to add.
+	 */
+	public void addCategory(Object category) {
+		categories.put(category, DUMMY);
+	}
+
+	/** Remove a category.
+	 *
+	 * If the category isn't in the filter, nothing happens.
+	 *
+	 * @param category The category to remove.
+	 */
+	public void removeCategory(Object category) {
+		categories.remove(category);
+	}
+
+	/** Pass on the event unmodified to the registered listener.
+	 *
+	 * @param event The event to pass.
+	 */
+	public void streamStarted(LogEvent event) {
+		listener.streamStarted(event);
+	}
+
+	/** Pass on the event unmodified to the registered listener.
+	 *
+	 * @param event The event to pass.
+	 */
+	public void streamStopped(LogEvent event) {
+		listener.streamStopped(event);
+	}
+
+	/** Filter the event, and possibly pass it onto the registered listener.
+	 *
+	 * @param event The event to filter.
+	 */
+	public void messageLogged(LogEvent event) {
+		boolean found = categories.containsKey(event.getCategory());
+		if ((passThrough && !found) || (!passThrough && found))
+			listener.messageLogged(event);
+	}
+
+	/** Ignore this event.
+	 */
+	public void propertyChange(java.beans.PropertyChangeEvent ignore) {}
+
+	/** If true, pass through by default, otherwise filter out by default.
+	 */
+	protected boolean passThrough;
+
+	/** Table of categories to filter/pass.
+	 *
+	 * This table maps all values to {@link #DUMMY}.  In Java2, we can get rid of
+	 * <code>DUMMY</code> and use a {@link java.util.HashSet} instead.
+	 */
+	protected Hashtable categories = new Hashtable();
+
+	/** The DUMMY value for all mappings in the {@link #categories} table.
+	 */
+	protected static final Object DUMMY = new Object();
+
+	/** The listener on whose behalf we filter.
+	 */
+	protected LogListener listener;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogListener.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogListener.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogListener.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogListener.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,69 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.util.EventObject;
+
+/** Listener for logging events.
+ *
+ * Objects of classes that implement this interface are notified when messages are logged
+ * and when other logging events occur.
+ *
+ * @see Log
+ * @see LogEvent
+ * @author Kelly
+ */
+public interface LogListener extends java.beans.PropertyChangeListener {
+	/** A message got logged.
+	 *
+	 * The <var>event</var> contains the details of the message, including
+	 * 
+	 * <ul>
+	 *   <li>The timestamp of the message, from {@link LogEvent#getTimestamp}.</li>
+	 *   <li>The source of the message, from {@link EventObject#getSource}, which is always
+	 *     a {@link String}.</li>
+	 *   <li>The category of the message, from {@link LogEvent#getCategory}.</li>
+	 *   <li>The message text, from {@link LogEvent#getMessage}.</li>
+	 * </ul>
+	 *
+	 * @param event The logging event.
+	 */
+	void messageLogged(LogEvent event);
+
+	/** A stream got started.
+	 *
+	 * The <var>event</var> contains the details of stream start, including 
+	 *
+	 * <ul>
+	 *   <li>The name of the stream, from {@link LogEvent#getStream}.</li>
+	 *   <li>The time the stream got started, from {@link LogEvent#getTimestamp}</li>
+	 *   <li>The source of the new stream start, from {@link EventObject#getSource} (always a {@link String}).</li>
+	 * </ul>
+	 *
+	 * @param event The logging event.
+	 */
+	void streamStarted(LogEvent event);
+
+	/** A stream was stopped.
+	 *
+	 * The <var>event</var> contains the detail of the stream stop, which is the name
+	 * of the stream, from {@link LogEvent#getStream} or {@link EventObject#getSource}
+	 * (always a {@link String}).
+	 *
+	 * @param event The logging event.
+	 */
+	void streamStopped(LogEvent event);
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogWriter.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogWriter.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogWriter.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/LogWriter.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,375 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.util.Date;
+
+/** A writer for log messages.
+ *
+ * Objects of this class let you write messages into the logging facility. You get these
+ * objects by calling one of the <code>get</code> methods of class {@link Log} and use it
+ * as you would use a {@link java.io.PrintWriter}. This class automatically flushes a message to
+ * the logging facility whenever you call one of the <code>println</code> methods, or
+ * {@link #flush}. It prints all values and objects using the platform's default character
+ * encoding.
+ *
+ * <p>Note that the <code>println</code> methods of this class don't actually write
+ * <em>any</em> line separation characters into the log.  Log listeners will want the
+ * messages without such characters anyway, so this is the correct behavior.
+ *
+ * @see Log
+ * @author Kelly
+ */
+public class LogWriter extends java.io.Writer {
+	/** Constructor.
+	 *
+	 * @param timestamp The time for messages logged with the returned writer.
+	 * @param source The source of the log message.
+	 * @param category The messages' category.
+	 */
+	LogWriter(Date timestamp, String source, Object category) {
+		buf = new StringBuffer(/*length*/ 80);
+		lock = buf;
+		this.timestamp = timestamp;
+		this.source = source;
+		this.category = category;
+		flushed = false;
+	}
+
+	/** Write a single character.
+	 *
+	 * This writes a single character (the low order 16 bits of c) to the log
+	 * writer. The character isn't flushed to the logging facility until you call one
+	 * of the <code>println</code> methods or {@link #flush}.
+	 *
+	 * @param c The character to write.
+	 */
+	public void write(int c) {
+		if (buf == null) return;
+		buf.append((char) c);
+	}
+
+	/** Write a portion of an array of characters.
+	 *
+	 * This writes the given character array starting at offset and going for length bytes.
+	 *
+	 * @param array The character array to write.
+	 * @param offset Where in the array to get characters to write.
+	 * @param length How many characters to write.
+	 */
+	public void write(char[] array, int offset, int length) {
+		if (buf == null || length == 0) return;
+		if (offset < 0 || offset > array.length || length < 0 || (offset+length) > array.length || (offset+length) < 0)
+			throw new IndexOutOfBoundsException("Can't write " + length + " characters at " + offset
+				+ " from array whose length is " + array.length);
+		buf.append(array, offset, length);
+	}
+
+	/** Write an array of characters.
+	 *
+	 * This writes the entire given array.
+	 *
+	 * @param array Array of characters to write.
+	 */
+	public void write(char[] array) {
+		write(array, 0, array.length);
+	}
+
+	/** Write a portion of a string.
+	 *
+	 * This writes length characters from the given string, starting offset characters
+	 * into it.
+	 *
+	 * @param string The string to write.
+	 * @param offset Where in the string to get characters to write.
+	 * @param length How many characters to write.
+	 */
+	public void write(String string, int offset, int length) {
+		if (buf == null || length == 0) return;
+		buf.append(string.substring(offset, offset + length));
+	}
+
+	/** Write a string.
+	 *
+	 * This writes the entire given string.
+	 *
+	 * @param string String to write.
+	 */
+	public void write(String string) {
+		write(string, 0, string.length());
+	}
+
+	/** Flush the log writer.
+	 *
+	 * This sends any text sent to the writer on its way to the logging facility, and beyond.
+	 */
+	public void flush() {
+		if (buf == null) return;
+		Log.logMessage(timestamp, source, category, buf.toString());
+		buf.setLength(0);
+		flushed = true;
+	}
+
+	/** Close the log writer.
+	 *
+	 * <p>This flushes any remaining text to the logging facility and then shuts down
+	 * the log writer. You can't use it again after that (but closing a previously
+	 * closed log writer is OK).
+	 */
+	public void close() {
+		flush();
+		buf = null;
+	}
+
+	/** Print a boolean value.
+	 *
+	 * This prints a boolean value ("true" or "false") into the log.
+	 *
+	 * @param b The <code>boolean</code> to print.
+	 */
+	public void print(boolean b) {
+		write(b? "true" : "false");
+	}
+
+	/** Print a character.
+	 *
+	 * The character is translated into one or more bytes according to the platform's
+	 * default character encoding.
+	 *
+	 * @param c The <code>char</code> to print.
+	 */
+	public void print(char c) {
+		write(String.valueOf(c));
+	}
+
+	/** Print an integer.
+	 *
+	 * @param i The <code>int</code> to print.
+	 */
+	public void print(int i) {
+		write(String.valueOf(i));
+	}
+
+	/** Print a long integer.
+	 *
+	 * @param l The <code>long</code> to print.
+	 */
+	public void print(long l) {
+		write(String.valueOf(l));
+	}
+
+	/** Print a floating-point number.
+	 *
+	 * @param f The <code>float</code> to print.
+	 */
+	public void print(float f) {
+		write(String.valueOf(f));
+	}
+
+	/** Print a double-precision floating-point number.
+	 *
+	 * @param d The <code>double</code> to print.
+	 */
+	public void print(double d) {
+		write(String.valueOf(d));
+	}
+
+	/** Print an array of characters.
+	 *
+	 * @param a The array of chars to print.
+	 */
+	public void print(char[] a) {
+		write(a);
+	}
+
+	/** Print a string.
+	 *
+	 * If the argument is <code>null</code> then the string
+	 * "null" is printed.
+	 *
+	 * @param s The <code>String</code> to print.
+	 */
+	public void print(String s) {
+		if (s == null) s = "null";
+		write(s);
+	}
+
+	/** Print an object.
+	 *
+	 * @param obj The <code>Object</code> to print.
+	 */
+	public void print(Object obj) {
+		write(String.valueOf(obj));
+	}
+
+	/** Print a boolean value and terminate the message.
+	 *
+	 * This prints a boolean into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param b The <code>boolean</code> to print.
+	 */
+	public void println(boolean b) {
+		print(b);
+		println();
+	}
+
+	/** Print a character value and terminate the message.
+	 *
+	 * This prints a character into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param c The <code>char</code> to print.
+	 */
+	public void println(char c) {
+		print(c);
+		println();
+	}
+
+	/** Print an integer value and terminate the message.
+	 *
+	 * This prints an integer into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param i The <code>int</code> to print.
+	 */
+	public void println(int i) {
+		print(i);
+		println();
+	}
+
+	/** Print a long integer value and terminate the message.
+	 *
+	 * This prints a long integer into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param l The <code>long</code> to print.
+	 */
+	public void println(long l) {
+		print(l);
+		println();
+	}
+
+	/** Print a floating-point value and terminate the message.
+	 *
+	 * This prints a floating-point value into the log, and flushes the message to the
+	 * logging facility.
+	 *
+	 * @param f The <code>float</code> to print.
+	 */
+	public void println(float f) {
+		print(f);
+		println();
+	}
+
+	/** Print a double-precision floating-point value and terminate the message.
+	 *
+	 * This prints a double-precision floating-point into the log, and flushes the
+	 * message to the logging facility.
+	 *
+	 * @param d The <code>double</code> to print.
+	 */
+	public void println(double d) {
+		print(d);
+		println();
+	}
+
+	/** Print a character array and terminate the message.
+	 *
+	 * This prints a character array into the log, and flushes the message to the
+	 * logging facility.
+	 *
+	 * @param a The array of chars to print.
+	 */
+	public void println(char[] a) {
+		print(a);
+		println();
+	}
+
+	/** Print a String and terminate the message.
+	 *
+	 * This prints a String into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param s The <code>String</code> to print.
+	 */
+	public void println(String s) {
+		print(s);
+		println();
+	}
+
+	public void println(Throwable t) {
+		if (t == null) 
+			println("Null throwable");
+		else {
+			StackTraceElement[] frames = t.getStackTrace();
+			if (frames == null)
+				println("Null stack trace in " + t.getClass().getName());
+			else {
+				println(t.getClass().getName() + ":");
+				for (int i = 0; i < frames.length; ++i)
+					println(frames[i]);
+			}
+		}
+	}
+
+	/** Print an Object and terminate the message.
+	 *
+	 * This prints an Object into the log, and flushes the message to the logging
+	 * facility.
+	 *
+	 * @param obj The <code>Object</code> to print.
+	 */
+	public void println(Object obj) {
+		print(obj);
+		println();
+	}
+
+	/** Terminate the current message.
+	 *
+	 * This terminates any message text built up and sends it to the logging facility.
+	 */
+	public void println() {
+		flush();
+	}
+
+	/** Are we flushed yet?
+	 *
+	 * @return True if flushed, false otherwise.
+	 */
+	public boolean isFlushed() {
+		return flushed;
+	}
+
+	/** The buffer used to build up the message.  If this is null, then the writer is closed.
+	 */
+	private StringBuffer buf;
+
+	/** The timestamp this LogWriter will use for log messages.
+	 */
+	private Date timestamp;
+
+	/** The source label.
+	 */
+	private String source;
+
+	/** The category of messages generated by this writer.
+	 */
+	private Object category;
+
+	/** Flushed yet? */
+	private boolean flushed;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullInputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullInputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullInputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullInputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An input stream that's always empty.
+ *
+ * @author Kelly
+ * @version $Revision: 1.1.1.1 $
+ */
+public final class NullInputStream extends InputStream {
+        /**
+	 * Construct a null input stream.
+         */
+        public NullInputStream() {
+                open = true;
+        }
+
+        /**
+	 * Read a byte, which you can't do, so you always get -1 to indicate end-of-file.
+         *
+         * @return -1 to indicate end of file.
+         * @throws IOException If the stream is closed.
+         */
+        public int read() throws IOException {
+                checkOpen();
+                return -1;
+        }
+
+        public void close() throws IOException {
+                checkOpen();
+                open = false;
+        }
+
+        /**
+	 * Check if we're open.
+         *
+         * @throws IOException If we're not open.
+         */
+        private void checkOpen() throws IOException {
+                if (!open) throw new IOException("Stream closed");
+        }
+
+        /** Is the stream open? */
+        private boolean open;
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullOutputStream.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullOutputStream.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/NullOutputStream.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,88 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.*;
+import java.util.*;
+
+/** A null output stream.
+ *
+ * This output stream throws away all data it gets.
+ *
+ * @author Kelly
+ */
+public class NullOutputStream extends OutputStream {
+	/** Construct a null output stream.
+	 */
+	public NullOutputStream() {
+		open = true;
+	}
+
+	/** Write a byte to the output stream, which is thrown away.
+	 *
+	 * @param b The byte to toss.
+	 * @throws IOException If the stream is closed.
+	 */
+	public void write(int b) throws IOException {
+		checkOpen();
+	}
+
+	/** Write a byte array to the output stream, which is thrown away.
+	 *
+	 * @param a The array to write.
+	 * @param offset Where in the array to ignore bytes to write.
+	 * @param length How many bytes to ignore.
+	 * @throws IOException If the stream is closed.
+	 */
+	public void write(byte[] a, int offset, int length) throws IOException {
+		if (a == null)
+			throw new NullPointerException("Can't write a null array");
+		else if ((offset < 0) || (offset > a.length) || (length < 0) || ((offset + length) > a.length)
+			|| ((offset + length) < 0))
+			throw new IndexOutOfBoundsException("Offset " + offset + " and length " + length
+				+ " not in array of length " + a.length);
+		checkOpen();
+	}
+
+	/** Flush an output stream, which does nothing.
+	 *
+	 * @throws IOException If the stream is closed.
+	 */
+	public void flush() throws IOException {
+		checkOpen();
+	}
+
+	/** Close an output stream.
+	 *
+	 * @throws IOException If the stream is already closed.
+	 */
+	public void close() throws IOException {
+		checkOpen();
+		open = false;
+	}
+
+	/** Check if we're open.
+	 *
+	 * @throws IOException If we're not open.
+	 */
+	private void checkOpen() throws IOException {
+		if (!open) throw new IOException("Stream closed");
+	}
+
+	/** Is the output stream open? */
+	private boolean open;
+}
+

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/WriterLogger.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/WriterLogger.java?rev=964250&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/WriterLogger.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/io/WriterLogger.java Wed Jul 14 23:04:25 2010
@@ -0,0 +1,166 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements.  See the NOTICE.txt file distributed with this work for
+// additional information regarding copyright ownership.  The ASF licenses this
+// file to you under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+package org.apache.oodt.commons.io;
+
+import java.io.Writer;
+import java.io.IOException;
+import org.apache.oodt.commons.util.*;
+
+/** Log messages to a <code>Writer</code>.
+ *
+ * This class defines a {@link LogListener} that logs its messages to a character stream
+ * {@link Writer}.  This formats and writes to the output stream all log events passed to
+ * {@link LogListener#messageLogged}, one per line.  Each line is separated with the
+ * system's line separator characters, specified by the <code>line.separator</code> system
+ * property.
+ *
+ * <p>It ignores all events passed to {@link LogListener#streamStarted} and
+ * {@link LogListener#streamStopped}.
+ *
+ * <p>It quietly hides all IO errors that may occur during writing.
+ *
+ * @see Log
+ * @author Kelly
+ */
+public class WriterLogger implements LogListener {
+	/** Constructor given an output stream.
+	 *
+	 * Construct a writer logger that writes message events to the given output stream
+	 * and flushes the stream after every logged message.
+	 *
+	 * @param outputStream The output stream to which to write events.
+	 */
+	public WriterLogger(java.io.OutputStream outputStream) {
+		this(new java.io.BufferedWriter(new java.io.OutputStreamWriter(outputStream)), /*autoFlush*/ true);
+	}
+
+	/** Constructor given a writer.
+	 *
+	 * Construct a writer logger that writes message events to the given writer and
+	 * flushes the stream after every logged message.
+	 *
+	 * @param writer The writer to which to write events.
+	 */
+	public WriterLogger(Writer writer) {
+		this(writer, /*autoFlush*/ true);
+	}
+
+	/** General constructor.
+	 *
+	 * Construct a writer logger that writes message events to the given writer and
+	 * optionally flushes the stream after every logged message.
+	 *
+	 * @param writer The writer to which to write events.
+	 * @param autoFlush If true, call flush on the writer after every message
+	 * logged. If false, don't call flush.
+	 */
+	public WriterLogger(Writer writer, boolean autoFlush) {
+		if (writer == null)
+			throw new IllegalArgumentException("Can't write to a null writer");
+		this.writer = writer;
+		this.autoFlush = autoFlush;
+		this.lineSep = System.getProperty("line.separator", "\n");
+	}
+
+	/** Close the writer.
+	 *
+	 * This closes the writer to which this logger was logging.  Future message events
+	 * are ignored and not written.
+	 */
+	public final void close() {
+		if (writer == null) return;
+		try {
+			writer.close();
+		} catch (IOException ignore) {}
+		writer = null;
+	}
+
+	/** Log a message to the writer.
+	 *
+	 * This method first calls {@link #formatMessage} to format the message, and then
+	 * writes the message to the output stream.  
+	 * 
+	 * @param event The event describing the message that was logged.
+	 */
+	public final void messageLogged(LogEvent event) {
+		if (writer == null) return;
+		try {
+			writer.write(formatMessage(event.getTimestamp(), (String) event.getSource(), event.getCategory(),
+				event.getMessage()) + lineSep);
+			if (autoFlush) writer.flush();
+		} catch (IOException ignore) {}
+	}
+
+	/** Format a message for logging.
+	 *
+	 * This method formats a message as follows: 
+	 *
+	 * <p><code><var>epochTime</var> (<var>time</var>) <var>source</var> <var>category</var>:
+	 * <var>message</var> 
+	 *
+	 * <p>where <var>epochTime</var> is the time in milliseconds since midnight, 1st
+	 * January 1970 GMT, <var>time</var> is human-readable time, <var>source</var> is
+	 * the source of the message, <var>category</var> is the category under which the
+	 * message was logged, and <var>message</var> is the message.
+	 *
+	 * <p>You can override this method and provide your own formatting.
+	 *
+	 * @param timestamp Timestamp for the message.
+	 * @param source Source of the message.
+	 * @param category Category of the message.
+	 * @param message The message text.
+	 */
+	protected String formatMessage(java.util.Date timestamp, String source, Object category, String message) {
+		return ("\n" + DateConvert.isoFormat(timestamp) + " " + source + " " + category + ": " + message);
+	}
+
+	/** Ignore the stream started event.
+	 *
+	 * @param event The event to ignore.
+	 */
+	public void streamStarted(LogEvent event) {
+		return;
+	}
+
+	/** Ignore the stream stopped event.
+	 *
+	 * @param event The event to ignore.
+	 */
+	public void streamStopped(LogEvent event) {
+		return;
+	}
+
+	public void propertyChange(java.beans.PropertyChangeEvent ignore) {}
+
+	/** The writer to which we write log messages.
+	 *
+	 * If null, then we were closed.  This is protected so you can extend this class
+	 * and log other events normally ignored.
+	 */
+	protected Writer writer;
+	
+	/** If true, flush after every message logged.
+	 *
+	 * This is protected so you can extend this class and log other events normally ignored.
+	 */
+	protected boolean autoFlush;
+
+	/** What the line separator is.
+	 *
+	 * This is protected so you can extend this class and log other events normally ignored.
+	 */
+	protected String lineSep;
+}