Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / October 2007

Tip: Looking for answers? Try searching our database.

Why should close() close the underlying stream?

Thread view: 
Xiao Ma - 24 Oct 2007 02:33 GMT
A stream may have an underlying stream. For example,
   FileOutputStream fos = new FileOutputStream("foo");
   BufferedOutputStream bos = new BufferedOutputStream(fos);
fos is the underlying stream for bos.

Now if I call bos.close(), it will also close its underlying stream.
Why should the underlying stream be closed? I can think of some cases
where I want to continue to write to the underlying output stream
after I close the "outer" output stream.

I know it's useful if people do
   BufferedOutputStream bos = new BufferedOutputStream(new
FileOutputStream("foo"));
and you don't have a handle on the underlying stream. But you can
always define a variable for the underlying stream.

I have a method that allows the caller to pass in an output stream.
Then I want to use a BufferedOutputStream to write to it. After that,
I should close the BufferedOutputStream before exiting the method. But
that closes the output stream passed in by the caller and the caller
can no longer write to it. For example,
   void foo(OutputStream out) {
       try {
           BufferedOutputStream bout = new BufferedOutputStream(out);
           bout.write(blahblah);
       }
       finally {
           bout.close(); // I should close any stream I created but
this closes the underlying stream, too.
       }
   }
Lew - 24 Oct 2007 02:50 GMT
> A stream may have an underlying stream. For example,
>     FileOutputStream fos = new FileOutputStream("foo");
[quoted text clipped - 5 lines]
> where I want to continue to write to the underlying output stream
> after I close the "outer" output stream.

If you don't want the stream closed, don't close the stream.

Signature

Lew

Eric Sosman - 24 Oct 2007 03:02 GMT
> A stream may have an underlying stream. For example,
>     FileOutputStream fos = new FileOutputStream("foo");
[quoted text clipped - 6 lines]
> after I close the "outer" output stream.
> [...]

    The people who designed java.io apparently felt that
such cases were a small minority, and chose to simplify
matters for what they considered the much more common case.

    If you need the "one at a time" discipline, you can
get it without much work.  Just extend FileOutputStream (or
whatever) with a class of your own that inherits almost all
its methods from the superclass, but overrides close() and
ignores it.  If desired you could add a reallyClose() method
that forwards to super.close(), and/or a getActualStream()
method that returns the superclass instance.

Signature

Eric Sosman
esosman@ieee-dot-org.invalid

Daniel Pitts - 24 Oct 2007 05:03 GMT
>> A stream may have an underlying stream. For example,
>>     FileOutputStream fos = new FileOutputStream("foo");
[quoted text clipped - 18 lines]
> that forwards to super.close(), and/or a getActualStream()
> method that returns the superclass instance.

You can't return a superclass instance, however you probably should use
inheritance here anyway.  You should probably implement an OutputStream
that delegates to another OutputStream except for close().

Using that approach, you could use reallyClose() or getDelegate().close()

Although, as has been mentioned, usually if you close a stream, you
really DO want to close the underlying streams too.

Signature

Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>

Lew - 24 Oct 2007 05:14 GMT
> Although, as has been mentioned, usually if you close a stream, you
> really DO want to close the underlying streams too.

I can't seem to think of a use case for not closing the underlying stream.
Every time I think I have one, it's simpler not to close the stream at all.

Signature

Lew

Silvio Bierman - 24 Oct 2007 12:59 GMT
>> Although, as has been mentioned, usually if you close a stream, you
>> really DO want to close the underlying streams too.
>
> I can't seem to think of a use case for not closing the underlying
> stream. Every time I think I have one, it's simpler not to close the
> stream at all.

Hello Lew,

I had some use cases for this in the past. I was writing a compound XML
doc to an OutputStream (HTTP response) and needed a library to write an
XML representation of one of it's object instances somewhere in to an
element in the total XML doc.
Unfortunately the library insisted on closing the stream that I passed
it. Off course that would prevent me to complete the surrounding XML
document.
I needed to be able to pass it an OutputStream that would just extend my
current OutputStream and then just flush it when it was done. Simple
enough, off course.

