-
Bug
-
Resolution: Fixed
-
P3
-
8, 9, 10
-
b23
-
x86_64
-
windows_7
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8187046 | 9 | Alexander Zvegintsev | P3 | Closed | Won't Fix | |
JDK-8188632 | 8u172 | Alexander Zvegintsev | P3 | Resolved | Fixed | b01 |
JDK-8190634 | 8u171 | Alexander Zvegintsev | P3 | Resolved | Fixed | b01 |
JDK-8187101 | 8u162 | Alexander Zvegintsev | P3 | Resolved | Fixed | b01 |
JDK-8198128 | emb-8u171 | Unassigned | P3 | Resolved | Fixed | b01 |
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b11)
Java HotSpot(TM) Client VM (build 25.141-b11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
I realize the javax.embed.singleThread=true is "experimental", but it seems to be widely adopted as people start to transition over to JavaFX.
We are setting up some automated GUI testing and we noticed something odd happening. When we traced it back, it was because an accidental double-click was happening on a button. This button brought up a modal dialog.
The bug is that, when running with the JavaFX system initialized, there is an extra "run later" happening in the swing event loop. This allows the modal dialog opening to be delayed one cycle; thereby letting the second click to also trigger the action. In the attached example, this causes the dialog to be opened twice.
The impact of this bug is that it makes it difficult to transition over to JavaFX since using JavaFX in part of the application causes older parts to be vulnerable since states that were impossible before (e.g. two input dialogs) are now possible.
Occasionally, the following exception occurs:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.Thread cannot be cast to java.awt.EventDispat
at java.awt.SequencedEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
...
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the attached program and run it from the command line like this:
"c:\Program Files (x86)\Java\jre1.8.0_121\bin\java.exe" -Djavafx.embed.singleThread=true -jar dist\JavaFXDoubleClick.jar
Then run it from the command line like this:
"c:\Program Files (x86)\Java\jre1.8.0_121\bin\java.exe" -Djavafx.embed.singleThread=false -jar dist\JavaFXDoubleClick.jar
When the singleThread=true, this will open up two dialogs, but when it is false, the expected result of one dialog will open.
The sleep is included in the test program so that it is easier to reproduce.
In our application it is easily reproducible since we have enough logic surrounding window placement and such that it take takes that extra few milliseconds which allows the error to occur.
The stack trace is listed in https://bugs.openjdk.java.net/browse/JDK-8088132, but the exception is not the problem I want to emphasize. The major problem is this extra cycle.
The code path is in EventQueue.java.
protected void dispatchEvent(final AWTEvent event) has code to handle the fx dispatcher. This eventually gets to a call to Platform.runLater() in the SwingFXUtils.FXDispatcher
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Modality would work as expected. Only one dialog would ever show
ACTUAL -
It is possible to get two modal dialogs
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.Thread cannot be cast to java.awt.EventDispat
at java.awt.SequencedEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3$1.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._enterNestedEventLoopImpl(Native Method)
at com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(WinApplication.java:218)
at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:583)
at javafx.embed.swing.SwingFXUtils$FwSecondaryLoop.lambda$enter$63(SwingFXUtils.java:332)
at com.sun.javafx.application.PlatformImpl.runAndWait(PlatformImpl.java:317)
at com.sun.javafx.application.PlatformImpl.runAndWait(PlatformImpl.java:307)
at javafx.embed.swing.SwingFXUtils$FwSecondaryLoop.enter(SwingFXUtils.java:331)
at java.awt.Dialog.show(Unknown Source)
at java.awt.Component.show(Unknown Source)
at java.awt.Component.setVisible(Unknown Source)
at java.awt.Window.setVisible(Unknown Source)
at java.awt.Dialog.setVisible(Unknown Source)
at javafxdoubleclick.JavaFXDoubleClick.showDialog(JavaFXDoubleClick.java:70)
at javafxdoubleclick.JavaFXDoubleClick.lambda$new$1(JavaFXDoubleClick.java:48)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.focusLost(Unknown Source)
at java.awt.Component.processFocusEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.SentEvent.dispatch(Unknown Source)
at java.awt.DefaultKeyboardFocusManager$DefaultKeyboardFocusManagerSentEvent.dispatch(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.sendMessage(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.SequencedEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3$1.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._enterNestedEventLoopImpl(Native Method)
at com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(WinApplication.java:218)
at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:583)
at javafx.embed.swing.SwingFXUtils$FwSecondaryLoop.lambda$enter$63(SwingFXUtils.java:332)
at com.sun.javafx.application.PlatformImpl.runAndWait(PlatformImpl.java:317)
at com.sun.javafx.application.PlatformImpl.runAndWait(PlatformImpl.java:307)
at javafx.embed.swing.SwingFXUtils$FwSecondaryLoop.enter(SwingFXUtils.java:331)
at java.awt.Dialog.show(Unknown Source)
at java.awt.Component.show(Unknown Source)
at java.awt.Component.setVisible(Unknown Source)
at java.awt.Window.setVisible(Unknown Source)
at java.awt.Dialog.setVisible(Unknown Source)
at javafxdoubleclick.JavaFXDoubleClick.showDialog(JavaFXDoubleClick.java:70)
at javafxdoubleclick.JavaFXDoubleClick.lambda$new$1(JavaFXDoubleClick.java:48)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3$1.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Unknown Source)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javafxdoubleclick;
import java.awt.FlowLayout;
import java.lang.reflect.Method;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
*
* @author ocMAClaassen
*/
public final class JavaFXDoubleClick extends JFrame {
private boolean odd = true;
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new JavaFXDoubleClick());
}
private JavaFXDoubleClick() {
try {
Class<?> clazz;
clazz = Class.forName("com.sun.javafx.application.PlatformImpl");
Method m = clazz.getMethod("startup", Runnable.class);
Runnable r = new Runnable() {
@Override
public void run() {
}
};
m.invoke(null, new Object[]{r});
}
catch (Exception ex) {
ex.printStackTrace();
}
JPanel panel = new JPanel(new FlowLayout());
JButton button = new JButton("Double Click Me");
button.addActionListener((e) -> showDialog());
panel.add(button);
setContentPane(panel);
panel.setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(200, 100);
setVisible(true);
}
private void showDialog() {
System.err.println(System.getProperty("java.version") + " " + System.getProperty("javafx.embed.singleThread"));
JDialog dialog = new JDialog(this);
dialog.setModal(true);
JPanel panel = new JPanel();
panel.add(new JLabel("Modal Dialog"));
dialog.setContentPane(panel);
dialog.pack();
if (!odd)
dialog.setLocation(400, 300);
else
dialog.setLocation(200, 300);
odd = !odd;
sleep(100);
dialog.setVisible(true);
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
}
catch (InterruptedException ex) {
ex.printStackTrace();;
}
}
}
---------- END SOURCE ----------
SUBMITTED WORKAROUND :
We have a utility that we use to create our buttons, which gave us an opportunity to surround the actionPreformed call to be wrapped in a try / finally block that can keep a flag preventing the subsequent calls from actually doing anything. However, this doesn't feel like a very satisfying solution
- backported by
-
JDK-8187101 Extra runLater causes impossible states to be possible using javafx.embed.singleThread=true
-
- Resolved
-
-
JDK-8188632 Extra runLater causes impossible states to be possible using javafx.embed.singleThread=true
-
- Resolved
-
-
JDK-8190634 Extra runLater causes impossible states to be possible using javafx.embed.singleThread=true
-
- Resolved
-
-
JDK-8198128 Extra runLater causes impossible states to be possible using javafx.embed.singleThread=true
-
- Resolved
-
-
JDK-8187046 Extra runLater causes impossible states to be possible using javafx.embed.singleThread=true
-
- Closed
-
- duplicates
-
JDK-8098528 AWT Events are not coelasced under single threaded AWT/FX mode
-
- Closed
-