1+ /*
2+ * Copyright 2000-2025 JetBrains s.r.o.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation.
8+ *
9+ * This code is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+ * version 2 for more details (a copy is included in the LICENSE file that
13+ * accompanied this code).
14+ *
15+ * You should have received a copy of the GNU General Public License version
16+ * 2 along with this work; if not, write to the Free Software Foundation,
17+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+ *
19+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+ * or visit www.oracle.com if you need additional information or have any
21+ * questions.
22+ */
23+
24+ import javax .swing .*;
25+ import java .awt .*;
26+ import java .awt .event .AWTEventListener ;
27+ import java .awt .event .WindowEvent ;
28+ import java .util .*;
29+ import java .util .List ;
30+ import java .util .concurrent .*;
31+ import java .util .concurrent .atomic .AtomicReference ;
32+ import java .util .stream .Collectors ;
33+
34+ /* @test
35+ @summary Check if threads aren't spawned indefinitely after creating a window
36+ @key headful
37+ @run main BugJBR9563
38+ */
39+ public class BugJBR9563 {
40+ public static void main (String [] args ) throws Exception {
41+ CountDownLatch windowOpened = new CountDownLatch (1 );
42+ AtomicReference <JFrame > frameRef = new AtomicReference <>();
43+
44+ AWTEventListener listener = e -> {
45+ if (e .getID () == WindowEvent .WINDOW_OPENED ) {
46+ Window w = ((WindowEvent ) e ).getWindow ();
47+ if (w instanceof JFrame f && "Test" .equals (f .getTitle ())) {
48+ frameRef .set (f );
49+ windowOpened .countDown ();
50+ }
51+ }
52+ };
53+ Toolkit .getDefaultToolkit ().addAWTEventListener (listener , AWTEvent .WINDOW_EVENT_MASK );
54+
55+ try {
56+ SwingUtilities .invokeLater (() -> {
57+ JFrame f = new JFrame ("Test" );
58+ f .add (new JTextField (30 ));
59+ f .setDefaultCloseOperation (WindowConstants .EXIT_ON_CLOSE ); // unchanged
60+ f .pack ();
61+ f .setLocationRelativeTo (null );
62+ f .setVisible (true );
63+ });
64+
65+ Assert (windowOpened .await (5 , TimeUnit .SECONDS ), "Window did not open in time" );
66+
67+ EventQueue .invokeAndWait (() -> { /* no-op */ });
68+
69+ Robot robot = new Robot ();
70+ robot .setAutoWaitForIdle (true );
71+ robot .waitForIdle (); // waits for paints/repaints to finish
72+
73+ Toolkit .getDefaultToolkit ().sync ();
74+
75+ JFrame f = frameRef .get ();
76+ Assert (f != null , "Frame is not captured" );
77+
78+
79+ Set <String > threadsAfterRendering = getCurrentThreads ();
80+ System .out .println ("Threads after rendering: " + threadsAfterRendering );
81+
82+ Callable <Boolean > task = () -> {
83+ System .out .println ("Check threads state..." );
84+ Set <String > threads = getCurrentThreads ();
85+ List <String > newThreads = threads .stream ().filter (it -> !threadsAfterRendering .contains (it )).toList ();
86+
87+ System .out .println ("New threads list: " + newThreads );
88+ return newThreads .isEmpty ();
89+ };
90+
91+ List <Boolean > results = Collections .synchronizedList (new ArrayList <>());
92+
93+ for (int i = 0 ; i < 10 ; i ++) {
94+ System .out .println ("Check threads state..." );
95+ Set <String > threads = getCurrentThreads ();
96+ List <String > newThreads = threads .stream ().filter (it -> !threadsAfterRendering .contains (it )).toList ();
97+
98+ System .out .println ("New threads list: " + newThreads );
99+
100+ results .add (newThreads .isEmpty ());
101+ Thread .sleep (1000 );
102+ }
103+
104+ boolean testResult = results .stream ().reduce (true , (a , b ) -> a && b );
105+ if (testResult ) {
106+ System .out .println ("Test passed" );
107+ } else {
108+ System .out .println ("Test failed" );
109+ throw new RuntimeException ("Test failed" );
110+ }
111+ } finally {
112+ Toolkit .getDefaultToolkit ().removeAWTEventListener (listener );
113+ JFrame f = frameRef .get ();
114+ if (f != null ) {
115+ EventQueue .invokeAndWait (f ::dispose ); // Dispose on EDT
116+ }
117+ }
118+ }
119+
120+ private static Set <String > getCurrentThreads () {
121+ Map <Thread , StackTraceElement []> allThreads = Thread .getAllStackTraces ();
122+
123+ return allThreads
124+ .keySet ()
125+ .stream ()
126+ .map (t -> t .threadId () + " " + t .getName ())
127+ .collect (Collectors .toSet ());
128+ }
129+
130+ private static void Assert (boolean condition , String str ) {
131+ if (!condition ) {
132+ System .err .println (" ASSERT FAILED: " + str );
133+ throw new RuntimeException ("Assert Failed: " + str );
134+ }
135+ }
136+ }
0 commit comments