JDK-6431340 : NullPointerException on Dialog hide on dual head system (non-xinerama)
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2006-05-29
  • Updated: 2011-02-16
  • Resolved: 2006-06-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b91Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b84)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0-beta2-b84, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Red Hat Linux Fedora Core 4
custom 2.6.16 kernel x86_64 GNU/Linux

EXTRA RELEVANT SYSTEM CONFIGURATION :
Dual head X-Windows system (non-Xinerama)

A DESCRIPTION OF THE PROBLEM :
Hiding a modal dialog (e.g. JDialog.dispose()) that is the child of a non-modal dialog, which in turn is the child of a JFrame causes a NullPointerException if the windows are on a secondary screen of a multi-head x-windows configuration (non-Xinerama).

Tested with JOptionPane.showMessageDialog and JFileChooser. Based on trace info, this probably happens on any Dialog.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached test program. Press the "Open Dialog" button. Press OK on the message dialog.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Dialog closes.
ACTUAL -
Primary screen- dialog closes.
Secondary screen: NullPointerException thrown, dialog stays up.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception occurred during event dispatching:
java.lang.NullPointerException
        at sun.awt.X11.XWindowPeer.restoreTransientFor(XWindowPeer.java:1199)
        at sun.awt.X11.XWindowPeer.removeFromTransientFors(XWindowPeer.java:1265)
        at sun.awt.X11.XWindowPeer.setModalBlocked(XWindowPeer.java:1020)
        at java.awt.Window.setModalBlocked(Window.java:1159)
        at java.awt.Dialog.unblockWindow(Dialog.java:1538)
        at java.awt.Dialog.modalHide(Dialog.java:1442)
        at java.awt.Dialog.hideAndDisposePreHandler(Dialog.java:1171)
        at java.awt.Dialog.hide(Dialog.java:1213)
        at java.awt.Component.show(Component.java:1418)
        at java.awt.Component.setVisible(Component.java:1369)
        at java.awt.Window.setVisible(Window.java:694)
        at java.awt.Dialog.setVisible(Dialog.java:979)
        at javax.swing.JOptionPane$3.propertyChange(JOptionPane.java:997)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
        at java.awt.Component.firePropertyChange(Component.java:7802)
        at javax.swing.JOptionPane.setValue(JOptionPane.java:1940)
        at javax.swing.plaf.basic.BasicOptionPaneUI$ButtonActionListener.actionPerformed(BasicOptionPaneUI.java:1184)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:377)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:232)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
        at java.awt.Component.processMouseEvent(Component.java:5999)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3203)
        at java.awt.Component.processEvent(Component.java:5764)
        at java.awt.Container.processEvent(Container.java:1984)
        at java.awt.Component.dispatchEventImpl(Component.java:4407)
        at java.awt.Container.dispatchEventImpl(Container.java:2042)
        at java.awt.Component.dispatchEvent(Component.java:4237)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4248)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3912)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3842)
        at java.awt.Container.dispatchEventImpl(Container.java:2028)
        at java.awt.Window.dispatchEventImpl(Window.java:2300)
        at java.awt.Component.dispatchEvent(Component.java:4237)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:600)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:177)
        at java.awt.Dialog$1.run(Dialog.java:1039)
        at java.awt.Dialog$2.run(Dialog.java:1085)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.awt.Dialog.show(Dialog.java:1083)
        at javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:848)
        at javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:645)
        at javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:616)
        at TestCase$ButtonActionListener.actionPerformed(TestCase.java:35)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:377)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:232)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
        at java.awt.Component.processMouseEvent(Component.java:5999)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3203)
        at java.awt.Component.processEvent(Component.java:5764)
        at java.awt.Container.processEvent(Container.java:1984)
        at java.awt.Component.dispatchEventImpl(Component.java:4407)
        at java.awt.Container.dispatchEventImpl(Container.java:2042)
        at java.awt.Component.dispatchEvent(Component.java:4237)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4248)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3912)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3842)
        at java.awt.Container.dispatchEventImpl(Container.java:2028)
        at java.awt.Window.dispatchEventImpl(Window.java:2300)
        at java.awt.Component.dispatchEvent(Component.java:4237)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:600)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class TestCase {
        public TestCase() {
                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
                GraphicsDevice[] gs = ge.getScreenDevices();

                if (gs.length<2) {
                        System.out.println("Not multi-head environment, test not valid!");
                        System.exit(1);
                }

                for (int i=0; i<gs.length; i++) {
                        JFrame frame=new JFrame("Main App",gs[i].getDefaultConfiguration());
                        frame.show();
                        JDialog dialog=new JDialog(frame,"Dialog "+i,false,gs[i].getDefaultConfiguration());
                        JButton button=new JButton("Open Dialog");
                        button.addActionListener(new ButtonActionListener(dialog));
                        dialog.getContentPane().add(button);
                        dialog.pack();
                        dialog.show();
                }
        }
        private static class ButtonActionListener implements ActionListener {
                JDialog dialog;
                public ButtonActionListener(JDialog dialog) {
                        super();
                        this.dialog=dialog;
                }
                public void actionPerformed(ActionEvent e) {
                        JOptionPane.showMessageDialog(dialog,"Null Pointer Exception Ahead!","Press OK for NPE",JOptionPane.INFORMATION_MESSAGE);
                }
        }
        public static void main(String args[]) {
                new TestCase();
        }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use Java version <1.6

Comments
EVALUATION On linux/solaris with XToolkit new AWT modality uses WM_TRANSIENT_FOR hint. When a modal dialog is shown all its blocked windows are set up into a single transient_for chain, each window is transient for previous one. In the case of multiscreen configurations, both Xinerama-enabled or Xinerama-disabled, there may be a situation when this chain is partly on one screen and partly on another. This may confuse a window manager and lead to exceptions. Here is an example (very close to the test from the bug description). Say we have two frames, A and B, A on screen #0 and B on screen #1. Then we show two modeless dialogs D1 with parent A and D2 with parent B. D1 is located on screen #0 and D2 is located on screen #1. Then we show modal dialog M with parent D1 on screen #0, and all these 5 windows (including M) must be arranged into a chain. This chain depends on the current z-order of these windows, for example it can be like this: A-D1-B-D2-M. If all the windows are on the same screen, everything would be fine, however in this case it would lead to M not to be a child of D1 (D1 can be above M which is wrong). The fix of this problem may be the same as for 6401700/6412803: we should keep a single logical transient_for chain and several real chains for different X screens. When running with Xinerama on all the screens are the same so no changes are required.
29-05-2006

SUGGESTED FIX *** /tmp/geta7531.yv7534 2006-05-29 12:21:01.913252426 +0400 --- XWindowPeer.java 2006-05-29 12:02:32.000000000 +0400 *************** *** 1013,1019 **** private static Vector<XWindowPeer> collectJavaToplevels() { Vector<XWindowPeer> javaToplevels = new Vector<XWindowPeer>(); Vector<Long> v = new Vector<Long>(); ! v.add(XToolkit.getDefaultRootWindow()); while (v.size() > 0) { long win = v.remove(0); XQueryTree qt = new XQueryTree(win); --- 1013,1030 ---- private static Vector<XWindowPeer> collectJavaToplevels() { Vector<XWindowPeer> javaToplevels = new Vector<XWindowPeer>(); Vector<Long> v = new Vector<Long>(); ! X11GraphicsEnvironment ge = ! (X11GraphicsEnvironment)GraphicsEnvironment.getLocalGraphicsEnvironment(); ! GraphicsDevice[] gds = ge.getScreenDevices(); ! if (!ge.runningXinerama() && (gds.length > 1)) { ! for (GraphicsDevice gd : gds) { ! int screen = ((X11GraphicsDevice)gd).getScreen(); ! long rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen); ! v.add(rootWindow); ! } ! } else { ! v.add(XToolkit.getDefaultRootWindow()); ! } while (v.size() > 0) { long win = v.remove(0); XQueryTree qt = new XQueryTree(win); *************** *** 1146,1159 **** void updateTransientFor() { int state = getWMState(); XWindowPeer p = prevTransientFor; ! while ((p != null) && (p.getWMState() != state)) { p = p.prevTransientFor; } if (p != null) { setToplevelTransientFor(this, p, false, false); } XWindowPeer n = nextTransientFor; ! while ((n != null) && (n.getWMState() != state)) { n = n.nextTransientFor; } if (n != null) { --- 1157,1170 ---- void updateTransientFor() { int state = getWMState(); XWindowPeer p = prevTransientFor; ! while ((p != null) && ((p.getWMState() != state) || (p.getScreenNumber() != getScreenNumber()))) { p = p.prevTransientFor; } if (p != null) { setToplevelTransientFor(this, p, false, false); } XWindowPeer n = nextTransientFor; ! while ((n != null) && ((n.getWMState() != state) || (n.getScreenNumber() != getScreenNumber()))) { n = n.nextTransientFor; } if (n != null) {
29-05-2006

EVALUATION This may be related to another recent bug 6430802.
29-05-2006