groovyswingbuilder

Handling exceptions in Groovy SwingBuilder.doOutside


(This is Groovy 1.8.9. The problem is fixed in Groovy 2.1.0-rc-1)

I am trying to handle exceptions that occur in a SwingBuilder.doOutside{}. I have used Thread.setDefaultUncaughtExceptionHandler(), but it doesn't seem to intercept the uncaught exceptions in the doOutside{}.

Here's a sample program that illustrates the problem. If I run this from the command line and click on the EDT Exception button, I see the printStackTrace() results on stderr. If I click Outside Exception, nothing shows. What am I doing wrong?

import groovy.swing.SwingBuilder

class ExceptionTest {
    static main(args) {
        Thread.setDefaultUncaughtExceptionHandler(
            { thread, exception ->
                System.err.println "thread ${thread.getName()}"
                exception.printStackTrace()
            } as Thread.UncaughtExceptionHandler)

        def swing = new SwingBuilder()

        def testEDTButton = swing.button('EDT exception')
        testEDTButton.actionPerformed = { throw new Exception("EDT exception") }

        def testOutsideButton = swing.button('Outside Exception')
        testOutsideButton.actionPerformed = { swing.doOutside { throw new Exception("Exception outside") } }

        def frame = swing.frame(title: 'Test exception reporting') {
            vbox {
                widget(testEDTButton)
                widget(testOutsideButton)
            }
        }
        frame.pack()
        frame.show()
    }
}

Solution

  • I looked at the SwingBuilder source for Groovy 1.8.9, and it turns out that doOutside uses ExecutorService.submit() if it is invoked from the EDT thread:

    private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
    ...
    DEFAULT_EXECUTOR_SERVICE.submit(c)
    

    The submit() returns a Future, which gets thrown away, as you can see above. A Future traps its exceptions and only throws an exception itself if you call Future.get(). But that never happens in doOutside().

    So I need put a try-catch inside the closure I pass to doOutside() to be able to notice that an exception happened there.

    NOTE: The Future was replaced with a simple Thread.start() in Groovy 2.1.0-rc-1 (see bug https://issues.apache.org/jira/browse/GROOVY-5074). That removes the exception trapping problem.