December 12, 1998

Question

There's a common problem whereby Java programs using GUIs don't fully exit when the last window is closed and disposed. Has anybody tried retrieving a reference to the AWT thread and marking it as a daemon thread in order to get your application to auto-exit when all windows are closed and disposed? Did it work? If so how did you keep your application running after main() finished? Or have you come up with any other means, short of calling System.exit(0) to get your application to quit when the last window closes?

Answer

Nobody came up with a solid answer to this question though there were a number of useful partial solutions. Colin Cooper put his finger on the heart of the problem.
I foresee a problem here: you can only change the Daemon status of a thread before it starts running. To do this with the awt, you'd have to be working at a pretty low level...

Ranjit Pillai:

Looks like you can't change the state of the EventDispatchThread. Setting the thread to daemon resulted in an IllegalThreadStateException. Perhaps the logic is that some threads are intended to be strictly non-daemon.

Soln: How about getting a reference to the thread's ThreadGroup, emptying it and then calling destroy().


Ralph Prescott of LPA Software suggested this

Q: How to shut down a GUI

A: For 1.1 we simply implemented a reference counting scheme with our frames. Each frame was responsible for incrementing the count on creation and decrementing it on close via a WindowListener. When the frame count went to zero, down she went with System.exit().

Other schemes could involve a SecurityManager trapping canExit() and disallowing it if there are still frames up.

Now with 1.2 there is a static method Frame.getFrames() which lets you know if there are any other frames instantiated. So you do this in a SecurityManager and/or your own Frame. ----

It's not much of an answer. I hope there's a better one out there :-\


Jochen Bedersdorfer had the most detailed answer, and wins the copy of JavaBeans.

I just tried to dig a bit into the problem (well I wouldn't call it a problem, but a design decision) with the last window not ending the application.

I used a simple subclass of JFrame to take a look at the threads in the main group after the creation of the frame:

java.lang.ThreadGroup[name=main,maxpri=10]
        Thread[main,5,main]
        Thread[AWT-EventQueue-0,5,main]
        Thread[AWT-Input,5,main]
        Thread[AWT-Motif,5,main]

Obviously the names and perhaps count of running threads are platform dependent. (After disposing the frame another ScreenUpdater thread was added to the list.) With jikesdb I could confirm that all but AWT-Input are in sleeping mode after that. Your suggestion about marking these threads as demons doesn't work, because the threads are already started then. An IllegalThreadStateException is thrown if you try that.

So, since it seems that a. the AWT threads are platform dependent b. stopping these threads automatically is not possible (since you can't influence AWT startup) the only option seems to be to use an extended WindowAdapter like this one:


import java.awt.*;
import java.awt.event.*;
import java.util.*;

/** A simple window monitor that ends an application if all
    windows registered with the monitor have been closed.
    You can either use the static addWindow-method or use 
    an instance of WindowMonitor and use the add-method.
    
    @author Jochen Bedersdorfer (beders@dfki.de)

    You are free to use and modify this code.
    I don't take any responsibilities about the fitness or correctness
    of this code. [include standard disclaimer]
**/
public class WindowMonitor extends WindowAdapter {
  static WindowMonitor oneMonitor;
  Vector windowList;

  WindowMonitor() {
    windowList = new Vector();
  }

  /** Register the window with this monitor and
      register the monitor as window listener. This static method is
      useful if you don't want to manage an instance of WindowMonitor yourself **/
  public static void addWindow(Window w) {
    if (oneMonitor == null) // deferred allocation
      oneMonitor = new WindowMonitor();
    oneMonitor.add(w);
  }


  /** Register the window with this monitor and
      register the monitor as window listener **/
  public void add(Window w) {
    if (windowList.indexOf(w) == -1) {
      windowList.addElement(w);
      w.addWindowListener(this);
    }
  }

  /** End the application if all windows registered with the monitor
      are closed otherwise dispose the window **/
  public void windowClosing(WindowEvent we) {
    Window source = we.getWindow();
    int i;
    if ((i = windowList.indexOf(source)) == -1)
      throw new IllegalStateException("Window closing event was received from an unregistered window. Use the monitor's add method to register a window!");
    windowList.removeElementAt(i);
    if (windowList.size() == 0) // all windows removed, shutdown
      System.exit(0);
    else
      source.dispose();
  }

}

Here's a little test program for it (replace JFrame with Frame if you don't have swing):


import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class TestWindowMonitor {
  
  public static void main(String[] args) {
    WindowMonitor wm = new WindowMonitor();
    JFrame frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    wm.add(frame);
    frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    wm.add(frame);
    frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    wm.add(frame);
    }
}

Start it up and close the windows in any order you like. Here's another version that uses the static methods of WindowMonitor:


import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class TestStaticWindowMonitor {
  
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    WindowMonitor.addWindow(frame);
    frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    WindowMonitor.addWindow(frame);
    frame = new JFrame();
    frame.setSize(100,100);
    frame.setVisible(true);
    WindowMonitor.addWindow(frame);
    }

}

Well, of course, I didn't find any non-obvious solution, but since you can't control AWT startup, you are lost. You can of course destroy all threads in the main group if you detect that the last window has closed, but then you can call System.exit(0) without fussing around with Thread.currentThread().getThreadGroup().enumerate(...) :)


Gerhard Paulus noticed that you don't even have to bring up a window to have the problem.

FWIW, this is a small app which demonstrates the Font problem: (JDK 1.1.7A)

If you run the following Hello world app you must end the app with Ctrl+C. The app should end with a clean exit.

import java.awt.Font ;

class Hello {

static public void main(String[] params) {

System.out.println("Hello world ...") ;

Font defaultFont= new Font("Dialog", Font.PLAIN, 12) ;

}

}


[ Cafe au Lait | Books | Trade Shows | Links | FAQ | Tutorial | User Groups ]

Copyright 1998, 1999 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified February 14, 1999