Closing resource coming as return from method

1.2k views Asked by At

I have a helper class with a static method that returns a stream:

public static InputStream getDocument(File file) throws IOException {
    ZipFile zipFile = new ZipFile(file);
    return zipFile.getInputStream(zipFile.getEntry("entry"));
}

Another class accesses that method and uses that returned stream:

InputStream is = MyClass.getDocument(new File(str));

My code works.


However, according to the Java Documentation, I should close my resource:

A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement.

But, when I implement try-with-resources:

public static InputStream getDocument(File file) throws IOException {
    try (ZipFile zipFile = new ZipFile(file);) {
        return zipFile.getInputStream(zipFile.getEntry("entry"));
    }
}

or try-finally:

public static InputStream getDocument(File file) throws IOException {
    InputStream is = null;
    try {
        ZipFile zipFile = new ZipFile(docx);
        is = zipFile.getInputStream(zipFile.getEntry("entry"));
        return is;
    } finally {
        is.close();
    }
}

I get an exception:

java.io.IOException: Stream closed

How to make sure, that resource will be closed, after its use?

2

There are 2 answers

3
fantaghirocco On BEST ANSWER

Usually the caller is responsible to close/release the resources.
You can use the try-with-resource or the try-finally construct outside the method as follows:

try (InputStream is = getDocument(aFile) {
    //… do your stuff
}

If I can give you an advice, write it in the method documentation:

/**
  * The caller is required to close the returned {@link InputStream}
  */
public static InputStream getDocument(File file) throws IOException
4
tkruse On

try-with-resources does implicitly the same as your second approach with finally. There is no way to close the zipFile but return an inputStream based on this zipFile.

Generally it would be better to avoid this helper method and use a single try-with-resourrces with multiple resources:

try (
   ZipFile zipFile = new ZipFile(file);
   inputStream inputStream = zipFile.getInputStream(zipFile.getEntry("entry"));
) {
    // Do stuff
} // Both zipFile and inputStream closed here implicitly

If you need to create such a method, you can however make sure that when the stream is closed, the zipFile object also gets closed.

From a related question Does a ZipEntry persist after a ZipFile is closed?

zipFile = new ZipFile(file);
InputStream zipInputStream = ...
return new InputStream() {
    @Override
    public int read() throws IOException {
        return zipInputStream.read();
    }
    @Override
    public void close() throws IOException {
        // Better add try/catch around each close()
        zipInputStream.close();
        zipFile.close();
    }
}

That way, when the calling method closes the stream it receives, the zip file would also be closed.