Java Tutorial/File/OutputStream — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 05:20, 1 июня 2010

A null output stream. All data written to this stream is ignored.

/**
 * 
 * JFreeReport : a free Java reporting library
 * 
 *
 * Project Info:  http://reporting.pentaho.org/
 *
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
 * in the United States and other countries.]
 *
 * ------------
 * NullOutputStream.java
 * ------------
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 */
import java.io.IOException;
import java.io.OutputStream;
/**
 * A null output stream. All data written to this stream is ignored.
 *
 * @author Thomas Morgner
 */
public class NullOutputStream extends OutputStream
{
  /**
   * Default constructor.
   */
  public NullOutputStream ()
  {
  }
  /**
   * Writes to the stream (in this case, does nothing).
   *
   * @param i the value.
   * @throws IOException if there is an I/O problem.
   */
  public void write (final int i)
          throws IOException
  {
    // no i wont do anything here ...
  }
  /**
   * Writes to the stream (in this case, does nothing).
   *
   * @param bytes the bytes.
   * @throws IOException if there is an I/O problem.
   */
  public void write (final byte[] bytes)
          throws IOException
  {
    // no i wont do anything here ...
  }
  /**
   * Writes to the stream (in this case, does nothing).
   *
   * @param bytes the bytes.
   * @param off   the start offset in the data.
   * @param len   the number of bytes to write.
   * @throws IOException if there is an I/O problem.
   */
  public void write (final byte[] bytes, final int off, final int len)
          throws IOException
  {
    // no i wont do anything here ...
  }
}





Byte Counting OutputStream

import java.io.IOException;
import java.io.OutputStream;
/**
 * Output stream that counts bytes written to it (but discards them).
 * 
 * @author Jonathan Locke
 */
public final class ByteCountingOutputStream extends OutputStream
{
  private long size;
  /**
   * @see java.io.OutputStream#write(int)
   */
  public void write(int b) throws IOException
  {
    size++;
  }
  /**
   * @see java.io.OutputStream#write(byte[], int, int)
   */
  public void write(byte b[], int off, int len) throws IOException
  {
    size += len;
  }
  /**
   * @return Number of bytes written to this stream
   */
  public long size()
  {
    return size;
  }
}





Memory Byte Array OutputStream

/**
 * 
 * JFreeReport : a free Java reporting library
 * 
 *
 * Project Info:  http://reporting.pentaho.org/
 *
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
 * in the United States and other countries.]
 *
 * ------------
 * MemoryByteArrayOutputStream.java
 * ------------
 * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
 */
import java.io.IOException;
import java.io.OutputStream;
/**
 * A string writer that is able to write large amounts of data. The original
 * StringWriter contained in Java doubles its buffersize everytime the buffer
 * overflows. This is nice with small amounts of data, but awfull for huge
 * buffers.
 * 
 * @author Thomas Morgner
 */
