javaswingglasspanebusy-cursor

Swing goes into the wait state after locking unlocking through glasspane


I am experiencing a strange situation. On some condition (an inactivity timeout) I have to lock my swing window (and any subwindows) and after unlocking again through valid credentials, I need to unlock them all back.

I am using glasspane foe that and my two functions are as below

Main lock module

public void lock(boolean minimize) {
    if (!locked) {
        locked = true;
        lockMinimized = minimize;
        logger.debug(context + "Locking Target...");
        // Lock all frames using the AWT event dispatching thread.
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                Frame[] frames = Frame.getFrames();
                Window[] subwindows;
                for (Frame frame : frames) {
                    // Lock the frame itself
                    lockWindow(frame);

                    // Lock subwindows owned by the frame
                    subwindows = frame.getOwnedWindows();
                    for (Window subwindow : subwindows) {
                        if (subwindow instanceof RootPaneContainer) {
                            lockWindow(subwindow);
                        }
                    }
                }
                //do additional stuff - lock out of process windows
                if (lockUnlockInterface != null) {
                    logger.info("calling locking for out of jvm process ");
                    lockUnlockInterface.lock();
                }
            }
        });
        logger.debug(context + "Target locked.");
    }
}

Sub lock method

private void lockWindow(final Window window) {
    logger.debug(context + "Locking window: " + window.getClass().toString());
    Vector exemptWindowClassNames = getExemptList();
    if (window instanceof RootPaneContainer
            && ((RootPaneContainer) window).getRootPane() != null
            && !lockedWindows.containsKey(window)
            && !(exemptWindowClassNames.contains(window.getClass().toString()))) {
        logger.debug(context + "Locking window...");
        try {
            // Create an object to store original details for the locked window.
            LockedWindow lockedWindow = new LockedWindow();
            lockedWindows.put((RootPaneContainer) window, lockedWindow);

            // Remember the original glass pane and visibility before locking.
            lockedWindow.originalGlassPane = ((RootPaneContainer) window).getGlassPane();
            lockedWindow.wasVisible = ((RootPaneContainer) window).getContentPane().isVisible();

            // Add a LockedGlassPane to the window.
            LockedGlassPane lgp = new LockedGlassPane();
            lgp.setVisible(true); //hide the contents of the window
            ((RootPaneContainer) window).setGlassPane(lgp);
            ((RootPaneContainer) window).getContentPane().setVisible(false);
            lgp.setVisible(true); //redisplays the lock message after set as glassPane.
            ((RootPaneContainer) window).getContentPane().invalidate();

            // Minimize the window (if requested), while keeping a record of
            // which windows have been minimized so that they can be restored
            // later when the TimeoutTarget is unlocked.
            if (window instanceof Frame) {
                Frame frame = (Frame) window;
                // Remember the original minimized state of the window.
                lockedWindow.minimized = (frame.getExtendedState() & Frame.ICONIFIED) != 0;
                if (lockMinimized) {
                    frame.setExtendedState(Frame.ICONIFIED);
                }
            }

            //
            //Note required now, but keeping in case the requirement changes again.
            //
            // Prevent the window from being closed while this target is
            // locked.
            // lockedWindow.windowListeners = window.getWindowListeners();
            //  for (WindowListener wl : lockedWindow.windowListeners) {
            //     window.removeWindowListener(wl);
            // }
            //if (window instanceof JFrame) {
            // JFrame jframe = (JFrame) window;
            // lockedWindow.originalDefaultCloseOperation = jframe.getDefaultCloseOperation();
            // jframe.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
            //} else if (window instanceof JDialog) {
            //  JDialog jdialog = (JDialog) window;
            // lockedWindow.originalDefaultCloseOperation = jdialog.getDefaultCloseOperation();
            // jdialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
            //}
        } catch (Exception e) {
            logger.error(context + "Failed to lock window.", e);
        }
    }
    if (exemptWindowClassNames.contains(window.getClass().toString())) {
        window.toFront();
    }
}

unlock main method

