Search code examples
javaresourcesinputstream

Java: Close after returning BufferedInputStream


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?


Solution

  • 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.