Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SecondaryLoop in JavaFX, like Swing?

Tags:

java

swing

javafx

I have a Java Swing app which I'm investigating if it is even possible to port to JavaFX. The app is a development environment and simulator for an internally used scripting language. The interesting thing about it is you can set breakpoints for this scripting language and step through it, like any programmer would expect for a language.

Now because the language in the simulator is interpreted, deep within the execution of the interpreter, when it hits a breakpoint, it can pop back to the gui with a Java Swing SecondaryLoop class. So when the breakpoint is hit, it calls secondaryLoop.enter(). The gui is then active for the user to inspect variables and gui components are active. When the user hits "Continue" in the program, it calls secondaryLoop.exit() to continue execution of the interpreter. It wouldn't really be feasible for the interpreter to unwind it's entire state to go back to the primary loop, and then take up where it left off at exactly the same point. That's why the SecondaryLoop is invaluable in making it work.

Is this possible in JavaFX?

like image 883
xpusostomos Avatar asked Nov 01 '22 02:11

xpusostomos


1 Answers

Yes, it's possible. You need to use the enterNestedEventLoop and exitNestedEventLoop methods (they are inside the com.sun.javafx.tk.Toolkit class). See this usage example:

// Make sure to import the FX Toolkit first
import com.sun.javafx.tk.Toolkit;

// This object will be used as a unique identifier to the nested loop (to
// block the execution of the thread until exitNestedEventLoop is called)
final Object loopLock = new Object();

// Simulate a long process thread (DB call, download, etc)
Thread longProcess = new Thread(new Runnable()
{
    @Override
    public void run()
    {
        // Sleep for 12 seconds to simulate a long process
        try
        {
            Thread.sleep(12000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        // Setup a result to pass back to the enterNestedLoop() caller
        String result = "Result of this long process";

        // We are now done. Call exitNestedEventLoop() to unblock 
        // the enterNestedLoop() caller. This needs to run from
        // the FX Thread so use Platform.runLater()
        Runnable fxRunner = new Runnable()
        {
            public void run()
            {
                try
                {
                    Toolkit.getToolkit().exitNestedEventLoop(loopLock,
                            result);
                } catch (Throwable t)
                {
                    t.printStackTrace();
                }
            }
        };
        Platform.runLater(fxRunner);
    }
});

// Start that long process from the FX Thread
longProcess.start();
// The next call will block until exitNestedEventLoop is called, however 
// the FX Thread will continue processing UI requests
Object result = Toolkit.getToolkit().enterNestedEventLoop(loopLock);
// Next statement will print: "Result of this long process"
System.out.println("Result is: " + result);

Now, before you use this be warned of two important things:

  1. The com.sun.javafx.tk.Toolkit class is not part of the public API, so Oracle reserves the right of removing it without notice. I've been using it just fine from Java 7 to 8u51 so they could stay there forever, change package/names or disappear completely (unlikely).

  2. Nested loops (and Swing's secondary loops) are great for flexibility and small applications but overusing them often comes with a price. Nesting to many loops (huge stack trace) will often cause "strange" behaviour in your applications since initial parts of your code might end up waiting four or five things ahead completely unrelated to them. I've seen FX nested loops causing "empty" exceptions in FX WebEngine executeScript() calls and duplicating keyboard preprocessing (when pairing FX+Swing) among other problems.

That said I would recommend using the javafx.concurrent.Task instead (if it makes sense). Using the Task class will require a bit more effort but I think it's the correct way of doing things and will probably save you lots of maintenance time.

For extra reference about the FX Task class see this great article: http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm

UPDATE: enterNestedEventLoop and exitNestedEventLoop will be part of the Java 9 public API (Platform class), more info in JDK-8090865

Hope this helps!

like image 165
JavierJ Avatar answered Nov 15 '22 04:11

JavierJ