public void unlock() { locked = false; lockMinimized = false;

    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            Window[] subwindows;
            for (RootPaneContainer window : lockedWindows.keySet()) {
                // Unlock the frame itself.
                unlockWindow(window);

                // Unlock subwindows owned by the frame.
                if (window instanceof Frame) {
                    subwindows = ((Frame) window).getOwnedWindows();
                    for (Window subwindow : subwindows) {
                        if (subwindow instanceof RootPaneContainer) {
                            unlockWindow((RootPaneContainer) subwindow);
                        }
                    }
                }
            }

            lockedWindows.clear();

          //do additional stuff - lock out of process windows
            if (lockUnlockInterface != null) {
                logger.info("calling unlocking for out of jvm process ");
                lockUnlockInterface.unlock();
            }
        }
    });
}

sub unlock method

private void unlockWindow(RootPaneContainer window) {
    try {
        LockedWindow lockedWindow = lockedWindows.get(window);
        logger.debug(context + "Unlocking window: " + window);
        if (lockedWindow != null) {
            logger.debug(context + "Unlocking...");
            // Restore the original glasspane for the window
            if (lockedWindow.originalGlassPane != null) {
                logger.debug(context + "Reset original glass pane.");
                window.setGlassPane(lockedWindow.originalGlassPane);
            }
            //make content pane visible again.
            (window).getContentPane().setVisible(lockedWindow.wasVisible);
            (window).getRootPane().invalidate();

            // Restore (un-minimize) the window if it wasn't minimized before
            // the lock.
            if (!lockedWindow.minimized && window instanceof Frame) {
                ((Frame) window).setExtendedState(((Frame) window).getExtendedState()
                        & ~Frame.ICONIFIED);
            }
            // Restore the original default close operation from before the
            // lock, which will normally allow the window to be closed.
            if (window instanceof Window) {
                if (lockedWindow.windowListeners != null) {
                    for (WindowListener wl : lockedWindow.windowListeners) {
                        ((Window) window).addWindowListener(wl);
                    }
                }
                if (window instanceof JFrame) {
                    ((JFrame) window)
                            .setDefaultCloseOperation(lockedWindow.originalDefaultCloseOperation);
                } else if (window instanceof JDialog) {
                    ((JDialog) window)
                            .setDefaultCloseOperation(lockedWindow.originalDefaultCloseOperation);
                }
            }
            logger.debug(context + "Window has been unlocked");
        }
    } catch (Exception e) {
        logger.error(context + "Failed to unlock window.", e);
    }

}

Just to repeat again My lock and unlock does happen successfully. Well unlock not successfully, cause after unlock there remains a busy cursor over my unlocked window. Its as good as gone. Useless.

I have seen from logs I am getting successfully out from unlock call. Then I don't know hat is causing that busy cursor to appear and block anything on my window.

I have got those logs too and they are pretty fine

I am not sure what is causing this then?

Possible culprits and things I have tried

  1. not doing invalidate in lock unlock
  2. setting glasspane to null explicitly
  3. not doing any listener stuff

All of these to no avail, situation remains bleak.

Anybody had experienced same, can give me pointers please?

One constraint I have is I cant leave the glasspane method, to maintain homogeneity between application I have to use that. So I have to get this only working, no alternatives.

UPDATE

@trashgod I have taken the thread dump Unfortunately unable to attach it. What I need to look into that? Last three lines are "VM Thread" prio=10 tid=0x28688000 nid=0x5e58 runnable

"VM Periodic Task Thread" prio=10 tid=0x28721c00 nid=0x2bc0 waiting on condition

JNI global references: 19887

Any help on this? What I should look at? "VM Periodic Task Thread" ?? some specific states which one?

How can I take help on the thread dump. I blv not through SO, crossing the char limit here.


