Reputation: 85
I have a JDialog with a thread in it. It sporadically gives me a ClassCastException when the dialog is created (which means I can have succesful ones with no exception), and I have no clue to where it should occur.
Here's a snippet of my JDialog class
public class ConfirmExitDialog extends JDialog implements Runnable,
ActionListener {
private static final long serialVersionUID = -8762051370686039110L;
private Thread dialogThread;
private boolean running;
private int result, count = 60;
private HandleExit handleExit = null;
// GUI
private JOptionPane optionPane;
private JLabel msgLabel = new JLabel();
private JButton btnYes;
private JButton btnNo;
private void updateLabelText() {
msgLabel.setText("<html>Ønsker du at afslutte dagens salg?<br>Programmet afslutter automatisk om " + count + " sekunder.</html>");
}
public int getResult() {
return result;
}
public ConfirmExitDialog(Frame frame, HandleExit handleExit) {
super(frame, false);
this.handleExit = handleExit;
setTitle("Afslut dagens salg?");
display();
running = true;
if (dialogThread == null) {
dialogThread = new Thread(this, "ConfirmExitDialog");
dialogThread.start();
}
this.setModal(true);
}
public void close() {
if (dialogThread != null)
running = false;
}
private void display() {
setLayout(new BorderLayout());
// Buttons
btnYes = new JButton("Ja");
btnYes.addActionListener(this);
btnYes.setMnemonic('J');
add(btnYes, BorderLayout.WEST);
btnNo = new JButton("Nej");
btnNo.addActionListener(this);
btnNo.setMnemonic('N');
add(btnNo, BorderLayout.EAST);
JButton[] buttons = { btnYes, btnNo };
updateLabelText();
optionPane = new JOptionPane(msgLabel, JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION, null, buttons, buttons[0]);
setContentPane(optionPane);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// Handle window closing correctly.
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
/*
* Instead of directly closing the window, we're going to change the
* JOptionPane's value property.
*/
optionPane.setValue(new Integer(JOptionPane.CLOSED_OPTION));
}
});
pack();
setVisible(true);
}
private void countDown() {
updateLabelText();
count--;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
display();
while (running && count > 0) {
System.out.println("Countdown " + count);
pack();
countDown();
Thread.sleep(1000);
}
setVisible(false);
if (count == 0)
handleExit.closeApplication(true, true);
} catch (InterruptedException ie) {
// Thread stopped
}
}
@Override
public void actionPerformed(ActionEvent event) {
Object src = event.getSource();
if (src == btnYes) {
setVisible(false);
result = JOptionPane.YES_OPTION;
running = false;
handleExit.closeApplication(true, false);
}
if (src == btnNo) {
setVisible(false);
result = JOptionPane.NO_OPTION;
running = false;
}
}
}
Here's the exception cast:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException at javax.swing.LayoutComparator.compare(LayoutComparator.java:61) at java.util.Arrays.mergeSort(Arrays.java:1293) at java.util.Arrays.mergeSort(Arrays.java:1282) at java.util.Arrays.sort(Arrays.java:1210) at java.util.Collections.sort(Collections.java:159) at javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(SortingFocusTraversalPolicy.java:119) at javax.swing.SortingFocusTraversalPolicy.getFirstComponent(SortingFocusTraversalPolicy.java:434) at javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(LayoutFocusTraversalPolicy.java:148) at javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(SortingFocusTraversalPolicy.java:511) at java.awt.FocusTraversalPolicy.getInitialComponent(FocusTraversalPolicy.java:152) at java.awt.Window.getMostRecentFocusOwner(Window.java:2131) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:629) at java.awt.Component.dispatchEventImpl(Component.java:4502) at java.awt.Container.dispatchEventImpl(Container.java:2099) at java.awt.Window.dispatchEventImpl(Window.java:2478) at java.awt.Component.dispatchEvent(Component.java:4460) at java.awt.EventQueue.dispatchEvent(EventQueue.java:599) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:101) at java.awt.EventQueue.dispatchEvent(EventQueue.java:597) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Thank you. Daniel
Upvotes: 3
Views: 2225
Reputation: 600
You are launching the dialog from a thread other than the Event Dispatcher thread. Any UI create/update should be from within the Swing's event dispatcher thread.
Try:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// show the UI here (display method in your logic?)
}
});
NOTE: Any update to an UI before it's shown (visible) may be done from outside the event dispatcher thread. BUT once the UI is displayed any changes should always be done from within the Event Dispatch thread.
Upvotes: 0
Reputation: 15706
Doing UI modifications in a Thread other than Swing's EventDispatchThread is strongly discouraged, as it can and will lead to weird side effects (just as the one you encountered).
Immagine the UI is about to repaint itself (and layout stuff) and you just interfer by changing parts of the UI at the same time using another thread. In such a situation chaos is verly likely to ensue.
For details see the Java Tutorials Lesson Concurrency in Swing
The correct way to deal with this is to have the background thread do any work that is not UI related, and schedule a UI update job in the EventDispatchThread (which will execute the job between handling events).
Example:
Thread t = new Thread() {
public void run () {
// do background work
SwingUtilities.invokeLater(new Runnable() {
public void run () {
// update UI parts
}
});
}
};
t.start();
SwingUtilities.invokeLater(Runnnable)
will schedule a Runnable for later execution, whereas
SwingUtilities.invokeAndWait(Runnnable)
will schedule a Runnable and wait until it has been executed.
Upvotes: 4
Reputation: 324157
All updates to Swing components should be done on the Event Dispatch Thread (EDT).
In your Thread you attempt to pack() the dialog every second. This code is NOT executing on the EDT.
Try using a SwingUtilities.invokeLater(...) to do the pack().
Or maybe a SwingWorker should be used instead of the Thread. Read the section from the Swing tutorial on Concurrency for more information.
Or maybe even a better approach is to start a Swing Timer to schedule closing of the dialog. When the Timer fires the code is automatically executed on the EDT. The Swing tutorial also has a section on using Timers.
Upvotes: 1