Just create a wrapping OutputStream that only calls wrapped.flush() in
its close method.

Silvio Bierman
Eric Sosman - 24 Oct 2007 13:41 GMT
>>> Although, as has been mentioned, usually if you close a stream, you
>>> really DO want to close the underlying streams too.
[quoted text clipped - 18 lines]
> Just create a wrapping OutputStream that only calls wrapped.flush() in
> its close method.

    Another approach might be to use a SequenceInputStream.
You would pass individual PipedOutputStream objects to the
libraries that write and close, connecting their other ends
to PipedInputStreams that feed the SIS, which you'd then
just read and copy to the final output.

    Or if the library doesn't generate "too much" data you
could hand it a ByteArrayOutputStream, let it be written and
closed, and then dump its data to your actual output.

    There may be a couple dozen ways to skin this cat ...

Signature

Eric Sosman
esosman@ieee-dot-org.invalid

Lew - 24 Oct 2007 14:22 GMT
Silvio Bierman wrote:
>> Unfortunately the library insisted on closing the stream that I passed
>> it. Off course that would prevent me to complete the surrounding XML
>> document.
>> I needed to be able to pass it an OutputStream that would just extend
>> my current OutputStream and then just flush it when it was done.
>> Simple enough, off course.

That's a bad library, not an argument for keeping the underlying stream open.
 Libraries should not close streams passed into them.

Signature

Lew

Arne Vajhøj - 25 Oct 2007 03:01 GMT
>> Although, as has been mentioned, usually if you close a stream, you
>> really DO want to close the underlying streams too.
>
> I can't seem to think of a use case for not closing the underlying
> stream. Every time I think I have one, it's simpler not to close the
> stream at all.

A very similar question showed up in the C# group and it turned
out that Jon Skeet had a wrapper class for it on his web site.

Arne
Eric Sosman - 24 Oct 2007 13:32 GMT
>> [...]   If desired you could add a reallyClose() method
>> that forwards to super.close(), and/or a getActualStream()
>> method that returns the superclass instance.
>>
> You can't return a superclass instance, [...]

    Gugghh!  You're right, of course; sorry for the red
herring.

Signature

Eric Sosman
esosman@ieee-dot-org.invalid

Arne Vajhøj - 25 Oct 2007 02:53 GMT
>>> [...]   If desired you could add a reallyClose() method
>>> that forwards to super.close(), and/or a getActualStream()
[quoted text clipped - 4 lines]
>     Gugghh!  You're right, of course; sorry for the red
> herring.

Something similar can be made to work.

package october;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

public class NonClosingStream extends OutputStream {
    private OutputStream real;
    public NonClosingStream(OutputStream real) {
        this.real = real;
    }
    public void close() throws IOException {
        // ignore
    }
    public void realclose() throws IOException {
        real.close();
    }
    public boolean equals(Object obj) {
        return real.equals(obj);
    }
    public void flush() throws IOException {
        real.flush();
    }
    public int hashCode() {
        return real.hashCode();
    }
    public String toString() {
        return real.toString();
    }
    public void write(byte[] b, int off, int len) throws IOException {
        real.write(b, off, len);
    }
    public void write(byte[] b) throws IOException {
        real.write(b);
    }
    public void write(int b) throws IOException {
        real.write(b);
    }
    public static void main(String[] args) throws IOException {
        // OutputStream os = new FileOutputStream("C:\\z.z");
        NonClosingStream os = new NonClosingStream(new
FileOutputStream("C:\\z.z"));
        PrintStream ps = new PrintStream(os);
        ps.println("Hello");
        ps.close();
        PrintStream ps2 = new PrintStream(os);
        ps2.println("Hello");
        ps2.close();
        os.realclose();
    }
}