Solution

  • I got the issue resolved.

    This answer helped me a lot. java swing clear the event queue Infact the key concept is same.

    So for the code part I modified the modules with the

    lockWindow

    private void lockWindow(final Window window) {
        if (window instanceof RootPaneContainer
                && ((RootPaneContainer) window).getRootPane() != null
                && !lockedWindows.containsKey(window)) {
            java.util.Timer timer = null;
            try {
    
                //don't do invalidate, invalidate as the first step
                //((RootPaneContainer) window).getContentPane().invalidate();
    
                // Create an object to store original details for the locked window.
                LockedWindow lockedWindow = new LockedWindow();
                lockedWindows.put((RootPaneContainer) window, lockedWindow);
    
                lockedWindow.originalGlassPane = ((RootPaneContainer) window).getGlassPane();
    
    
                //okk may be glasspane only in integrated scenario is causing the issue
                //comment it and check, we are still putting it in the map  above but its doing nothing
                /*
                // Remember the original glass pane and visibility before locking.
    
                //okk is this the only issue? What should be the originalGlassPane first time? null?
                lockedWindow.originalGlassPane = ((RootPaneContainer) window).getGlassPane();
                System.err.println("Original galss pane : " + ((RootPaneContainer) window).getGlassPane());
    
                lockedWindow.wasVisible = ((RootPaneContainer) window).getContentPane().isVisible();
    
                // Add a LockedGlassPane to the window.
                LockedGlassPane lgp = new LockedGlassPane();
                lgp.setVisible(true); //hide the contents of the window
                ((RootPaneContainer) window).setGlassPane(lgp);
    
                //don't do this stuff too
                ((RootPaneContainer) window).getContentPane().setVisible(false);
    
                lgp.setVisible(true); //redisplays the lock message after set as glassPane.
                */
                LockedGlassPane lgp = new LockedGlassPane();
                ((RootPaneContainer) window).setGlassPane(lgp);
                timer = switchToBusyCursor(window);
    
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    //do nothing
                    System.err.println("Am I interrupted?");
                }
                //okk the above thing worked, it doesnt lock naturlly, now try if setting visible code is an issue?
                //great this thing works so this also is not an issue, only galsspane in SiteManager is
                lockedWindow.wasVisible = ((RootPaneContainer) window).getContentPane().isVisible();
    
                ((RootPaneContainer) window).getContentPane().repaint();
    
                // Minimize the window (if requested), while keeping a record of
                // which windows have been minimized so that they can be restored
                // later when the TimeoutTarget is unlocked.
    
              //don't do this stuff too - as unlock is not working investigating that
                if (window instanceof Frame) {
                    Frame frame = (Frame) window;
                    // Remember the original minimized state of the window.
                    lockedWindow.minimized = (frame.getExtendedState() & Frame.ICONIFIED) != 0;
                    if (lockMinimized) {
                        frame.setExtendedState(Frame.ICONIFIED);
                    }
                }
    
                //
                //Note required now, but keeping in case the requirement changes again.
                //
                // Prevent the window from being closed while this target is
                // locked.
                // lockedWindow.windowListeners = window.getWindowListeners();
                //  for (WindowListener wl : lockedWindow.windowListeners) {
                //     window.removeWindowListener(wl);
                // }
                //if (window instanceof JFrame) {
                // JFrame jframe = (JFrame) window;
                // lockedWindow.originalDefaultCloseOperation = jframe.getDefaultCloseOperation();
                // jframe.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
                //} else if (window instanceof JDialog) {
                //  JDialog jdialog = (JDialog) window;
                // lockedWindow.originalDefaultCloseOperation = jdialog.getDefaultCloseOperation();
                // jdialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
                //}
            } catch (Exception e) {
                System.err.println(getThreadPrefix()  + " Failed to lock window." + e.getLocalizedMessage());
            } finally {
                switchToNormalCursorEventThread(window, timer);
            }
        }        
    }
    

    unlockWindow

    private void unlockWindow(RootPaneContainer window) {
        try {
            LockedWindow lockedWindow = lockedWindows.get(window);
            //System.err.println(getThreadPrefix()  + " Unlocking window::: " + lockeWindow.isDisplayable());
            if (lockedWindow != null && ((Frame) window).isDisplayable()) {
                System.err.println(getThreadPrefix() + "Unlocking..." + lockedWindow);
                // Restore the original glasspane for the window
    
                //okk may be glasspane only in integrated scenario is causing the issue
                //comment it and check, we are still putting it in the map  above but its doing nothing
    
    
                //okk is this the only issue? What should be the originalGlassPane first time? null?
                if (lockedWindow.originalGlassPane != null) {
                    System.err.println(getThreadPrefix() + "Reset original glass pane.");
                    window.setGlassPane(lockedWindow.originalGlassPane);
                    //lockedWindow.originalGlassPane.setVisible(true);
                }
    
    
                //make content pane visible again.
                //(window).getContentPane().setVisible(lockedWindow.wasVisible);
    
                //okk try this
                //(window).getContentPane().setVisible(true);
                //(window).getRootPane().invalidate();
    
                //okk the above thing worked, it doesnt lock naturlly, now try if setting visible code is an issue?
                //great this thing works so this also is not an issue
                (window).getContentPane().setVisible(lockedWindow.wasVisible);
    
                (window).getRootPane().repaint();
    
                // Restore (un-minimize) the window if it wasn't minimized before
                // the lock.
                //do this tuff anyways
                if (!lockedWindow.minimized && window instanceof Frame) {
                    ((Frame) window).setExtendedState(((Frame) window).getExtendedState()
                            & ~Frame.ICONIFIED);
                }
    
    
                // Restore the original default close operation from before the
                // lock, which will normally allow the window to be closed.
    
                //dont do listeneres??
                if (window instanceof Window) {
                    if (lockedWindow.windowListeners != null) {
                        for (WindowListener wl : lockedWindow.windowListeners) {
                            System.err.print("windowlistener is not null " + wl);
                            ((Window) window).addWindowListener(wl);
                        }
                    }
                    if (window instanceof JFrame) {
                        ((JFrame) window)
                                .setDefaultCloseOperation(lockedWindow.originalDefaultCloseOperation);
                    } else if (window instanceof JDialog) {
                        ((JDialog) window)
                                .setDefaultCloseOperation(lockedWindow.originalDefaultCloseOperation);
                    } 
                }
    
                //try this too
                //((RootPaneContainer)window).setGlassPane(null);
    
                //lockedWindows.remove(window);
              //stopEventTrap
                stopEventTrap((Frame)window);
                System.err.println(getThreadPrefix()  + " Window has been unlocked");
            }
        } catch (Exception e) {
            System.err.println(getThreadPrefix()  + " Failed to unlock window. " + e.getLocalizedMessage());
        }
    
    }
    

    Added these new methods taken from above answer with modifications as p[er my use case

    public static java.util.Timer switchToBusyCursor(final Window frame) {
        startEventTrap(frame);
        java.util.TimerTask timerTask = new java.util.TimerTask() {
    
            public void run() {
                startWaitCursor(frame);
            }
    
        };
        final java.util.Timer timer = new java.util.Timer();
        timer.schedule(timerTask, DELAY_MS);
        return timer;
    }
    
    public static void switchToNormalCursorEventThread(final Window window, final java.util.Timer timer) {
    
        Runnable r = new Runnable() {
    
            public void run() {
                switchToNormalCursor(window, timer);
            }
    
        };
    
        javax.swing.SwingUtilities.invokeLater(r);
    
    }
    
    public static void switchToNormalCursor(final Window window, final java.util.Timer timer) {
        timer.cancel();
        stopWaitCursor(window);
        //stopEventTrap(window);
    }
    
    private static void startWaitCursor(Window window) {
        ((RootPaneContainer) window).getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        ((RootPaneContainer) window).getGlassPane().addMouseListener(mouseAdapter);
        ((RootPaneContainer) window).getGlassPane().setVisible(true);
    }
    
    private static void stopWaitCursor(Window window) {
        ((RootPaneContainer) window).getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        //((RootPaneContainer) window).getGlassPane().removeMouseListener(mouseAdapter);
        //((RootPaneContainer) window).getGlassPane().setVisible(false);
    }
    
    private static void startEventTrap(Window window) {
        ((RootPaneContainer) window).getGlassPane().addMouseListener(mouseAdapter);
        ((RootPaneContainer) window).getGlassPane().setVisible(true);
    }
    
    private static void stopEventTrap(Window window) {
        java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
        ((RootPaneContainer) window).getGlassPane().removeMouseListener(mouseAdapter);
        ((RootPaneContainer) window).getGlassPane().setVisible(false);
    }
    
    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };
    

    I have also taken threaddumps and analysed them as @trashgod said. I found that was correct too, IMHO, nothing blocking / wrong there. Though yes, AWTEventQueue-0 was at the same code point always.