Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which resources should be closed?

I'm trying to download a file from an URL. There are many resources and I dont know which one I need to close or do I simply have to close all of them?

    public void downloadUpdate(final String url) {
    try {

        /* Which of these resources do I need to close? */
        final InputStream inputStream = new URL(url).openStream();
        final ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
        final FileOutputStream fileOutputStream = new FileOutputStream(Bukkit.getServer().getUpdateFolderFile());
        final FileChannel fileChannel = fileOutputStream.getChannel();

        /* Downloading the update... */
        fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);

    } catch (final IOException exception) {
        exception.printStackTrace();
    }
}
like image 489
dennisp Avatar asked Oct 15 '22 14:10

dennisp


1 Answers

In your case, likely the only resources that need to be closed are the InputStream and FileOutputStream. However, why not simply close them all by using try-with-resources? It doesn't hurt to call Closeable#close() just in case1. In fact, you probably should close every Closeable in your control (i.e. that you opened) when finished with them (you don't necessarily know if a wrapper also needs to release resources).

try (InputStream inputStream = new URL(url).openStream();
     ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
     FileOutputStream fileOutputStream = new FileOutputStream(Bukkit.getServer().getUpdateFolderFile());
     FileChannel fileChannel = fileOutputStream.getChannel()) {

    fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);

} catch (IOException ex) {
    ex.printStackTrace();
}

The above is not much different than what you currently have.


1. Calling Closeable#close() method has no effect if invoked previously. This isn't necessarily true of AutoCloseable implementations (that don't implement Closeable).


Also, if you're using Java 9+ you don't need to deal with NIO channels. The InputStream class had a method added in Java 9: transferTo(OutputStream).

try (InputStream is = new URL(url).openStream();
     FileOutputStream fos = new FileOutputStream(...)) {
    is.transferTo(fos);
} catch (IOException ex) {
    ex.printStackTrace();
}

You might also be able to use Files.copy(InputStream,Path,CopyOption...) (Java 7+).

try (InputStream is = new URL(url).openStream()) {
    Path file = Bukkit.getServer().getUpdateFolderFile().toPath();
    Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
    ex.printStackTrace();
}

Note this might not result in the same behavior as when using FileOutputStream. I'm not entirely sure if FileOutputStream#<init>(File) truncates any existing bytes or if it simply overwrites the bytes from the beginning.

If you prefer using NIO channels, you can open a FileChannel directly via FileChannel.open(Path,OpenOption...) rather than going through a FileOutputStream. As I showed in an example above, you can convert a File into a Path using File#toPath().

like image 160
Slaw Avatar answered Oct 19 '22 00:10

Slaw