Swing verify code on Event Dispatch Thread at runtime
Asked Answered
R

4

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.

Reviel answered 10/6, 2010 at 13:48 Comment(0)
H
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.)

Hanoi answered 10/6, 2010 at 14:2 Comment(1)
Thanks. I had seen FEST but saw it only in a unit testing context. Guess I could use it outside.Reviel
A
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.

Allare answered 10/6, 2010 at 14:1 Comment(2)
What's your alternative for long running tasks instead of SwingWorker?Reviel
Sent tasks to another thread [pool], which sends tasks back (directly or indirectly) through invokeLater.Allare
N
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

Novah answered 14/9, 2012 at 9:17 Comment(1)
One step was only described in a linked blog (that's only available by digging around in archive.org: here it is. To use your custom repaint manager, you need this static call: RepaintManager.setCurrentManager(new MyRepaintManager());Banshee
N
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.

Neophyte answered 10/6, 2010 at 13:53 Comment(5)
"instrument code" indicates that he wants to do this compile time, not runtime.Debbidebbie
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.Allare
@Debbidebbie 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.Neophyte
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.Reviel
Use the Substance LAF as mentioned. It'll throw up exceptions. I've used this tactic in the past. pushing-pixels.org/?p=368Neophyte

© 2022 - 2024 — McMap. All rights reserved.