Arne
Esmond Pitt - 26 Oct 2007 09:31 GMT
> Something similar can be made to work.
>
> public class NonClosingStream extends OutputStream {

It's *much* simpler than that:

public class NonClosingStream extends FilterOutputStream {
  public NonClosingStream(OutputStream out) { super(out); }
  public void close() {}
}
Zig - 26 Oct 2007 20:05 GMT
>> Something similar can be made to work.
>>  public class NonClosingStream extends OutputStream {
[quoted text clipped - 5 lines]
>    public void close() {}
> }

While that will actually work, pushing data through that stream is  
extremely inefficient.

While FilterInputStream.read(byte[], int, int) passes through the buffered  
read to the underlying stream, FilterOutputStream.write(byte[], int, int)  
breaks the write into unbuffered byte-by-byte writes.

So, you *really* want

public class NonClosingStream extends FilterOutputStream {
   public NonClosingStream(OutputStream out) { super(out); }
   public void write(byte[] buf, int off, int len) throws IOException {
         out.write(buf, off, len);
   }
   public void close() {}
}

Just another fun qwerk from classes designed back in the Java 1.0 days. As  
an alternative, check out org.apache.commons.io.input.ProxyInputStream.  
The Apache Commons IO library seems to have tried to learn from these  
little problems.

HTH,

-Zig
Arne Vajhøj - 27 Oct 2007 18:11 GMT
>>> Something similar can be made to work.
>>>  public class NonClosingStream extends OutputStream {
[quoted text clipped - 27 lines]
> org.apache.commons.io.input.ProxyInputStream. The Apache Commons IO
> library seems to have tried to learn from these little problems.

Yet another example of that extends can tie your code to the internals
of the class you are extending.

Not that I was aware - I did not use FilterOutputStream, because I did
not know about the class !

Arne
Zig - 24 Oct 2007 03:02 GMT
> A stream may have an underlying stream. For example,
>     FileOutputStream fos = new FileOutputStream("foo");
[quoted text clipped - 5 lines]
> where I want to continue to write to the underlying output stream
> after I close the "outer" output stream.

Because the streams in java.io are simple streams - the concept of  
segregating streams and embedding multiple sub-streams are much higher  
abstraction that must be implemented outside of the java.io package.

Think of it this way: once you have written your stream, if you don't want  
to close the underlying stream, how do you propose to write the  
corresponding read(InputStream) method to read that data back in? A simple  
BufferedInputStream is unbounded, so it will auto-read into the source  
stream past the point that your BufferedOutputStream closed it.

> I know it's useful if people do
>     BufferedOutputStream bos = new BufferedOutputStream(new
[quoted text clipped - 17 lines]
>         }
>     }

for BufferedOutputStream, a simple flush() will suffice. If you've got  
something fancier that needs to write a terminator, or has special  
handling for the last block of data, consider my comments above.

HTH,

-Zig
Mike Schilling - 24 Oct 2007 06:34 GMT
> for BufferedOutputStream, a simple flush() will suffice.

That's true for almost all output filter streams and writers:
BufferedOutputStream, DataOutputStream, etc.  When you're done with the
filter, you can just flush() it and stop using it.

The anaologus situation with input filter streams and readers is a problem;
having attached, say, a DataInputStream to an InputStream, it's not safe to
read a few data values and then return to reading the underlying stream.
You can't in general tell how many bytes have been read and buffered from
the underlying stream, and there's no call to push all of it back.  In
general, this problem is intractable.
Roedy Green - 24 Oct 2007 13:11 GMT
>Now if I call bos.close(), it will also close its underlying stream.
>Why should the underlying stream be closed?

because after you have composed your stack of i/o processors, it
behaves like a single unit.  OO thinking is that you are not supposed
to be aware of how code is constructed, only the final facade.

The practical reason is 99.9% of the time you want all the components
closed.
Signature

Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.