-
Bug
-
Resolution: Fixed
-
P4
-
1.2.1, 1.3.1, 1.4.0, 1.4.1, 1.4.2, 5.0
-
b50
-
generic, x86, sparc
-
generic, linux, solaris_9, windows_nt, windows_2000, windows_xp
FULL PRODUCT VERSION :
java version "1.4.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build
1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed
mode)
Same problem occurs under 1.3.1_01.
glibc-2.1.3-21
Linux jesse_laptop.netbeans.com 2.2.12-20 #1 Mon Sep 27
10:40:35 EDT 1999 i686 unknown
Red Hat Linux release 6.1 (Cartman)
A DESCRIPTION OF THE PROBLEM :
Various Swing action containers, for example AbstractButton,
JMenu, and JPopupMenu, permit a javax.swing.Action to be
added directly as of 1.3. They listen for property changes
on property "enabled" in order to update action state.
However if the Action implementation fires a
PropertyChangeEvent with a null newValue, these containers
simply throw NPE.
The reason is obvious in their code, e.g. JPopupMenu.java:
} else if (propertyName.equals("enabled")) {
Boolean enabledState = (Boolean) e.getNewValue();
menuItem.setEnabled(enabledState.booleanValue());
The code should check for enabledState == null and if so,
use ((Action)e.getSource()).isEnabled() instead. It should
also trigger the clause if propertyName == null. I imagine
there are other such places in the code, I have only noticed
these.
According to the JavaBeans Specification it is acceptable to
use a null newValue (or oldValue, or propertyName) in case
an event source finds it too expensive to compute old and
new values, or is not sure whether the values will actually
be needed by someone but wishes to ensure that a change is
fired nonetheless:
http://java.sun.com/j2se/1.3/docs/api/java/beans/PropertyChangeEvent.html
"Null values may be provided for the old and the new values
if their true values are not known. An event source may send
a null object as the name to indicate that an arbitrary set
of if its properties have changed. In this case the old and
new values should also be null."
I could see no documentation overriding this proviso
specifically for Action's, so it seems that the containers
are at fault.
FYI, bugs and troubles in NetBeans/Forte for Java caused by
this:
http://www.netbeans.org/issues/show_bug.cgi?id=13084
http://openide.netbeans.org/servlets/ReadMsg?msgId=219999&listName=dev
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the enclosed class. When the frame appears,
select the menu item; an NPE is thrown. Select the toolbar
button; ditto. Select the button and when the popup menu
appears, select its item; ditto.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected results: all three uses of the action do nothing
and throw no exception.
Actual results: they do nothing but throw exceptions, see
Error Messages section.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
at
javax.swing.JMenu$ActionChangedListener.propertyChange(JMenu.java:678)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:267)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:235)
at
javax.swing.AbstractAction.firePropertyChange(AbstractAction.java:181)
at
TestNullPropChangeNewValuesFromAction.actionPerformed(TestNullPropChangeNewValuesFromAction.java:10)
at
javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1770)
at
javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1823)
at
javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:422)
at
javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:260)
at
javax.swing.AbstractButton.doClick(AbstractButton.java:292)
at
javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1094)
at
javax.swing.plaf.basic.BasicMenuItemUI$MenuDragMouseHandler.menuDragMouseReleased(BasicMenuItemUI.java:993)
at
javax.swing.JMenuItem.fireMenuDragMouseReleased(JMenuItem.java:585)
at
javax.swing.JMenuItem.processMenuDragMouseEvent(JMenuItem.java:482)
at
javax.swing.JMenuItem.processMouseEvent(JMenuItem.java:429)
at
javax.swing.MenuSelectionManager.processMouseEvent(MenuSelectionManager.java:282)
at
javax.swing.plaf.basic.BasicMenuUI$MouseInputHandler.mouseReleased(BasicMenuUI.java:337)
at
java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:230)
at
java.awt.Component.processMouseEvent(Component.java:5020)
at java.awt.Component.processEvent(Component.java:4819)
at java.awt.Container.processEvent(Container.java:1383)
at
java.awt.Component.dispatchEventImpl(Component.java:3527)
at
java.awt.Container.dispatchEventImpl(Container.java:1440)
at java.awt.Component.dispatchEvent(Component.java:3368)
at
java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3219)
at
java.awt.LightweightDispatcher.processMouseEvent(Container.java:2930)
at
java.awt.LightweightDispatcher.dispatchEvent(Container.java:2866)
at
java.awt.Container.dispatchEventImpl(Container.java:1426)
at java.awt.Window.dispatchEventImpl(Window.java:1568)
at java.awt.Component.dispatchEvent(Component.java:3368)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:448)
at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:193)
at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:147)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:141)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:133)
at
java.awt.EventDispatchThread.run(EventDispatchThread.java:101)
java.lang.NullPointerException
at
javax.swing.AbstractButton$ButtonActionPropertyChangeListener.propertyChange(AbstractButton.java:1154)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:267)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:235)
at
javax.swing.AbstractAction.firePropertyChange(AbstractAction.java:181)
at
TestNullPropChangeNewValuesFromAction.actionPerformed(TestNullPropChangeNewValuesFromAction.java:10)
at
javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1770)
at
javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1823)
at
javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:422)
at
javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:260)
at
javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:261)
at
java.awt.Component.processMouseEvent(Component.java:5020)
at java.awt.Component.processEvent(Component.java:4819)
at java.awt.Container.processEvent(Container.java:1383)
at
java.awt.Component.dispatchEventImpl(Component.java:3527)
at
java.awt.Container.dispatchEventImpl(Container.java:1440)
at java.awt.Component.dispatchEvent(Component.java:3368)
at
java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3219)
at
java.awt.LightweightDispatcher.processMouseEvent(Container.java:2930)
at
java.awt.LightweightDispatcher.dispatchEvent(Container.java:2866)
at
java.awt.Container.dispatchEventImpl(Container.java:1426)
at java.awt.Window.dispatchEventImpl(Window.java:1568)
at java.awt.Component.dispatchEvent(Component.java:3368)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:448)
at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:193)
at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:147)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:141)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:133)
at
java.awt.EventDispatchThread.run(EventDispatchThread.java:101)
java.lang.NullPointerException
at
javax.swing.JPopupMenu$ActionChangedListener.propertyChange(JPopupMenu.java:393)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:267)
at
javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:235)
at
javax.swing.AbstractAction.firePropertyChange(AbstractAction.java:181)
at
TestNullPropChangeNewValuesFromAction.actionPerformed(TestNullPropChangeNewValuesFromAction.java:10)
at
javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1770)
at
javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1823)
at
javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:422)
at
javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:260)
at
javax.swing.AbstractButton.doClick(AbstractButton.java:292)
at
javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1094)
at
javax.swing.plaf.basic.BasicMenuItemUI$MouseInputHandler.mouseReleased(BasicMenuItemUI.java:934)
at
java.awt.Component.processMouseEvent(Component.java:5020)
at java.awt.Component.processEvent(Component.java:4819)
at java.awt.Container.processEvent(Container.java:1383)
at
java.awt.Component.dispatchEventImpl(Component.java:3527)
at
java.awt.Container.dispatchEventImpl(Container.java:1440)
at java.awt.Component.dispatchEvent(Component.java:3368)
at
java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3219)
at
java.awt.LightweightDispatcher.processMouseEvent(Container.java:2930)
at
java.awt.LightweightDispatcher.dispatchEvent(Container.java:2866)
at
java.awt.Container.dispatchEventImpl(Container.java:1426)
at java.awt.Window.dispatchEventImpl(Window.java:1568)
at java.awt.Component.dispatchEvent(Component.java:3368)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:448)
at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:193)
at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:147)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:141)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:133)
at
java.awt.EventDispatchThread.run(EventDispatchThread.java:101)
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TestNullPropChangeNewValuesFromAction extends
AbstractAction {
public TestNullPropChangeNewValuesFromAction() {
super("Hi mom");
}
public void actionPerformed(ActionEvent e) {
firePropertyChange("enabled", null, null);
}
public static void main (String args[]) {
JFrame frame = new
JFrame("TestNullPropChangeNewValuesFromAction");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar menubar = new JMenuBar();
JMenu menu = new JMenu("Test");
menu.add(new
TestNullPropChangeNewValuesFromAction());
menubar.add(menu);
frame.setJMenuBar(menubar);
frame.getContentPane().setLayout(new FlowLayout());
JToolBar toolbar = new JToolBar();
toolbar.add(new
TestNullPropChangeNewValuesFromAction());
frame.getContentPane().add(toolbar);
final JButton button = new JButton("Show popup");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JPopupMenu popup = new JPopupMenu();
popup.add(new
TestNullPropChangeNewValuesFromAction());
popup.show(button, 10, 10);
}
});
frame.getContentPane().add(button);
frame.pack();
frame.setVisible(true);
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
A work around is to always be sure to pass a newValue (and a
property name). The disadvantage is potential loss of
efficiency, depending on the application.
(Review ID: 137658)
======================================================================
- duplicates
-
JDK-4240860 JToolBar.ActionChangedListener not persistant
- Closed
-
JDK-4765360 JMenuItem.add(Action) does not handle Action.SHORT_DESCRIPTION properly
- Closed
-
JDK-4905329 Cannot serialize JButton that was configured from an Action
- Closed
-
JDK-5026829 The behaviour of certain components in combination with Actions is inconsistent.
- Closed
-
JDK-6201473 JMenuItems created by Action are not garbage collected until Action is collected
- Closed
-
JDK-6386583 Using JPopupMenu.add(Action) gives a memory leak
- Closed
-
JDK-6442683 JMenuItem$MenuItemPropertyChangeListener doesnt have default constuctor
- Closed
-
JDK-6442684 AbstractButton$ButtonActionPropertyChangeListener has no default constuctor
- Closed
-
JDK-4461866 need a replacement for JToolBar.add(Action) method
- Closed
-
JDK-4751971 RFE: problems with AbstractButton.configurePropertiesFromAction()
- Closed
- relates to
-
JDK-6371910 Actions regression when changing enabled via putValue("enabled", ...);
- Resolved