In the first part of this series, I’ve
introduced you to the Source
, Sink
and Transformation
interfaces.
This time, I’ll look into a caveat and provide some more useful implementations.
Now what is the caveat?
Let’s look at the idiom for using a Source
or Sink
(assuming Java 6):
InputStream in = source.input();
try {
... // use in here
} finally {
in.close();
}
The use of the finally
-block ensures that the input stream obtained from the source gets always closed, even if an
exception is thrown by the try
-block.
This is critical:
If you don’t do this, then you may gradually lock up system resources which are associated with the input stream.
A long running server app may even kill your system because it’s gradually allocating all file handles!
So far, so good?
Not really!
What happens if there is an exception thrown by the try
-block and the finally
-block?
Well, the exception thrown by the finally
-block suppresses the exception thrown by the try
-block.
However, the former is only an aftermath of the latter and so you would really like the exception from the try
-block
to take precedence instead.
This issue is solved with the try-with-resources statement in Java 7. Using it, the previous idiom gets even more simple:
try (InputStream in = source.input()) {
... // use in here
}
Not only this is shorter, but in case of a double exception, the exception from the closing of the input stream gets
added to the suppressed list of the exception from the try
-block.
For more information, please have a look at the Java Language Specification, section
14.20.3 “try-with-resources”.
Oll Korrect (OK) now?
Not yet!
Sometimes, a Source
or Sink
implementation doesn’t want the client to close the stream, for example when writing to
the standard output or error streams.
‘No problem, I can implement a FilterOutputStream and override it’s close() method!’ you may think. Right, let’s have a look at the following approach:
public class Sinks {
public static Sink output() { return uncloseable(System.out); }
public static Sink error() { return uncloseable(System.err); }
public static Sink uncloseable(final OutputStream out) {
return new Sink() {
@Override public OutputStream output() {
return new FilterOutputStream(out) {
@Override public void close() throws IOException {
out.flush();
}
};
}
};
}
private Sinks() { }
}
The method (or better, function) uncloseable
decorates the given output stream with a filter output stream which,
instead of closing the given output stream, just flushes it.
This is critical because otherwise, it may “swallow” some bytes when writing to the standard output or error streams.
Finally, the methods/functions output
and error
do just that, i.e. they return a sink for writing to the standard
output and standard error without ever closing them.
I’ll leave the mirror implementation for reading from standard input up to you as an exercise.
Next time, I’ll look into another useful abstraction, the Store
interface!
Enjoy!