10

Are there any libraries that instrument code to verify that methods called on Swing components are called on the Event Dispatch Thread? It probably wouldn't be too difficult to write some basic code for doing this, but I'm sure there are edge cases and whatnot that other people have handled. I'm looking for this at runtime though, not for unit tests.

Lii
  • 11,553
  • 8
  • 64
  • 88
Jeff Storey
  • 56,312
  • 72
  • 233
  • 406

4 Answers4

11

The FEST framework has a tool to detect Swing usage off the EDT. It's basically a RepaintManager that you install. The framework is oriented towards testing, but the RepaintManager can be used at deployment time.

Alternatively, to check all methods such as getters and setters for access only on the EDT, you can use AspectJ and load-time weaving to add the SwingUtilities.isDisaptchThread() advice to each method on your swing components (and the JDK Swing components.)

@Aspect
public class EDTCheck {

    @Pointcut("call (* javax.swing..*+.*(..)) || " +
              "call (javax.swing..*+.new(..))")
    public void swingMethods() {}

    @Pointcut("call (* com.mystuff.swing..*+.*(..)) || " +
              "call (com.mystuff.swing..*+.new(..))")
    public void mySwingMethods() {}


    @Pointcut("call (* javax.swing..*+.add*Listener(..)) || " +
              "call (* javax.swing..*+.remove*Listener(..)) || " +
              "call (void javax.swing.JComponent+.setText(java.lang.String))")
    public void safeMethods() {}

    @Before("(swingMethods() || mySwingMethods()) && !safeMethods()")
    public void checkCallingThread(JoinPoint.StaticPart thisJoinPointStatic) {
        if(!SwingUtilities.isDispatchThread()) {
            System.out.println(
                    "Swing single thread rule violation: " 
                    + thisJoinPointStatic);
            Thread.dumpStack();
            // or you might throw an unchecked exception
        }
    }

}

(Slightly modified from the article - added mySwingMethods pointcut, and use SwingUtiliites.isDispatchThread(). In practice it is the same as EventQueue.isDispatchThread() but the abstraction is cleaner.)

Gili
  • 86,244
  • 97
  • 390
  • 689
mdma
  • 56,943
  • 12
  • 94
  • 128
2

I came across a simple and clever (albeit not entirely bullet proof) method by Scott Delap at http://today.java.net/pub/a/today/2005/04/19/desktoplive.html.

As hinted at in the FEST answer by mdma above a custom RepaintManger can be used, simply override the methods

addInvalidComponent(JComponent component) 

and

addDirtyRegion(JComponent component, int x, int y, int w, int h)

and use

SwingUtilities.isEventDispatchThread()

to check if we're on AWT, you can integrate this into JUnit fairly easily by adding a

org.JUnit.Assert.fail("AWT Thread Violation")

in your checking routine.

The logic here is that most (but not all) Swing operations cause a repaint which eventually calls into these methods in RepaintManager so we can catch these and do our checking.

Alexander Potochkin has a good round up of various methods here: http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html

dassimon
  • 141
  • 8
  • One step was only described in a linked blog (that's only available by digging around in archive.org: [here it is.](https://web.archive.org/web/20070315213731/http://www.clientjava.com/blog/2004/08/20/1093059428000.html) To *use* your custom repaint manager, you need this static call: `RepaintManager.setCurrentManager(new MyRepaintManager());` – Joshua Goldberg Sep 03 '14 at 16:02
2

From a coding point of view, clearly separate EDT code from non-EDT code. Avoid SwingWorker like the plague.

To assert that you are on the EDT (insert ! for off), add the line:

assert java.awt.EventQueue.isDispatchThread();

You will need -ea/-enableassertions for this to do anything. However, it mostly works as an executable comment.

Robin
  • 36,233
  • 5
  • 47
  • 99
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

All you need is the following code:

void method() {
    if (!SwingUtilities.isEventDispatchThread()) {
       SwingUtilities.invokeAndWait(new Runnable() { public void run() { method(); } );
       return;
    }

    // do method code here, guaranteed to be in EDT
}

Also, using Substance as your LAF will help you to work out where Swing-related stuff isn't being executed on the EDT. It has checks in place to make sure that any Swing stuff is done on the proper thread, or it fails with an exception.

Chris Dennett
  • 22,412
  • 8
  • 58
  • 84
  • "instrument code" indicates that he wants to do this compile time, not runtime. – aioobe Jun 10 '10 at 13:57
  • 4
    Ergh. Be clear whether you are on the EDT or not. Code should not check and then do an `invokeLater`. Certainly avoid `invokeAndWait` if you don't like deadlocks. `assert java.awt.EventQueue.isDispatchThread();` - done. – Tom Hawtin - tackline Jun 10 '10 at 13:57
  • @aioobe dunno, he says 'instrument', and then goes on to say that he wants it at runtime. I suppose he's looking for annotations which decorate the code to check the thread safeness. Ardor3D does something similar with the @mainthread annotation. – Chris Dennett Jun 10 '10 at 14:01
  • I'm basically trying to test my existing app. There have been a few developers on it and I just want to run it and make sure the Swing stuff is happening on the EDT without manually plowing through all the code. I don't care if it's with aspects or annotations ,just seeing if something existed already. – Jeff Storey Jun 10 '10 at 16:09
  • Use the Substance LAF as mentioned. It'll throw up exceptions. I've used this tactic in the past. http://www.pushing-pixels.org/?p=368 – Chris Dennett Jun 10 '10 at 16:55