public class MemoryByteArrayOutputStream extends OutputStream {
  private int maximumBufferIncrement;
  private int cursor;
  private byte[] buffer;
  private byte[] singleIntArray;
  /**
   * Create a new character-stream writer whose critical sections will
   * synchronize on the writer itself.
   */
  public MemoryByteArrayOutputStream() {
    this(4096, 65536);
  }
  /**
   * Create a new character-stream writer whose critical sections will
   * synchronize on the writer itself.
   */
  public MemoryByteArrayOutputStream(final int bufferSize, final int maximumBufferIncrement) {
    this.maximumBufferIncrement = maximumBufferIncrement;
    this.buffer = new byte[bufferSize];
    this.singleIntArray = new byte[1];
  }
  /**
   * Write a portion of an array of characters.
   * 
   * @param cbuf
   *          Array of characters
   * @param off
   *          Offset from which to start writing characters
   * @param len
   *          Number of characters to write
   * @throws java.io.IOException
   *           If an I/O error occurs
   */
  public synchronized void write(final byte[] cbuf, final int off, final int len)
      throws IOException {
    if (len < 0) {
      throw new IllegalArgumentException();
    }
    if (off < 0) {
      throw new IndexOutOfBoundsException();
    }
    if (cbuf == null) {
      throw new NullPointerException();
    }
    if ((len + off) > cbuf.length) {
      throw new IndexOutOfBoundsException();
    }
    ensureSize(cursor + len);
    System.arraycopy(cbuf, off, this.buffer, cursor, len);
    cursor += len;
  }
  /**
   * Writes <code>b.length</code> bytes from the specified byte array to this
   * output stream. The general contract for <code>write(b)</code> is that it
   * should have exactly the same effect as the call <code>write(b, 0,
   * b.length)</code>.
   * 
   * @param b
   *          the data.
   * @throws java.io.IOException
   *           if an I/O error occurs.
   * @see java.io.OutputStream#write(byte[], int, int)
   */
  public void write(final byte[] b) throws IOException {
    write(b, 0, b.length);
  }
  /**
   * 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.
   * <p/> Subclasses of <code>OutputStream</code> must provide an
   * implementation for this method.
   * 
   * @param b
   *          the <code>byte</code>.
   * @throws java.io.IOException
   *           if an I/O error occurs. In particular, an
   *           <code>IOException</code> may be thrown if the output stream has
   *           been closed.
   */
  public synchronized void write(final int b) throws IOException {
    this.singleIntArray[0] = (byte) (0xFF & b);
    write(singleIntArray, 0, 1);
  }
  private void ensureSize(final int size) {
    if (this.buffer.length >= size) {
      return;
    }
    final int computedSize = (int) Math.min((this.buffer.length + 1) * 1.5, this.buffer.length
        + maximumBufferIncrement);
    final int newSize = Math.max(size, computedSize);
    final byte[] newBuffer = new byte[newSize];
    System.arraycopy(this.buffer, 0, newBuffer, 0, cursor);
    this.buffer = newBuffer;
  }
  /**
   * Flush the stream. If the stream has saved any characters from the various
   * write() methods in a buffer, write them immediately to their intended
   * destination. Then, if that destination is another character or byte stream,
   * flush it. Thus one flush() invocation will flush all the buffers in a chain
   * of Writers and OutputStreams. <p/> If the intended destination of this
   * stream is an abstraction provided by the underlying operating system, for
   * example a file, then flushing the stream guarantees only that bytes
   * previously written to the stream are passed to the operating system for
   * writing; it does not guarantee that they are actually written to a physical
   * device such as a disk drive.
   * 
   * @throws java.io.IOException
   *           If an I/O error occurs
   */
  public void flush() throws IOException {
    if ((buffer.length - cursor) > 50000) {
      System.out.println("WASTED: " + (buffer.length - cursor));
    }
  }
  /**
   * Close the stream, flushing it first. Once a stream has been closed, further
   * write() or flush() invocations will cause an IOException to be thrown.
   * Closing a previously-closed stream, however, has no effect.
   * 
   * @throws java.io.IOException
   *           If an I/O error occurs
   */
  public void close() throws IOException {
  }
  public synchronized byte[] toByteArray() {
    final byte[] retval = new byte[cursor];
    System.arraycopy(buffer, 0, retval, 0, cursor);
    return retval;
  }
  public int getLength() {
    return cursor;
  }
  public byte[] getRaw() {
    if ((buffer.length - cursor) > 50000) {
      System.out.println("WASTED: " + (buffer.length - cursor) + " Length: " + buffer.length);
    }
    return buffer;
  }
}





Provides true Closable semantics ordinarily missing in a {@link java.io.ByteArrayOutputStream}.

/* Copyright (c) 2001-2009, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/* $Id: ClosableByteArrayOutputStream.java 2946 2009-03-22 17:44:48Z fredt $ */
/**
 * @todo - finer-grained synchronization to reduce average
 * potential monitor contention
 */
