views:

52

answers:

4

I'm using an ActionListener to update a JList whenever an item is selected.

jComboBox.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox) e.getSource();
        updateLocalFileList( cb.getSelectedItem().toString() );
    }
});

It is calling this method for the UI.

public void updateLocalFileList( String path ){
    DefaultListModel model = new DefaultListModel();
    for (String str : LocalFileSystem.getFileListFromDirectory( path )) {
        model.addElement( str );
    }
    getJList().setModel(model);
}

If getFileListFromDirectory() gives a NullPointerException, say when the letter of an empty dvd drive is selected, it seems to prevent the ActionListener from working as intended.

I'm not sure what's happening exactly but I suspect that passing a null value to the model is causing this issue.

Any ideas?

Edit

Here is the stacktrace as requested. As you can see, the method is obviously triggering a NullPointerException on inacessible drives. I don't precisely why it prevents the JList updating though since the rest of the application works fine.

java.lang.NullPointerException
    at mine.View.updateLocalFileList(View.java:274)
    at mine.View$1.actionPerformed(View.java:262)
    at javax.swing.JComboBox.fireActionEvent(Unknown Source)
    at javax.swing.JComboBox.setSelectedItem(Unknown Source)
    at javax.swing.JComboBox.setSelectedIndex(Unknown Source)
    at javax.swing.plaf.basic.BasicComboPopup$Handler.mouseReleased(Unknown Source)
    at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at javax.swing.plaf.basic.BasicComboPopup$1.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
+1  A: 

The NullPointerException occurs on the EventDispatchThread (which is where all GUI update code normally occurs) so it interrupts the event listener itself. Hence your GUI doesn't get updated properly (anymore). To prevent that you must explicitly handle exceptions, or intercept the causes that would otherwise trigger them.

I have a class that extends `Thread.UncaughtExceptionHandler` and I'm setting it in entry using `Thread.setDefaultUncaughtExceptionHandler()`. I don't know if that is sufficient. All it does at the moment is output an Exception to text.
James P.
It is not: try to visualise: the exception thrown at getFileListFromDirectory() interrupts your event listener;control flow is transferred to your exception handler so your setModel() call is never reached, so your GUI isn't updated properly.
Thanks for the explanation. Do you know of any sites that have a good example of how to handle exceptions in a Swing application?
James P.
+2  A: 

I would do two things to make this more robust.

  1. Ensure that getFileListFromDirectory never returns null. If there are no items, return Collections.emptyList rather than null. If that is not possible, specifically check the return value before using the "for each" iteration. At it is, a null pointer will stop the model from being updated. (So clicking an empty drive will not clear the file list.)
  2. assign cb.getSelectedItem() to a local variable and check for null before invoking the updateLocalFileList. If cb.getSelectedItem() is null, you might choose to clear the file list.
mdma
Thanks for your suggestions. Adding `Collections.emptyList` to the method would make sense since it's supposed to send back a List and not a null reference. I'll also add some checking to the graphical side.
James P.
To make getSelectedItem() return null, select an item, then press and hold CTRL and select the item again.
Joseph Gordon
+1  A: 

Your listener (the one that throws the exception) is not the only one that listens on the specific event. Core Swing classes (especially the UI delegates) register their own listener so that they can properly update the UI. If your listener fails, there is no guarantee that all (or any) of the other listeners will be notified with this specific event.

Kirill
A: 

A couple of points:

What exactly is the LocalFileSystem class? Is that a custom class?

If it is expected in some cases that it will return null, then use a local variable for it and check if it is null before calling any method on it.

You can check if a given file or directory exists using the exists method on File. There is also an isDirectory method as well. I suggest you consider using it.

Avrom
`LocalFileSystem` is indeed a custom class. The method uses the `list` method of File to send back a List of files (this is for navigating through directories). It will return null for an empty cd/dvd or disquette drive.
James P.
Then my best advice is simply the following:Whenever a null *may* be expected in a variable, do a programmatic check for it before calling any method on it.
Avrom