Java Forum / General / October 2007
Why should close() close the underlying stream?
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 MagazinesGet 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 ...
|
|
|