Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Sunday, September 21, 2025

Modal vs Always on Top

This article was originally posted on JRoller on Auguts 2, 2013

Here is an interesting piece of code:

      public static void main(String[] args) throws Exception {
          JFrame alwaysOnTopFrame = new JFrame(\"Always on top Frame\");
          alwaysOnTopFrame.setAlwaysOnTop(true);
          alwaysOnTopFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          alwaysOnTopFrame.setSize(500, 500);
          alwaysOnTopFrame.setVisible(true);
          alwaysOnTopFrame.setLocationRelativeTo(null);

          JOptionPane.showConfirmDialog(null, \"coucou\");
      }

It displays a simple frame, with the "Always on Top" activated, then opens a modal dialog. What will happen? Which one will prevail and be in front of the other?

As it turns out, if you try to run this code, you will see the Frame hiding the modal dialog box, although the application waits for you to answer the dialog first. What will appear to the average user is that there is a frozen window which does not react to anything. Impossible to move it or close it, until you guess that there is a dialog box behind it.

Of course, if you know it, you can discard the dialog box using Enter or Escape key, or the Alt+F4 combination, or access the dialog box menu through Alt+Space and move it around using the arrow keys. But the average user won't know it.

What is the solution? There is actually an option that you can set on the Frame, so that it will still respond to user actions even though there are modal dialogs on the screen. It is called Modal Exclusion Type. Try adding the following line:

  alwaysOnTopFrame.setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE);

When is a SwingWorker really done?

This article was originally posted on JRoller on April 25, 2013

Imagine you have a SwingWorker running, and you would like to know if it has terminated its job. You notice that it has a isDone() method, and say to yourself: "great! I can use it!". But are you sure you know what it is really doing? Are you sure that all your work will be over when it returns 'true'? You would say: "of course, it is called isDone". Well, let's have a closer look.

When you create a SwingWorker, it creates a Callable, and wraps it in a Future:

        Callable callable =
                new Callable() {
                    public T call() throws Exception {
                        setState(StateValue.STARTED);
                        return doInBackground();
                    }
                };

        future = new FutureTask(callable) {
                       @Override
                       protected void done() {
                           doneEDT();
                           setState(StateValue.DONE);
                       }
                   };

The Future will be executed by a ThreadPool (of maximum 10 threads). The doInBakground() is called, returning a value that will be used by the Future for its set() method. From this point onwards, calling the isDone() method will yield true. And this is even before the Future's done() method is called. So isDone() only means that doInBackground() terminated. Not what I was thinking.

OK, so why not use the getState() method. I see clearly that it is called by the Future right after the call to doneEDT(). But let's have a look at the doneEDT() method:

    private void doneEDT() {
        Runnable doDone =
            new Runnable() {
                public void run() {
                    done();
                }
            };
        if (SwingUtilities.isEventDispatchThread()) {
            doDone.run();
        } else {
            doSubmit.add(doDone);
        }
    }

As you can see, if we are already in the EDT, we execute immediately the method. But we know that we are started from a ThreadPool, so this is not the case. The doSubmit, surprisingly, uses a Timer and schedules the task in 30ms. Not a SwingUtilities.invokeLater() as I expected, but the result is the same: the getState() will return DONE before we even start the done() method. The PropertyChange are notifying you of the state change. So basically, you are left to yourself.

Monday, April 21, 2025

EDT Freeze Detector

 This article was originally posted on Jroller on April 2, 2013

It is always the same scenario: the support team contacts us with a problem of a "frozen GUI". They send us the logs so that we can investigate, but of course the logs do not show anything. The user already restarted its GUI, so when we ask the support to perform a jstack, it is already too late. Quite often, it is not even due to a deadlock, but to a long operation that should not be done in the EDT. Since the only thing we have to investigate is the logs, I decided to write an EDT Freeze Detector that would log any operation monopolizing the EDT for more than 10 seconds. Here is the code:

import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;

public class FreezeDetector extends EventQueue{
    private static final long FREEZE_TIMER_PERIOD = 10000L;

    private volatile AWTEvent currentEvent;
    private volatile Thread eventDispatchThread;

    private FreezeDetector() {
        Timer timer = new Timer("Freeze Detector", true);
        timer.schedule(new FreezeTimerTask(), FREEZE_TIMER_PERIOD, FREEZE_TIMER_PERIOD);
    }

    public static void installFreezeDetector() {
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(new FreezeDetector());
    }

    @Override
    protected void dispatchEvent(AWTEvent event) {
        eventDispatchThread = Thread.currentThread();
        currentEvent = event;

        try {
            super.dispatchEvent(event);
        }
        finally {
            currentEvent = null;
        }
    }

    private class FreezeTimerTask extends TimerTask {
        private AWTEvent lastEvent;

        @Override
        public void run() {
            if (lastEvent != null && lastEvent == currentEvent) {
                printStack();
            }

            lastEvent = currentEvent;
        }

        private void printStack() {
            StackTraceElement[] stackTrace = eventDispatchThread.getStackTrace();
            StringBuilder sb = new StringBuilder();
            sb.append("Freeze detected on EDT:");

            for (StackTraceElement stackElement : stackTrace) {
                sb.append(stackElement.toString()).append('\n');
            }

            //use your favorite logger
            System.out.println(sb);
        }
    }
}

Sunday, March 3, 2024

JFileChooser and the Lost Folder Selection

This article was originally posted on JRoller on July 7, 2005

It might sound like an Indiana Jones movie title, but it is an interesting problem we came across. We have a third party product which at some point displays a JFileChooser, in which you must select a directory. In old Java 1.4, this dialog box was working properly. Now that we switched to brand new 5.0, when we select a folder and click on open, it does not come back with the folder as a selected value, but instead goes into the folder. The main difference in the behavior comes from the fact that when we selected a folder, its name was visible in the selected file textfield, and now it is not.

The colleague who had to solve the problem tried to execute the program by copying the 1.4 version of JFileChooser into the bootclasspath. It did not help, so I suggested him to try with the UI class instead. And oh suprise, it works as in the old days. So he started to compare the source code of both versions, and in the ListSelectionListener, he found an interesting difference. A property which was always true before is now set to false by default. So to solve the problem, he inserted the following line in the main method:

UIManager.put("FileChooser.usesSingleFilePane", new Boolean(true));

I wonder if these properties are documented somewhere. There seems to be so many of them...

I checked in my more recent version of Java. This parameter still exists, and still does not seem to be documented.

Friday, November 3, 2023

JComboBox Editor Listening

This article was posted originally on JRoller June 3, 2005

To listen to edition event in the editor component of a JComboBox:

((JTextComponent)comboBox.getEditor().getEditorComponent()).getDocument().addDocumentListener(listener);


Monday, February 6, 2023

SwingWorker is not a Thread

 This article was originally posted on October 5th, 2011 on JRoller.com.

One practical class when developing Swing application is the SwingWorker. It allows you to start a lengthy task in the background, and then applying the result to the display on the Event Thread. The usual pattern is the following:

class MySwingWorker extends SwingWorker {
  @Override
  public Void doInBackground() {
    //perform background work
  }
 
  @Override
  protected void done() {
    //update display
  }
}


While the background work might be long, it is possible also to update the user interface with partial results. In which case, the pattern is the following:

class MySwingWorker extends SwingWorker<Void, Integer> {
  @Override
  public Void doInBackground() {
    while (!finished) {
      //perform background work
      publish(partialResult)
    }
    return result;
  }
 
  @Override
  protected void process(List<Integer> chunks) {
    //update display with partial results
  }
}


This seemed so practical for me, that I decided to use this instead of one of our Threads that is running in the background, waiting for some data, processing it, then adding one or several lines to a JTable. The idea would be to have my SwingWorker running constantly in the background, waiting for data from a blocking queue, then publishing the result to the Swing updating part. It looked like this:

class MyEternalSwingWorker extends SwingWorker {
  @Override
  public Void doInBackground() {
    while (!isCancelled()) {
      queue.take();
      //perform background work
      publish(row)
    }
    return null;
  }
 
  @Override
  protected void process(List chunks) {
    //add rows to the JTable
  }
}

If data were to arrive too fast, Swing has the possibility to coalesce all the updates, which should bring better performance. It worked quite fine, but suddenly, some of the users started to have some strange behaviors, and updates stopped quite unexpectedly for some windows. As it appears, it depends on the number of windows opened, and so on the number of such SwingWorkers running. That is when I discovered this interesting line in the SwingWorker code:

  private static final int MAX_WORKER_THREADS = 10;

SwingWorker is using a Thread Pool, with 10 Threads, and there is no way to change that. So all my nice Eternal Swing Workers were just using all the available Threads in the Pool, and no other Worker could start their background work.

So beware, SwingWorkers can not be used as Threads. They need to be used for finite work. Or be less than 10.

Update: As Eugene Ho suggested in the comments, SwingWorker is actually a Runnable. So you can run it in your own Thread without blocking anything in the SwingWorker's ThreadPool, while keeping all the advantages, such as events coalescing, of the class.

Update 2: Also, the number of Threads in the pool is a constant, but it is possible to override it with your own Thread Pool, since SwingWorker use the sun.awt.AppContext class to fetch it. Do the following:

  sun.awt.AppContext.getAppContext().put(SwingWorker.class, myThreadPoolExecutor);

Seeing the updates on my article, I can see that it is a pity that I could not retrieve the original comments, like the one from Eugene Ho. However, I checked the SwingWorker code, and all this is still true.