/**
 * Provides true Closable semantics ordinarily missing in a
 * {@link java.io.ByteArrayOutputStream}. 
 *
 * Accumulates output in a byte array that automatically grows as needed.
 *
 * Data is retrieved using <tt>toByteArray()</tt>,
 * <tt>toByteArrayInputStream()</tt>, <tt>toString()</tt> and
 * <tt>toString(encoding)</tt>. 
 *
 * {@link #close() Closing} a <tt>ClosableByteArrayOutputStream</tt> prevents
 * further write operations, but all other operations may succeed until after
 * the first invocation of {@link #free() free()}.
 *
 * Freeing a <tt>ClosableByteArrayOutputStream</tt> closes the stream and
 * releases the internal buffer, preventing successful invocation of all
 * operations, with the exception of <tt>size()<tt>, <tt>close()</tt>,
 * <tt>isClosed()</tt>, <tt>free()</tt> and <tt>isFreed()</tt>. 
 *
 * This class is especially useful when an accumulating output stream must be
 * handed off to an extenal client under contract that the stream should
 * exhibit true Closable behaviour in response both to internally tracked
 * events and to client invocation of the <tt>OutputStream.close()</tt> method.
 *
 * @author boucherb@users
 * @version 1.9.0
 * @since 1.9.0
 */
