bugbear said:
Even with Java's garbage collection, there is often
a new to close (or otherwise release) expensive
resource.
Classic instances include window and file handles.
This is easily handled using Java's try-catch-finally
model.
HandleThing h = new HandleThing(1,2,3);
try {
.
use h
.
.
} finally {
h.close();
}
These blocks can be nested to any depth,
and work well.
But the FilterInputStream class (and
any class that extends it) makes this model
unusable.
The problem is caused by 2 things.
1) a FilterInputStream closes its sub-stream
when it is itself closed.
2) Many InputStream don't like (i.e. throw exceptions)
if they are closed more than once.
So...
If a FilterInputStream is closed in an inner block,
it will ALSO close the InputStream that was opened
in an outer block.
So when the outer block's finally block does
its h.close(), it errors.
YUCK!
Am I missing trick here, or is it all as
bad as I think?
I would welcome any suggestions for elegant
implementation of correct closing under both
normal and exception cases.
The solution seems to be "Once you've wrapped
something, don't unwrap it." That is, once you've
set up the IS and wrapped it in the FIS, you should
only manipulate the FIS thereafter.
This can become a little bit ugly at the point
where you're setting up the original wrappings, because
you've got to recover properly if (for example) you get
an exception from the IS before you've wrapped it in
the FIS. Here's how I did a similar thing in one of
my early efforts; it still looks like a reasonable
approach to me, but if someone has a better I'd not
be sorry to learn of it:
File file = ...;
InputStream stream = null;
BufferedReader reader = null;
try {
stream = new FileInputStream(file);
if (file.getName().endsWith(".gz"))
stream = new GZIPInputStream(stream);
reader = new BufferedReader(
new InputStreamReader(stream));
doThingsWithButDoNotClose(reader);
}
catch (...) { ... }
finally {
try {
if (reader != null)
reader.close();
else if (stream != null)
stream.close();
}
catch (IOException ex) { ... }
}
Here, the "nullity" of `reader' and/or `stream' tells me
which (if either) of them to close in the finally block.
The important point is that if I succeed in wrapping one
object in another, I never again touch the "wrapee."