1

I have an application with several components. One of them is a JTabbedPane with other stuff in it (obviously). I access this pane only via this getter:

public JTabbedPane getPlotTabbedPane() {
    if (plotTabs == null) {
        plotTabs = new JTabbedPane();
        plotTabs.setFocusable(false);

        plotTabs.add("Measurement", getPlotPanel());
        plotTabs.add("Time", getPlotPanel().getTimePanel());
        plotTabs.add("Data", getPlotPanel().getDataPanel());
        plotTabs.add("Statistics", getPlotPanel().getStatisticsPanel());
        plotTabs.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                getPlotPanel().refreshRenderer();
            }
        });
    }
    return plotTabs;
}

So the first time, I access it (i.e. when it is not yet created), I create it, all the other times, I just give back the already created JTabbedPane. I was already told that this is not the best technique (if somebody can tell me a better one that does not mess up the layout composition code, I am very open to suggestions).

Now, this works perfectly fine in - let's say - 99% of the cases (application starts). In 1% I get a

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: -1
    at java.util.Vector.elementAt(Vector.java:430)
    at javax.swing.JTabbedPane.getTitleAt(JTabbedPane.java:1091)
    at javax.swing.plaf.basic.BasicTabbedPaneUI$Handler.updateHtmlViews(BasicTabbedPaneUI.java:3564)
    at javax.swing.plaf.basic.BasicTabbedPaneUI$Handler.componentAdded(BasicTabbedPaneUI.java:3699)
    at java.awt.Container.processContainerEvent(Container.java:2065)
    at java.awt.Container.processEvent(Container.java:2036)
    at java.awt.Component.dispatchEventImpl(Component.java:4653)
    at java.awt.Container.dispatchEventImpl(Container.java:2097)
    at java.awt.Component.dispatchEvent(Component.java:4481)
    at java.awt.Container.addImpl(Container.java:1083)
    at javax.swing.JTabbedPane.insertTab(JTabbedPane.java:703)
    at javax.swing.JTabbedPane.addTab(JTabbedPane.java:777)
    at javax.swing.JTabbedPane.add(JTabbedPane.java:814)
    at companyname.product.gui.Tab.getPlotTabbedPane(Tab.java:386)
    at companyname.product.gui.Tab.getFullPlotPanel(Tab.java:374)
    at companyname.product.gui.Tab.initComponents(Tab.java:262)
    at companyname.product.gui.Tab.<init>(Tab.java:80)

I won't go further down the stack, but this is basically the creation of the GUI. Tab is a extended JPanel. I can not really figure out, what the problem is nor what I can do to prevent it. Obviously, I could just wrap line 386 of Tab.java in a try/catch, but that doesn't solve the problem, it just solves the symptoms...

So, do you know what's the problem, how I can solve it or how I should do that stuff?

brimborium
  • 9,362
  • 9
  • 48
  • 76
  • Are you sure there's no `Caused by: ...` further down the stack trace? – Aleks G Jul 20 '12 at 12:40
  • And what is line 80 in Tab.java? – Aleks G Jul 20 '12 at 12:40
  • you might want to post an [SSCCE](http://sscce.org) for better help sooner. – David Kroukamp Jul 20 '12 at 12:43
  • Are you only manipulating your Swing components from the Event Dispatch Thread? – Jeffrey Jul 20 '12 at 12:47
  • @AleksG Hm... I will see next time it occurs, but I am pretty sure, there was no `Caused by:` because I would have noticed that. Line 80 in Tab.java is the `super();` call in the constructor (which calls `JPanel#JPanel()`). – brimborium Jul 20 '12 at 14:53
  • @DavidKroukamp I thought about that, but I think the problem is, that there is a lot going on and it is not really possible to post a SSCCE (especially not the first S...). The given method (`getPlotTabbedPane()`) is called a lot at startup. But it should only initialize it the first time. – brimborium Jul 20 '12 at 14:56
  • @Jeffrey manipulating yes, accessing no. I read out some stuff from other Threads. But changes are redirected over the EDT using `SwingUtilities.invokeLater()`. – brimborium Jul 20 '12 at 14:57
  • @brimborium fair enough but it doesnt make sense look ath the getPlotTabbedPane method, you pass no arguements to the method yet you return a variable so if you're modifying a public variable in the method there's no need to return it, use that instance of the variable again, from thus i can suggest making your `plotTabs` variable `private` thus restricting classes unintentionally modifying the variable. – David Kroukamp Jul 20 '12 at 15:50

1 Answers1

3

The stacktrace seems to suggest you access Swing components from another thread then the Event Dispatch Thread (EDT). This is not allowed.

I am pretty certain if you would make sure you only access Swing components from the correct thread you won't see this exception anymore.

More information can be found in the Concurrency in Swing tutorial. You can also take a look at this article which contains a RepaintManager which can help you to detect Swing threading violations

Robin
  • 36,233
  • 5
  • 47
  • 99
  • What if I need to access it from another Thread? `SwingUtilities.invokeNowAndWait()`? That will blow up my code extensively. How is that stuff usually done? Also: Thanks for the link with the `RepaintManager`, that is quite helpful. – brimborium Jul 20 '12 at 15:03
  • That is a possibility. But normally you simply won't need multi-thread access to the UI components – Robin Jul 20 '12 at 15:04
  • I am... I am doing time consuming measurement, which also provide realtime feedback to the user over the gui. I am obviously not doing the measurement in the EDT, but have to somehow change the GUI. At the same time I have to react to user interaction (which also changes a lot in the interface depending on the interaction). – brimborium Jul 20 '12 at 15:11
  • 2
    Then you should indeed use the methods in `SwingUtilities`, or consider using a `SwingWorker` which allows to do calculations in the background and update the UI on the correct at the end + in between – Robin Jul 20 '12 at 15:38
  • 1
    @brimborium the golden rule of Swing, Never, and we mean Never, try and access any UI component from any thread other then the ETD. You run to much of risk of corrupting the UI (as you seem to have done here). All time consuming processing should be done in separate threads and as robin has suggest, you should sync the UI with SwingUtilites.invoke... methods, or take advantage of the SwingWorker class. Check the tutorial that robin suggest to you. That's just the way it is, sorry – MadProgrammer Jul 20 '12 at 21:38
  • @MadProgrammer Nothing to apologize, that answers my question perfectly. Thanks a lot, I will look into the tutorial. Also thanks to Robin, that helped a lot. :) – brimborium Jul 24 '12 at 06:08