I am planning a function that creates and returns an InputStream
that in turn reads from another InputStream
because the initialization of that InputStream
is not trivial and I would like to use it in multiple places. Consider this simple example:
private static InputStream openStream() throws IOException {
Path path = Paths.get("/etc/passwd");
InputStream inputStream = Files.newInputStream(path);
return new BufferedInputStream(inputStream);
}
I will use this function as follows:
public static void main(String[] args) {
try (InputStream stream = openStream()) {
byte[] buffer = new byte[1024];
int numBytes;
while ((numBytes = stream.read(buffer, 0, buffer.length)) > 0) {
System.out.printf("Just read %d bytes from stream!%n", numBytes);
}
} catch (IOException e) {
e.printStackTrace();
}
}
However, I am concerned that closing the BufferedInputStream
in this example will not close the InputStream
inside it. Will this lead to orphaned file handles and memory leaks if called multiple times? What is a better solution for this?
A simple solution I could think of is to define a closable container class and put both input streams into that class. When calling close()
, this class would simply close all its open handles.
class StreamContainer implements Closeable {
private final InputStream[] inputStreams;
public StreamContainer(InputStream... inputStreams) {
this.inputStreams = inputStreams;
}
@Override
public void close() throws IOException {
for (InputStream inputStream : this.inputStreams) {
inputStream.close();
}
}
}
But I suppose, there might be a better solution, built-in mechanic or development pattern. Or maybe these constructs should be avoided?
In this cases you should read the code source of the BufferedInputStream
, this is the close definition
public void close() throws IOException {
while(true) {
byte[] buffer;
if ((buffer = this.buf) != null) {
if (!U.compareAndSetObject(this, BUF_OFFSET, buffer, (Object)null)) {
continue;
}
InputStream input = this.in;
this.in = null;
if (input != null) {
input.close();
}
return;
}
return;
}
}
As you can see when closing the BufferedInputStream, the underlying input stream is closed as well.
And this is the documentation of close:
public void close() throws IOException Closes this input stream and releases any system resources associated with the stream. Once the stream has been closed, further read(), available(), reset(), or skip() invocations will throw an IOException. Closing a previously closed stream has no effect.