JDK-6536033 : JPopupMenu and TrayIcon ClassCastException
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-03-19
  • Updated: 2010-04-04
  • Resolved: 2007-03-20
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)java version 

"1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Server VM (build 1.6.0_01-b06, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Using the java.awt.TrayIcon's "setPopupMenu" method expects an AWT popup menu.  However, the AWT menu does not match the native OS popup menu appearance (large arial font instead of smaller tahoma font on my windows desktop), so I attempted to use a JPopupMenu instead (worked nicely with JDIC), according to this blog entry:
http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html

This works fine the first time it's rendered, but all subsequent attempts to right-click the tray icon provoke a ClassCastException.  This looks like:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6309495 ("JMenuItem and TrayIcon does not work when in same application")
...but is different.  Looking at the source code referred to by my stack trace, it would almost certainly appear to be the same *type* of error, but in a different block of code.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please refer to http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html which has a straightforward example.  Bear in mind that the first attempt seems okay, it's thereafter that it fails.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
A JPopupMenu is displayed for the TrayIcon everytime it's right-clicked.
ACTUAL -
It is only displayed the first time.  All subsequent requests fail.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: java.awt.TrayIcon cannot be cast to java.awt.Component
	at javax.swing.plaf.basic.BasicPopupMenuUI$MouseGrabber.eventDispatched(BasicPopupMenuUI.java:796)
	at java.awt.Toolkit$SelectiveAWTEventListener.eventDispatched(Toolkit.java:2360)
	at java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Toolkit.java:2252)
	at java.awt.Toolkit.notifyAWTEventListeners(Toolkit.java:2210)
	at java.awt.TrayIcon.dispatchEvent(TrayIcon.java:689)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:604)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.RenderingHints;
import java.awt.BasicStroke;
import java.awt.geom.Path2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.BorderFactory;



public class Incident926606
{
    private Incident926606() {}

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable(){
            public void run() { createAndShowGUI(); }
        });
    }

    private static void createAndShowGUI()
    {
        if (!SystemTray.isSupported())
        {
            JOptionPane.showMessageDialog(null,
                "SystemTray is not supported",
                "Incident926606",
                JOptionPane.ERROR_MESSAGE);
            return;
        }

        // draw an icon
        BufferedImage icon = new BufferedImage(16,16,TYPE_INT_ARGB);
        Graphics2D    g2d  = icon.createGraphics();
        Path2D.Float  path = new Path2D.Float(Path2D.WIND_NON_ZERO);
        path.moveTo(0,1);
        path.lineTo(14,8);
        path.lineTo(0,14);
        path.closePath();
        g2d.setColor(Color.BLUE);
        g2d.fillRect(0,0,icon.getWidth(),icon.getHeight());
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON
        );
        g2d.setColor(Color.YELLOW);
        g2d.fill(path);
        g2d.setColor(Color.WHITE);
        g2d.setStroke(new BasicStroke(2));
        g2d.draw(path);
        g2d.dispose();

        // make a swing popup menu
        // then bind it to mouse events
        // we're doing this because awt popup doesn't match native equivalent
        final JPopupMenu popup = new JPopupMenu();
        for (int i = 1; i <= 5; i++)
        {
            popup.add(new JMenuItem("Mock option "+i));
        }
        final TrayIcon trayIcon = new TrayIcon(icon);
        trayIcon.addMouseListener(new MouseAdapter(){
            private void maybeShowPopup(MouseEvent evt)
            {
                if (evt.isPopupTrigger())
                {
                    popup.setLocation(evt.getX(),evt.getY());
                    popup.setInvoker(popup);
                    popup.setVisible(true);
                }
            }
            @Override
            public void mousePressed(MouseEvent evt)
            {
                maybeShowPopup(evt);
            }
            @Override
            public void mouseReleased(MouseEvent evt)
            {
                maybeShowPopup(evt);
            }
        });

        try
        {
            SystemTray.getSystemTray().add(trayIcon);
        }
        catch (AWTException e)
        {
            JOptionPane.showMessageDialog(null,
                e.toString(),
                "Incident926606",
                JOptionPane.ERROR_MESSAGE);
            return;
        }

        JLabel label = new JLabel("<html><body>" +
            "Right-click tray icon twice.<br>" +
            "First time, you'll see a JPopupMenu.<br>" +
            "Second time, it'll crash (watch STDOUT).<br>" +
            "If you select an option the first time,<br>" +
            "you won't see a problem." +
            "</body></html>"
        );
        label.setBorder(BorderFactory.createEmptyBorder(14,14,14,14));

        JFrame frame = new JFrame("Incident926606");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.setIconImage(icon);
        frame.add(label);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosing(WindowEvent evt)
            {
                SystemTray.getSystemTray().remove(trayIcon);
                System.exit(0);
            }
        });
        frame.setVisible(true);
    }
} 
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use an AWT popup menu.  But it's really ugly and doesn't fit in at all.  I have in the past used JDIC but that project is apparently dead.