Question

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.

Was it helpful?

Solution

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.)

OTHER TIPS

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

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top