I'm using a JDialog
instatiated at startup of the application, to show messages several times. Sometimes the dialog and its controls are invisible, but clickable.
The JDialog
is instantiated only once and set to visible 'true' each time a message should be shown and then set to visible 'false' till the next message should be shown.
To exlude multithreading related problems, i always use SwingUtilities.invokeLater(...)
for ui calls, when a thread creates a message and shows the dialog.
Because its a huge project and my problem isn't related to any specific code, i don't post code but describe the problem. The problems seems not to be reproducible but happens sometimes, so it might be a threading problem despite running each related call on the EDT
.
What am I doing wrong?
public class MessageHandler {
private volatile static MessageHandler messageHandler = null;
private List<Message>messages = null;
private volatile WeakReference<MessagesPanelControl> view = null;
private final Object viewSynchronizationObject = new Object();
private MessageHandler() {
messages = new ArrayList<Message>();
}
public static MessageHandler getInstance() {
MessageHandler result = messageHandler;
if (result == null) {
synchronized (MessageHandler.class) {
result = messageHandler;
if (result == null)
messageHandler = result = new MessageHandler();
}
}
return result;
}
public void registerView(MessagesPanelControl view) {
this.view = new WeakReference<MessagesPanelControl>(view);
}
public void addMessage(final Message message) {
synchronized (viewSynchronizationObject) {
messages.add(message);
Collections.sort(messages);
updateView();
}
}
private void updateView() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
synchronized (viewSynchronizationObject) {
if (view == null) {
return;
}
MessagesPanelControl mpc = view.get();
if (mpc != null) {
mpc.updateView();
}
}
}
});
}
}
In the MainFrame class i'm doing this initialization once at startup:
MessagesPanelControl mp = new MessagesPanelControl();
MessageHandler.getInstance().registerView(mp);
LockPane messageBasicPane = new LockPane(this, mp);
And then in different threads this is called to show a message via the MessageHandler
Singleton:
MessageHandler.getInstance().addMessage(Message.getSimpleMessage("Error", "Fatal error occured", Message.MessageIcon.ERROR));
I didn't post all details, but all necessary parts to understand the whole problme, hope it makes it more understandable.
The MessagePanelControl
(mpc) is a class, that extends JPanel
. Its updateView()
-method creates the message controlls based on the MessageHandler's
message list like buttons, labels and icons. Finally the method sends a Delegate
like command to the main frame to show the JDialog
containing the MessagePanelControl
.
Summarized it does:
- messageList.size()>0: create message panels for each message in list in
MessageHandler
- messageList.size()>0: show JDialog with
MessagePanelControl
messageList.size()<=0: hide JDialog with
MessagePanelControl
public void updateView() { synchronized (viewMPCSynchronizationObject) { Utils.throwExceptionWhenNotOnEDT();
JPanel messagesListPanel = new JPanel(); scrollPane.setViewportView(messagesListPanel); scrollPane.setBorder(null); messagesListPanel.setLayout(new BoxLayout(messagesListPanel, BoxLayout.Y_AXIS)); if (MessageHandler.getInstance().getMessages() != null && MessageHandler.getInstance().getMessages().size() > 0) { [...] //Create buttons, text icons... for each message [...] SwingUtilities.invokeLater(new Runnable() { public void run() { MainFrame().showMessageBoard(); } }); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { MainFrame().closeMessageBoard(); } }); } repaint(); }
}
MainFrame:
//Show Messageboard
public void showMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(true);
messageBasicPane.repaint();
}
}
[...]
//Close Messageboard
public void closeMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(false);
}
}
This line creates the JDialog
, in detail:
[...]
public LockPane(JFrame parentFrame, JComponent componentToShow, Dimension paneSize, float opacity, ModalityType modality) {
super(parentFrame, true);
Utils.throwExceptionWhenNotOnEDT();
createDialog(paneSize, opacity, modality);
if (componentToShow != null) {
add(componentToShow);
}
pack();
}
private void createDialog(Dimension paneSize, float opacity, ModalityType modality) {
Utils.throwExceptionWhenNotOnEDT();
setUndecorated(true);
setModalityType(modality);
if (opacity < 1 && opacity >= 0)
com.sun.awt.AWTUtilities.setWindowOpacity(this, opacity);
setSize(paneSize);
setPreferredSize(paneSize);
setMaximumSize(paneSize);
setBounds(0, 0, paneSize.width, paneSize.height);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
}
[...]
A new observation with Java VisualVM
is, that the AWT-EventQueue isn't blocked, only sometime there are small periods of 'wait' but nothing blocking. Another strange thing is, that sometimes my JDialog
is fully transparent (invisible) and sometimes its white with the desired opacity.