public class ClosableByteArrayOutputStream extends OutputStream {
    /**
     * Data buffer.
     */
    protected byte[] buf;
    /**
     * # of valid bytes in buffer.
     */
    protected int count;
    /**
     * Whether this stream is closed.
     */
    protected boolean closed;
    /**
     * Whether this stream is freed.
     */
    protected boolean freed;
    /**
     * Creates a new output stream. 
     *
     * The buffer capacity is initially 32 bytes, though its size increases
     * if necessary.
     */
    public ClosableByteArrayOutputStream() {
        this(32);
    }
    /**
     * Creates a new output stream with a buffer capacity of the specified
     * <tt>size</tt>, in bytes.
     *
     * @param size the initial size.
     * @exception IllegalArgumentException if size is negative.
     */
    public ClosableByteArrayOutputStream(int size)
    throws IllegalArgumentException {
        if (size < 0) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + size);    // NOI18N
        }
        buf = new byte[size];
    }
    /**
     * Writes the specified single byte.
     *
     * @param b the single byte to be written.
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #close() closed}.
     */
    public synchronized void write(int b) throws IOException {
        checkClosed();
        int newcount = count + 1;
        if (newcount > buf.length) {
            buf = copyOf(buf, Math.max(buf.length << 1, newcount));
        }
        buf[count] = (byte) b;
        count      = newcount;
    }
    /**
     * Writes the specified portion of the designated octet sequence. 
     *
     * @param b the data.
     * @param off the start offset in the data.
     * @param len the number of bytes to write.
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #close() closed}.
     */
    public synchronized void write(byte b[], int off,
                                   int len) throws IOException {
        checkClosed();
        if ((off < 0) || (off > b.length) || (len < 0)
                || ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        int newcount = count + len;
        if (newcount > buf.length) {
            buf = copyOf(buf, Math.max(buf.length << 1, newcount));
        }
        System.arraycopy(b, off, buf, count, len);
        count = newcount;
    }
    /**
     * By default, does nothing. 
     *
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #close() closed}.
     */
    public void flush() throws IOException {
        checkClosed();
    }
    /**
     * Writes the complete contents of this stream"s accumulated data to the
     * specified output stream. 
     *
     * The operation occurs as if by calling <tt>out.write(buf, 0, count)</tt>.
     *
     * @param out the output stream to which to write the data.
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #free() freed}.
     */
    public synchronized void writeTo(OutputStream out) throws IOException {
        checkFreed();
        out.write(buf, 0, count);
    }
    /**
     * Returns the current capacity of this stream"s data buffer.
     *
     * @return  the length of the internal data array
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #free() freed}.
     */
    public synchronized int capacity() throws IOException {
        checkFreed();
        return buf.length;
    }
    /**
     * Resets the <tt>count</tt> field of this output stream to zero, so that
     * all currently accumulated data is effectively discarded. 
     *
     * Further write operations will reuse the allocated buffer space. 
     *
     * @see #count
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #close() closed}.
     */
    public synchronized void reset() throws IOException {
        checkClosed();
        count = 0;
    }
    /**
     * Attempts to reduce this stream"s capacity to its current size. 
     *
     * If the data buffer is larger than necessary to hold its current sequence
     * of bytes, then it may be resized to become more space efficient.
     * Calling this method may, but is not required to, affect the value
     * returned by a subsequent call to the {@link #capacity()} method. 
     *
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #free() freed}.
     */
    public synchronized void trimToSize() throws IOException {
        checkFreed();
        if (buf.length > count) {
            buf = copyOf(buf, count);
        }
    }
    /**
     * Retrieves a copy of this stream"s accumated data, as a byte array.
     *
     * @return a copy of this stream"s accumated data, as a byte array.
     * @see #size()
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #free() freed}.
     */
    public synchronized byte[] toByteArray() throws IOException {
        checkFreed();
        return copyOf(buf, count);
    }
    /**
     * Returns the current size of this stream"s accumated data.
     *
     * @return the value of the <tt>count</tt> field, which is the number
     *      of valid bytes in this output stream.
     * @see #count
     * @throws java.io.IOException never
     */
    public synchronized int size() throws IOException {
        return count;
    }
    /**
     * Sets the size of this stream"s accumulated data. 
     *
     * @param   newSize the new size
     * @throws  ArrayIndexOutOfBoundsException if new size is negative
     */
    public synchronized void setSize(int newSize) {
        if (newSize < 0) {
            throw new ArrayIndexOutOfBoundsException(newSize);
        } else if (newSize > buf.length) {
            buf = copyOf(buf, Math.max(buf.length << 1, newSize));
        }
        count = newSize;
    }
    /**
     * Performs an effecient (zero-copy) conversion of the data accumulated in
     * this output stream to an input stream. 
     *
     * To ensure the future integrity of the resulting input stream, {@link
     * #free() free} is invoked upon this output stream as a side-effect.
     *
     * @return an input stream representing this output stream"s accumulated
     *      data
     * @throws java.io.IOException if an I/O error occurs.
     *      In particular, an <tt>IOException</tt> may be thrown
     *      if this output stream has been {@link #free() freed}.
     */
    public synchronized ByteArrayInputStream toByteArrayInputStream()
    throws IOException {
        checkFreed();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(buf, 0,
            count);
        free();
        return inputStream;
    }
    /**
     * Converts this stream"s accumuated data into a string, translating bytes
     * into characters according to the platform"s default character encoding.
     *
     * @return String translated from this stream"s accumuated data.
     * @throws RuntimeException may be thrown if this output stream has been
     *      {@link #free() freed}.
     */
    public synchronized String toString() {
        try {
            checkFreed();
        } catch (IOException ex) {
            throw new RuntimeException(ex.toString());
        }
        return new String(buf, 0, count);
    }
    /**
     * Converts this stream"s accumuated data into a string, translating bytes
     * into characters according to the specified character encoding.
     *
     * @return String translated from the buffer"s contents.
     * @param enc a character-encoding name.
     * @throws java.io.IOException may be thrown if this output stream has been
     *      {@link #free() freed}.
     * @throws UnsupportedEncodingException If the named encoding is not
     *      supported.
     */
    public synchronized String toString(String enc)
    throws IOException, UnsupportedEncodingException {
        checkFreed();
        return new String(buf, 0, count, enc);
    }
    /**
     * Closes this object for further writing. 
     *
     * Other operations may continue to succeed until after the first invocation
     * of {@link #free() free()}. 
     *
     * @throws java.io.IOException if an I/O error occurs (default: never)
     */
    public synchronized void close() throws IOException {
        closed = true;
    }
    /**
     * Retrieves whether this stream is closed. 
     * @return <tt>true</tt> if this stream is closed, else <tt>false</tt>
     */
    public synchronized boolean isClosed() {
        return closed;
    }
    /**
     * Closes this object and releases the underlying buffer for
     * garbage collection. 
     *
     * @throws java.io.IOException if an I/O error occurs while closing
     *      this stream (default: never).
     */
    public synchronized void free() throws IOException {
        closed = true;
        freed  = true;
        buf    = null;
        count  = 0;
    }
    /**
     * Retrieves whether this stream is freed. 
     *
     * @return <tt>true</tt> if this stream is freed; else <tt>false</tt>.
     */
    public synchronized boolean isFreed() {
        return freed;
    }
    /**
     * Tests whether this stream is closed. 
     *
     * @throws java.io.IOException if this stream is closed.
     */
    protected synchronized void checkClosed() throws IOException {
        if (closed) {
            throw new IOException("stream is closed.");    // NOI18N
        }
    }
    /**
     * Tests whether this stream is freed. 
     *
     * @throws java.io.IOException if this stream is freed.
     */
    protected synchronized void checkFreed() throws IOException {
        if (freed) {
            throw new IOException("stream buffer is freed.");    // NOI18N
        }
    }
    /**
     * Retrieves a copy of <tt>original</tt> with the given
     * <tt>newLength</tt>. 
     *
     * @param original the object to copy
     * @param newLength the length of the copy
     * @return copy of <tt>original</tt> with the given <tt>newLength</tt>
     */
    protected byte[] copyOf(byte[] original, int newLength) {
        byte[] copy = new byte[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
}





Transfers all bytes that can be read from one stream to another stream.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Main {

  /**
   * Transfers all bytes that can be read from <tt>in</tt> to <tt>out</tt>.
   *
   * @param in The InputStream to read data from.
   * @param out The OutputStream to write data to.
   * @return The total number of bytes transfered.
   */
  public static final long transfer(InputStream in, OutputStream out)
    throws IOException
  {
    long totalBytes = 0;
    int bytesInBuf = 0;
    byte[] buf = new byte[4096];
    while ((bytesInBuf = in.read(buf)) != -1) {
      out.write(buf, 0, bytesInBuf);
      totalBytes += bytesInBuf;
    }
    return totalBytes;
  }
}





Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when finished.

/*
 * JBoss DNA (http://www.jboss.org/dna)
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * See the AUTHORS.txt file in the distribution for a full listing of 
 * individual contributors. 
 *
 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
 * is licensed to you under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * JBoss DNA 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
import java.io.IOException;
import java.io.OutputStream;
/**
 * @author Randall Hauch
 */
public class Main {
  /**
   * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when
   * finished.
   * 
   * @param content the content to write to the stream; may be null
   * @param stream the stream to which the content is to be written
   * @throws IOException
   * @throws IllegalArgumentException if the stream is null
   */
  public static void write( String content,
                            OutputStream stream ) throws IOException {
      boolean error = false;
      try {
          if (content != null) {
              byte[] bytes = content.getBytes();
              stream.write(bytes, 0, bytes.length);
          }
      } catch (IOException e) {
          error = true; // this error should be thrown, even if there is an error flushing/closing stream
          throw e;
      } catch (RuntimeException e) {
          error = true; // this error should be thrown, even if there is an error flushing/closing stream
          throw e;
      } finally {
          try {
              stream.flush();
          } catch (IOException e) {
              if (!error) throw e;
          } finally {
              try {
                  stream.close();
              } catch (IOException e) {
                  if (!error) throw e;
              }
          }
      }
  }
}