- Closing Java Streams with AutoCloseable
- How Java 8 API met AutoCloseable
- JDK Streams requiring closing
- Stream closing 101
- Crash testing close handlers
- Streams vs flatMap
- Summary
- Tags
- Mike Kowalski
- How to Exit JFrame on Close in Java Swing
- Exit JFrame on Close in Java Swing
- Creating a Java JFrame
- Two Approaches on Closing JFrame
Closing Java Streams with AutoCloseable
What could be kind of surprise even for some experienced Java programmers, the java.util.Stream interface extends java.lang.AutoCloseable . This (in theory) puts it on par with files, DB connections, and other resources requiring manual closing. Should we close all our Streams in the same way as any other resources?
In this post, I’d like to explain which Streams have to be closed and how the closing actually works. We will also go through some of the interesting under-documented properties of this feature.
How Java 8 API met AutoCloseable
AutoCloseable interface has been introduced in Java 7 as a part of try-with-resources construct, designed for easy resource cleanup right after they are not used anymore:
import java.sql.Connection; // Java < 7 Connection connection = . ; try < // do something with the connection >finally < connection.close(); // explicitely close the connection! >// Java 7+ try (Connection connection = . ) < // do something with the connection >// no explicit close needed!
The goal was to simplify the cleanup process to avoid stuff like DB/network connections or files being left opened unnecessarily. Unfortunately, this change (probably due to backward compatibility reasons) did not introduce any compile-time checks, so such resource leaks are still possible. That’s why the docs for AutoCloseable state clearly:
For code that must operate in complete generality, or when it is known that the AutoCloseable instance requires resource release, it is recommended to use try -with-resources constructions.
Streams (introduced with Java 8) seem to be a great example of that «freedom of choice», as the docs are mentioning them directly later:
However, when using facilities such as java.util.stream.Stream that support both I/O-based and non-I/O-based forms, try -with-resources blocks are in general unnecessary when using non-I/O-based forms.
The moral is straightforward — when a Stream is dealing with I/O operations (files, connections, etc.) it should be closed — like this:
try (Stream linesStream = Files.lines(Paths.get("file.txt"))) < linesStream.forEach(System.out::println); >catch (IOException e) < // TODO: handle IOException. >
It’s quite disappointing, that Streams API is not smart enough to perform the cleanup (basically by calling AutoCloseable::close() method) on its own, as a part of the terminal operation (like .collect() or sum() ). This is rather counterintuitive especially because an attempt of reusing already «consumed» Stream ends up with the IllegalStateException :
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
As long as this is how it works, using try-with-resources for closing Streams is probably the best solution we have. Unfortunately, it’s also an additional thing we have to remember about.
JDK Streams requiring closing
As far as I know, there is no official list of JDK API methods requiring to close the Streams they produce. However, a simple search for the Stream’s onClose(Runnable closeHandler) method usages (a way of defining close handler, described in the next section) revealed the members of only two classes: java.nio.file.Files and java.util.Scanner . The search has been performed against Java 15.
Methods of the java.nio.file.Files that rely on manual Stream closing:
- Files.list(Path dir) [docs]
- Files.walk(Path start, int maxDepth, FileVisitOption. options) [docs]
- Files.find(Path start, int maxDepth, BiPredicate matcher, FileVisitOption. options) [docs]
- Files.lines(Path path) [docs]
- Files.lines(Path path, Charset cs) [docs]
Methods of the java.util.Scanner that rely on manual Stream closing:
At the time of writing, IntelliJ Idea does not provide any out-of-the-box inspection that could show the Streams, that we forgot to close. Luckily, Sonarlint plugin (and the SonarQube itself) both report such cases thanks to the S2095 rule.
Update 2021-06-23: As has been pointed out in a comment, IntelliJ Idea (at least version 2021.1) does provide a dedicated inspection. However, it’s disabled by default and seems to work only in some IO-related cases (not in case of onClose presence in general). To enable the inspection, navigate to Settings > Editor > Inspections and type AutoCloseable used without ‘try’-with-resources into the search box.
Stream closing 101
Enabling controlled closing of a Stream is as simple as defining onClose handler by passing appropriate java.lang.Runnable instance. However, please keep in mind that without the usage of try-with-resources (or equivalent) our close handler would not be executed.
Stream closeable = List.of("a", "b", "c", "d", "e").stream() .onClose(() -> System.out.println("closed!")); try (closeable)
A result is quite predictable:
In fact, we can easily define multiple close handlers:
var multiCloseable = IntStream.of(1, 2, 3) .onClose(() -> System.out.println("first!")) .onClose(() -> System.out.println("second!")) .onClose(() -> System.out.println("third!")); try (multiCloseable)
In such a case, the result is a bit more gripping:
It looks like when more than one onClose handlers are defined, they are being executed in order of their appearance (FIFO).
Crash testing close handlers
Allowing multiple close handlers to be defined leads to the interesting question: what happens when one of the closing procedures fails? Let’s take our previous example into another ride, and modify the second close handler in a way, that will throw an exception.
var multiCloseable = IntStream.of(1, 2, 3) .onClose(() -> System.out.println("first!")) .onClose(() -> < throw new IllegalStateException("second has failed"); >) .onClose(() -> System.out.println("third!")); try (multiCloseable)
When one of the Stream’s close handlers throws an exception, the rest of them is still being executed in a standard order — exactly as we can see in the output:
6 first! third! Exception in thread "main" java.lang.IllegalStateException: second has failed
We can go even one step further, and let all 3 close handlers fail in the same way. We already know, that all defined handlers will be executed, but what (which?) exception will be reported?
var multiCloseable = IntStream.of(1, 2, 3) .onClose(() -> < throw new IllegalStateException("first has failed"); >) .onClose(() -> < throw new IllegalStateException("second has failed"); >) .onClose(() -> < throw new IllegalStateException("third has failed"); >); try (multiCloseable)
In case when more than one close handler fails, the returned exception is the first one being thrown. However, all other (next) exceptions are not lost, but reported as suppressed ones:
6 Exception in thread "main" java.lang.IllegalStateException: first has failed at com.mikemybytes.Playground.lambda$main$0(Playground.java:48) at java.base/java.util.stream.Streams$1.run(Streams.java:842) at java.base/java.util.stream.Streams$1.run(Streams.java:842) at java.base/java.util.stream.AbstractPipeline.close(AbstractPipeline.java:323) at com.mikemybytes.Playground.main(Playground.java:58) Suppressed: java.lang.IllegalStateException: second has failed at com.mikemybytes.Playground.lambda$main$1(Playground.java:51) at java.base/java.util.stream.Streams$1.run(Streams.java:846) . 3 more Suppressed: java.lang.IllegalStateException: third has failed at com.mikemybytes.Playground.lambda$main$2(Playground.java:54) at java.base/java.util.stream.Streams$1.run(Streams.java:846) . 2 more
That means, that with a bit of stack trace traversing we can obtain a complete set of all such exceptions being thrown in the meantime.
Streams vs flatMap
Update 2021-05-03: As František Řezáč and Manos Tzagkarakis have pointed me out on Twitter, flatMap behaves a bit differently in terms of stream closing. This paragraph has been added thanks to their suggestion.
Surprisingly, closeable Streams are handled differently when being passed to flatMap . As we can find in the docs, the flatMap method:
Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.)
Let’s illustrate that with an example:
var input1 = Stream.of("a", "b").onClose(() -> < System.out.println("closed 1"); >); var input2 = Stream.of("c", "d").onClose(() -> < System.out.println("closed 2"); >); var combined = Stream.of(input1, input2).onClose(() -> < System.out.println("closed combined"); >); var elements = combined.flatMap(s -> s).count(); // no closing here! System.out.println("elements: " + elements);
Every Stream passed to the flatMap method will be closed automatically without a need of using try-with-resources (or calling close() directly) on the Stream that contains them — that’s why the output contains traces of closing both the input1 and input2 Streams:
closed 1 closed 2 elements: 4
Summary
Finally, let’s recap all of the discussed Stream closing properties:
- In general, all I/O-related Streams should be put inside try-with-resources construct to ensure automated resource cleanup. Pay close attention to the methods of java.nio.file.Files and java.util.Scanner .
- When unsure about if the particular Stream should be closed or not, look for onClose(Runnable closeHandler) calls within the Stream returning method.
- Single Stream could have multiple close handlers defined — they will be executed in the FIFO order.
- All close handlers defined for a Stream will be executed even in case of throwing an exception in any of them.
- In case when multiple exceptions have been thrown from close handlers, the returned stack trace will contain all of them. The first exception thrown will become the main one.
- Streams passed to flatMap will be closed regardless of closing the Stream that contains them.
Tags
Mike Kowalski
Software engineer & consultant believing in craftsmanship and the power of fresh espresso. Writing in & about Java, distributed systems, and beyond. Mikes his own opinions and bytes.
How to Exit JFrame on Close in Java Swing
In this tutorial, we will focus on learning how to exit JFrame on close in Java Swing. I will be discussing various methods in this tutorial in detail.
Exit JFrame on Close in Java Swing
We need to use the Java Swing library to create a JFrame, thus we must first import the library into our code. The Java Swing Library is used to create window-based applications with various powerful components such as JFrame which we will be using to create a frame.
Do read this article, which clearly explains the Java Swing Library, its components such as JLabel, the creation of a JFrame, inbuilt- methods, its parameters, etc.
Creating a Java JFrame
JFrame is a class of the javax.swing package which is a container that provides a window on the screen. We create a JFrame with the title “Exit on Close Example” and use the setVisible(true) function to display the frame. We specify the location of the frame using the setBounds() function.
Let’s see the code:
JFrame frame = new JFrame("Exit on Close Example"); // Creation of a new JFrame with the title frame.setVisible(true); // To display the frame frame.setBounds(100, 200, 350, 200); // To set the bounds of the frame.
We have created a plain empty JFrame with our title successfully. Now, we have to exit JFrame on close. One simple way of doing this is clicking the cross button on the top right corner of the frame which closes the frame but the application or code will still be running in the background.
The efficient way of closing the JFrame is that we use the inbuilt method provided by the JFrame class i.e., using the JFrame.setDefaultCloseOperation(int) method. We use this method to change the behavior of the default JFrame which hides it instead of disposing of it when the user closes the window.
Two Approaches on Closing JFrame
Method 1: Using the WindowConstants
The simplest and most used technique is to use WindowConstants.EXIT_ON_CLOSE as a parameter. We must extend the JFrame class to our main class to use this. By using this, it closes the JFrame window completely and also frees the memory.
Method 2: Using the JFrame Constants
We have various constants defined by javax.swing.JFrame to close the JFrame as per our requirements. They are :
- JFrame.EXIT_ON_CLOSE — A System.exit(0) will be executed which will exit the entire code and the frame.
- JFrame.DISPOSE_ON_CLOSE — It will dispose of the frame but keep the code running.
- JFrame.DO_NOTHING_ON_CLOSE — The frame will not be closed. It basically does nothing.
- JFrame.HIDE_ON_CLOSE — This is the default one. It will hide the frame but keep the code running.
I have implemented both methods in the code for greater understanding.
Let’s see the code:
import javax.swing.*; public class ExitOnClose extends JFrame < public static void main(String[] args) < JFrame frame = new JFrame("Exit on Close Example"); // Creation of a new JFrame with the title frame.setLayout(null); // To position our components frame.setVisible(true); // To display the frame frame.setBounds(100, 200, 350, 200); // To set the locations of the frame. //------------------------------------------------ //To Terminate the JFrame and the code on close //------------------------------------------------ //METHOD-1 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //METHOD-2 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); >>
Thanks for reading! I hope you found this post useful!