swingx
  1. swingx
  2. SWINGX-1418

Ensure that lookup for missing UI delegates returns null

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 1.6.2
    • Fix Version/s: 1.6.3
    • Component/s: Plaf
    • Labels:
      None

      Description

      A NPE can occur for SwingX based third party components in NetBeans visual editor.

      java.lang.NullPointerException
      at org.jdesktop.swingx.plaf.LookAndFeelAddons.getUI(LookAndFeelAddons.java:315)
      at de.javasoft.swing.JYLabel.updateUI(JYLabel.java:77)
      at javax.swing.JLabel.<init>(JLabel.java:145)
      at javax.swing.JLabel.<init>(JLabel.java:216)
      at de.javasoft.swing.JYLabel.<init>(JYLabel.java:49)
      at de.javasoft.swing.JYLabel.<init>(JYLabel.java:46)
      at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$5.<init>(BasicJYTabbedPaneUI.java:1029)
      at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.addTab(BasicJYTabbedPaneUI.java:1029)
      at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.access$17(BasicJYTabbedPaneUI.java:1021)
      at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$4.propertyChange(BasicJYTabbedPaneUI.java:809)
      at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
      at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
      at java.awt.Component.firePropertyChange(Component.java:8132)
      at de.javasoft.swing.JYTabbedPane.firePropertyChange(JYTabbedPane.java:1345)
      at de.javasoft.swing.JYTabbedPane.insertTab(JYTabbedPane.java:386)
      [catch] at org.netbeans.modules.form.layoutsupport.delegates.JTabbedPaneSupport.addComponentsToContainer(JTabbedPaneSupport.java:259)
      at org.netbeans.modules.form.layoutsupport.LayoutSupportManager.addComponentsToContainer(LayoutSupportManager.java:668)
      at org.netbeans.modules.form.VisualReplicator.addComponent(VisualReplicator.java:391)
      at org.netbeans.modules.form.VisualReplicator.updateAddedComponents(VisualReplicator.java:357)
      at org.netbeans.modules.form.FormDesigner$FormListener.run(FormDesigner.java:2414)
      at org.netbeans.modules.form.FormLAF$3.run(FormLAF.java:329)
      at org.openide.util.Mutex.doEventAccess(Mutex.java:1361)
      at org.openide.util.Mutex.readAccess(Mutex.java:271)
      at org.netbeans.modules.form.FormLAF.executeWithLookAndFeel(FormLAF.java:312)
      at org.netbeans.modules.form.FormDesigner$FormListener.processEvents(FormDesigner.java:2339)
      at org.netbeans.modules.form.FormDesigner$FormListener.formChanged(FormDesigner.java:2305)
      at org.netbeans.modules.form.FormModel.fireEvents(FormModel.java:1296)
      at org.netbeans.modules.form.FormModel.fireEventBatch(FormModel.java:1269)
      at org.netbeans.modules.form.FormModel.firePendingEvents(FormModel.java:1232)
      at org.netbeans.modules.form.FormModel.access$000(FormModel.java:68)
      at org.netbeans.modules.form.FormModel$2.run(FormModel.java:1212)
      at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
      at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
      at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:148)
      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)

      Fix proposal: simply add a ui null check
      if (ui == null)
      return null;
      String realUI = ui.getClass().getName();
      Class<?> realUIClass;
      try {
      ...

        Activity

        Hide
        Karl Schaefer added a comment -

        Wolfgang,

        I am willing to make the change on face value, but before I do what's causing it? Was there a mismatch between UIClassID leading to a null ComponentUI class? Or, was it another variation of the ClassLoader issues in LookAndFeelAddons?

        Karl

        Show
        Karl Schaefer added a comment - Wolfgang, I am willing to make the change on face value, but before I do what's causing it? Was there a mismatch between UIClassID leading to a null ComponentUI class? Or, was it another variation of the ClassLoader issues in LookAndFeelAddons? Karl
        Hide
        wzberger added a comment -

        Because the issue is also NetBeans related, it's hard to find the real cause. The issue occurs only in combination with NetBeans. The UIClassId key is definitely provided (used JRE with modified UIManager for checking) but can't be found - for me it looks like a class loader issue.

        Thanks,
        Wolfgang

        Show
        wzberger added a comment - Because the issue is also NetBeans related, it's hard to find the real cause. The issue occurs only in combination with NetBeans. The UIClassId key is definitely provided (used JRE with modified UIManager for checking) but can't be found - for me it looks like a class loader issue. Thanks, Wolfgang
        Hide
        Karl Schaefer added a comment -

        If you're willing to help me, I'd like to try to solve the underlying issue, but I don't have a clue how to use NetBeans. I tried to import my Eclipse projects over the weekend to get up and running quickly, but that failed. If you can give me the quickest way to get up and running with NetBeans to elicit this issue, I'd really appreciate it.

        Show
        Karl Schaefer added a comment - If you're willing to help me, I'd like to try to solve the underlying issue, but I don't have a clue how to use NetBeans. I tried to import my Eclipse projects over the weekend to get up and running quickly, but that failed. If you can give me the quickest way to get up and running with NetBeans to elicit this issue, I'd really appreciate it.
        Hide
        wzberger added a comment -

        Sorry for the delay - as always being a bit busy

        • Simply download and install NetBeans
        • Create a new project and choose Samples/Java/GUI Form Examples
        • Open menu Tools/Libraries and choose "New library...", name:SwingX
        • Select "Add Jar/Folder..." button and add your jar files
        • for the mentioned issue also add synthetica.jar, syntheticaAddons.jar, jywidgets.jar
        • in the GUIFormExamples project select Antenna.java
        • in case that the form doesn't appear select the "Design" button just below the tab
        • Open menu Tools/Palette/Swing AWT Components
        • select "Add from Library...", choose SwingX and needed JX* components
        • for the issue also add JYTabbedPane
        • the components should now appear in the Palette view
        • select JYTabbedPane and place the component somewhere in the panel
        • select a JPanel and place the component within the JYTabbedPane
          in the Navigator view you can see the component hierarchy
          make sure that the panel is child of JYTabbedPane
        • select the preview button (eye) to execute the form
        • when moving the mouse over the tabbed pane the issue occurs

        You can find the stack trace in the log file which is located in userDir/.netbeans/version/var/log/messages.log

        The issue does not occur if a JYLabel is added to the form first. I'm still not quite sure why the issue occurs. However, by adding a null check we only get the Addons warning and everything works fine.

        Thanks,
        Wolfgang

        Show
        wzberger added a comment - Sorry for the delay - as always being a bit busy Simply download and install NetBeans Create a new project and choose Samples/Java/GUI Form Examples Open menu Tools/Libraries and choose "New library...", name:SwingX Select "Add Jar/Folder..." button and add your jar files for the mentioned issue also add synthetica.jar, syntheticaAddons.jar, jywidgets.jar in the GUIFormExamples project select Antenna.java in case that the form doesn't appear select the "Design" button just below the tab Open menu Tools/Palette/Swing AWT Components select "Add from Library...", choose SwingX and needed JX* components for the issue also add JYTabbedPane the components should now appear in the Palette view select JYTabbedPane and place the component somewhere in the panel select a JPanel and place the component within the JYTabbedPane in the Navigator view you can see the component hierarchy make sure that the panel is child of JYTabbedPane select the preview button (eye) to execute the form when moving the mouse over the tabbed pane the issue occurs You can find the stack trace in the log file which is located in userDir/.netbeans/version/var/log/messages.log The issue does not occur if a JYLabel is added to the form first. I'm still not quite sure why the issue occurs. However, by adding a null check we only get the Addons warning and everything works fine. Thanks, Wolfgang
        Hide
        wzberger added a comment -

        Finally found some time for further investigations. It looks like the issue is caused by NetBeans visual editor (Form module - FormLAF.java). The method #useDesignerLookAndFeel() seems to remove the default keys temporarily. Unfortunately components which dynamically create new (event triggered) sub-components like JYTabbedPane does, will fail. I will report the issue to NetBeans.

        Exception:
        java.lang.Error
        at javax.swing.UIDefaults.getUIError(UIDefaults.java:783)
        at javax.swing.MultiUIDefaults.getUIError(MultiUIDefaults.java:150)
        at javax.swing.UIDefaults.getUI(UIDefaults.java:813)
        at javax.swing.UIManager.getUI(UIManager.java:1032)
        at org.jdesktop.swingx.plaf.LookAndFeelAddons.getUI(LookAndFeelAddons.java:311)
        at de.javasoft.swing.JYLabel.updateUI(JYLabel.java:78)
        at javax.swing.JLabel.<init>(JLabel.java:145)
        at javax.swing.JLabel.<init>(JLabel.java:175)
        at de.javasoft.swing.JYLabel.<init>(JYLabel.java:53)
        at de.javasoft.swing.JYLabel.<init>(JYLabel.java:48)
        at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$5.<init>(BasicJYTabbedPaneUI.java:1039)
        at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.addTab(BasicJYTabbedPaneUI.java:1039)
        at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.access$17(BasicJYTabbedPaneUI.java:1031)
        at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$4.propertyChange(BasicJYTabbedPaneUI.java:819)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
        at java.awt.Component.firePropertyChange(Component.java:8132)
        at de.javasoft.swing.JYTabbedPane.firePropertyChange(JYTabbedPane.java:1345)
        at de.javasoft.swing.JYTabbedPane.insertTab(JYTabbedPane.java:386)
        at org.netbeans.modules.form.layoutsupport.delegates.JTabbedPaneSupport.addComponentsToContainer(JTabbedPaneSupport.java:259)
        at org.netbeans.modules.form.layoutsupport.LayoutSupportManager.addComponentsToContainer(LayoutSupportManager.java:668)
        at org.netbeans.modules.form.VisualReplicator.addComponent(VisualReplicator.java:391)
        at org.netbeans.modules.form.VisualReplicator.updateAddedComponents(VisualReplicator.java:357)
        at org.netbeans.modules.form.FormDesigner$FormListener.run(FormDesigner.java:2414)
        at org.netbeans.modules.form.FormLAF$3.run(FormLAF.java:329)
        at org.openide.util.Mutex.doEventAccess(Mutex.java:1361)
        at org.openide.util.Mutex.readAccess(Mutex.java:271)
        at org.netbeans.modules.form.FormLAF.executeWithLookAndFeel(FormLAF.java:312)
        at org.netbeans.modules.form.FormDesigner$FormListener.processEvents(FormDesigner.java:2339)
        at org.netbeans.modules.form.FormDesigner$FormListener.formChanged(FormDesigner.java:2305)
        at org.netbeans.modules.form.FormModel.fireEvents(FormModel.java:1296)
        at org.netbeans.modules.form.FormModel.fireEventBatch(FormModel.java:1269)
        at org.netbeans.modules.form.FormModel.firePendingEvents(FormModel.java:1232)
        at org.netbeans.modules.form.FormModel.access$000(FormModel.java:68)
        at org.netbeans.modules.form.FormModel$2.run(FormModel.java:1212)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:137)
        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)

        Related FormLAF method:
        private static void useDesignerLookAndFeel(FormModel formModel) {
        if (!initialized) {
        try

        { initialize(); }

        catch (Exception ex)

        { Logger.getLogger(FormLAF.class.getName()).log(Level.INFO, ex.getMessage(), ex); }

        }
        UIDefaults defaults = UIManager.getDefaults();
        netbeansDefaults.clear();
        copyMultiUIDefaults(defaults, netbeansDefaults);
        netbeansDefaults.keySet().removeAll(userDefaults.keySet());
        defaults.keySet().removeAll(netbeansDefaults.keySet());

        if (!preview)

        { setUseDesignerDefaults(formModel); }

        else if (MetalLookAndFeel.class.isAssignableFrom(previewLaf))

        { MetalLookAndFeel.setCurrentTheme(lafToTheme.get(previewLaf)); }

        }

        Thanks,
        Wolfgang

        Show
        wzberger added a comment - Finally found some time for further investigations. It looks like the issue is caused by NetBeans visual editor (Form module - FormLAF.java). The method #useDesignerLookAndFeel() seems to remove the default keys temporarily. Unfortunately components which dynamically create new (event triggered) sub-components like JYTabbedPane does, will fail. I will report the issue to NetBeans. Exception: java.lang.Error at javax.swing.UIDefaults.getUIError(UIDefaults.java:783) at javax.swing.MultiUIDefaults.getUIError(MultiUIDefaults.java:150) at javax.swing.UIDefaults.getUI(UIDefaults.java:813) at javax.swing.UIManager.getUI(UIManager.java:1032) at org.jdesktop.swingx.plaf.LookAndFeelAddons.getUI(LookAndFeelAddons.java:311) at de.javasoft.swing.JYLabel.updateUI(JYLabel.java:78) at javax.swing.JLabel.<init>(JLabel.java:145) at javax.swing.JLabel.<init>(JLabel.java:175) at de.javasoft.swing.JYLabel.<init>(JYLabel.java:53) at de.javasoft.swing.JYLabel.<init>(JYLabel.java:48) at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$5.<init>(BasicJYTabbedPaneUI.java:1039) at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.addTab(BasicJYTabbedPaneUI.java:1039) at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI.access$17(BasicJYTabbedPaneUI.java:1031) at de.javasoft.swing.plaf.basic.BasicJYTabbedPaneUI$4.propertyChange(BasicJYTabbedPaneUI.java:819) at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339) at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276) at java.awt.Component.firePropertyChange(Component.java:8132) at de.javasoft.swing.JYTabbedPane.firePropertyChange(JYTabbedPane.java:1345) at de.javasoft.swing.JYTabbedPane.insertTab(JYTabbedPane.java:386) at org.netbeans.modules.form.layoutsupport.delegates.JTabbedPaneSupport.addComponentsToContainer(JTabbedPaneSupport.java:259) at org.netbeans.modules.form.layoutsupport.LayoutSupportManager.addComponentsToContainer(LayoutSupportManager.java:668) at org.netbeans.modules.form.VisualReplicator.addComponent(VisualReplicator.java:391) at org.netbeans.modules.form.VisualReplicator.updateAddedComponents(VisualReplicator.java:357) at org.netbeans.modules.form.FormDesigner$FormListener.run(FormDesigner.java:2414) at org.netbeans.modules.form.FormLAF$3.run(FormLAF.java:329) at org.openide.util.Mutex.doEventAccess(Mutex.java:1361) at org.openide.util.Mutex.readAccess(Mutex.java:271) at org.netbeans.modules.form.FormLAF.executeWithLookAndFeel(FormLAF.java:312) at org.netbeans.modules.form.FormDesigner$FormListener.processEvents(FormDesigner.java:2339) at org.netbeans.modules.form.FormDesigner$FormListener.formChanged(FormDesigner.java:2305) at org.netbeans.modules.form.FormModel.fireEvents(FormModel.java:1296) at org.netbeans.modules.form.FormModel.fireEventBatch(FormModel.java:1269) at org.netbeans.modules.form.FormModel.firePendingEvents(FormModel.java:1232) at org.netbeans.modules.form.FormModel.access$000(FormModel.java:68) at org.netbeans.modules.form.FormModel$2.run(FormModel.java:1212) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:597) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:137) 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) Related FormLAF method: private static void useDesignerLookAndFeel(FormModel formModel) { if (!initialized) { try { initialize(); } catch (Exception ex) { Logger.getLogger(FormLAF.class.getName()).log(Level.INFO, ex.getMessage(), ex); } } UIDefaults defaults = UIManager.getDefaults(); netbeansDefaults.clear(); copyMultiUIDefaults(defaults, netbeansDefaults); netbeansDefaults.keySet().removeAll(userDefaults.keySet()); defaults.keySet().removeAll(netbeansDefaults.keySet()); if (!preview) { setUseDesignerDefaults(formModel); } else if (MetalLookAndFeel.class.isAssignableFrom(previewLaf)) { MetalLookAndFeel.setCurrentTheme(lafToTheme.get(previewLaf)); } } Thanks, Wolfgang
        Hide
        Karl Schaefer added a comment -

        Thanks for the update. I will make the simple change that you suggested, just for added safety.

        Show
        Karl Schaefer added a comment - Thanks for the update. I will make the simple change that you suggested, just for added safety.
        Hide
        kleopatra added a comment - - edited

        @Karl,

        I think returning null is not an option - the lookup has to succeed or fail. The problem of throwing is elsewhere (as Wolfgang verified) and must be solved elsewhere. So - a strong NO from me: never-ever bend contracts to make a single mis-behaved client (like Netbeans) happy.

        CU
        Jeanette

        Show
        kleopatra added a comment - - edited @Karl, I think returning null is not an option - the lookup has to succeed or fail. The problem of throwing is elsewhere (as Wolfgang verified) and must be solved elsewhere. So - a strong NO from me: never-ever bend contracts to make a single mis-behaved client (like Netbeans) happy. CU Jeanette
        Hide
        Karl Schaefer added a comment -

        It doesn't bend the contract, it corrects it. Core returns null when a UI delegate cannot be found and JComponent is designed to work without a UI delegate. I think it is better behavior to return null and not throw an exception. Core force a giant System.err message by printing a stacktrace, but never actually bubbles up the exception, just returns null.

        Show
        Karl Schaefer added a comment - It doesn't bend the contract, it corrects it. Core returns null when a UI delegate cannot be found and JComponent is designed to work without a UI delegate. I think it is better behavior to return null and not throw an exception. Core force a giant System.err message by printing a stacktrace, but never actually bubbles up the exception, just returns null.
        Hide
        wzberger added a comment -

        To improve safety a null check has to be done - for me returning null is the correct behavior. The raising exception is thrown in core UIDefaults.getUI(), that's also OK.

        Related NetBeans issue: http://netbeans.org/bugzilla/show_bug.cgi?id=197521

        Show
        wzberger added a comment - To improve safety a null check has to be done - for me returning null is the correct behavior. The raising exception is thrown in core UIDefaults.getUI(), that's also OK. Related NetBeans issue: http://netbeans.org/bugzilla/show_bug.cgi?id=197521
        Hide
        kleopatra added a comment -

        interestingly you are right: core makes a lot of noice on any error in the lookup process, but ultimately returns null So SwingX should do the same.

        Me-thought that sure the addons method was incorrect in not guarding against null, but on detecting a null I would have thrown an exception like the rest of the method does if something goes wrong after it found a not-null ui.

        Hmm ... shouldn't we go the whole way and do the same as core if something goes wrong in the rest of the method: make noise and return null?

        Cheers
        Jeanette

        Show
        kleopatra added a comment - interestingly you are right: core makes a lot of noice on any error in the lookup process, but ultimately returns null So SwingX should do the same. Me-thought that sure the addons method was incorrect in not guarding against null, but on detecting a null I would have thrown an exception like the rest of the method does if something goes wrong after it found a not-null ui. Hmm ... shouldn't we go the whole way and do the same as core if something goes wrong in the rest of the method: make noise and return null? Cheers Jeanette
        Hide
        Karl Schaefer added a comment -

        Updated the title to capture the real issue. We need to ensure that we return null when the look up fails (making noise as appropriate).

        Show
        Karl Schaefer added a comment - Updated the title to capture the real issue. We need to ensure that we return null when the look up fails (making noise as appropriate).
        Hide
        Karl Schaefer added a comment -

        SWINGX-1418: Stop throwing exceptions and return null UI delegate when we cannot coerce a valid delegate. Instead of exceptions print to System.err (as core does). Use the same messages as core except for the case that core does not have.

        swingx-core/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java

        Committed revision 4004.

        Show
        Karl Schaefer added a comment - SWINGX-1418 : Stop throwing exceptions and return null UI delegate when we cannot coerce a valid delegate. Instead of exceptions print to System.err (as core does). Use the same messages as core except for the case that core does not have. swingx-core/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java Committed revision 4004.

          People

          • Assignee:
            Karl Schaefer
            Reporter:
            wzberger
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Time Tracking

              Estimated:
              Original Estimate - 5 minutes
              5m
              Remaining:
              Remaining Estimate - 5 minutes
              5m
              Logged:
              Time Spent - Not Specified
              Not Specified