3

Somewhere in my code a call to paint is being called but the stacktrace gives absolutely no idea as to what and where. It's also intermittent so I know it's a threading issue, and if I put a breakpoint then the code never fails. So how do I tell where or how the event thread is called to repaint the component?

Here is the stacktrace:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 4
    at javax.swing.plaf.basic.BasicTabbedPaneUI.paintTabArea(BasicTabbedPaneUI.java:834)
    at javax.swing.plaf.basic.BasicTabbedPaneUI.paint(BasicTabbedPaneUI.java:797)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:778)
    at javax.swing.JComponent.paint(JComponent.java:1054)
    at javax.swing.JComponent.paintChildren(JComponent.java:887)
    at javax.swing.JComponent.paint(JComponent.java:1063)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5219)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1529)
    at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1452)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1249)
    at javax.swing.JComponent._paintImmediately(JComponent.java:5167)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4978)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:808)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:718)
    at javax.swing.RepaintManager.access$1100(RepaintManager.java:62)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1677)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:694)
    at java.awt.EventQueue$3.run(EventQueue.java:692)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
Stephane Grenier
  • 15,527
  • 38
  • 117
  • 192
  • 2
    Essentially, you can't. Paints are scheduled onto the event queue and are processed at some time in the future by the event dispatching thread. You need to try and figure out who is changing the state of the UI incorrectly and ensure that these are made from within the context of the EDT – MadProgrammer Jan 25 '14 at 22:15
  • Is there any way to tell what component was painted? – Stephane Grenier Jan 25 '14 at 22:42
  • The instance, not really, but I can tell you it was a JTabbedPane – MadProgrammer Jan 25 '14 at 22:43
  • 1
    Have you looked through your code to make sure that all Swing code is called on the EDT? All except `repaint()` requests, that is. That's where I'd put in my time and effort if it were me. – Hovercraft Full Of Eels Jan 25 '14 at 22:53
  • Try one of the approaches cited [here](http://stackoverflow.com/q/7787998/230513) to look for EDT violations. – trashgod Jan 25 '14 at 23:39
  • 2
    True, repaint() calls are posting a PaintEvent in the EDT so the stack traces are "tricked" and they cannot show the actual root of the call. But I think you are looking at the wrong side of the problem. The "offender" is not the code that calls repaint() but the one that changes the tabbed pane. And since you add a breakpoint (where exactly?) you are introducing delay to the execution of the program and you don't get the error so I suspect a racing condition. Spot the thread that updates JTabbedPane and you will find your suspect. – Stelios Adamantidis Jan 26 '14 at 00:47
  • Exactly. And if that's the case then how do you determine which JTabbedPane if there are a lot of them. Spotting the thread would be even more of a challenge... – Stephane Grenier Jan 26 '14 at 04:19
  • 2
    @Stephane Grenier I don't know how complex your code might be and how many `JTabbedPane`s and `Thread`s are there, but if you have more than one threads updating more than one tabbed panes then you **should** change **all** of them. As others have said it's not safe to update the UI outside the EDT. So spot all these places an then you have 2 options: Either use an `InvokeLater()` call to update the UI, or remove the `Thread` and use a `SwingWorker` instead. – Stelios Adamantidis Jan 26 '14 at 11:49
  • @SteliosAdamantidis That's actually what I've done some time ago, but I fear there might still be some errant thread, etc. that's causing the issue. And trying to find that is beyond brutal. Is there any way to set an ID for each component so that I can at least identify it and limit the amount of code I have to look through? – Stephane Grenier Jan 26 '14 at 16:18
  • @StephaneGrenier No I'm afraid that you will have to undertake the brutality. The trace is not verbose enough to tell you which is the broken Component. Your best bet is to replace the UI delegate of the tabbed pane: according to the LAF you are using subclass the appropriate class (eg MetalTabbedPaneUI if you use metal) override paintTabArea(), call super and surround it with try catch. In catch re-throw the exception but include the Component's name. Problem is that you need to find **all** tabbed panes and call setUi(new MyTabbedPaneUi()) and setName("foo"). I can't think of sth better. – Stelios Adamantidis Jan 26 '14 at 18:38

1 Answers1

2

You're maybe performing actions on your GUI outside the EDT. In every piece of code you suspect, you may want to use SwingUtilities.isEventDispatchThread() to test if you are currently in EDT. If this method returns false then you should not performing any UI action.

Here is an example of how to use it for debugging:

public static void checkInEventDispatchThread(String pMethod) {
    if (!SwingUtilities.isEventDispatchThread()) {
        System.err.println("** NOT in EDT: " + pMethod + "() in " + 
                           Thread.currentThread().getName());
    }
}

And for every piece of code you suspect:

public void myMethod() {
    UtilClass.checkInEventDispatchThread("myMethod");
}

Of course, it's only for debugging purpose, you shall not keep such calls for production/releases.

xav
  • 5,452
  • 7
  • 48
  • 57