Integrating a JavaFX UI with regular Java – Part 1

When JavaFX was first released, I didn’t give it much attention, as it felt like yet another visualization platform. With the latest release of JavaFX 1.3 and a project requiring a ‘fancy’ user interface, it was time to give it a try. The surprise came quickly: after downloading NetBeans and the JavaFX runtime, I was able to quickly build user interfaces that actually look cool.

After some tutorials and many bogus screens with crazy animations, it was time to put JavaFX to work on a real project. We had an application that receives a lot of data, processes this and stores it in a database. The data reception and processing were already fully developed in ‘regular’ Java using the Spring framework. The goal was to somehow try and integrate this existing code with a fancy JavaFX interface to visualize the data.

Starting the JavaFX UI

We set out by trying to wrap a JavaFX object in a Swing container. This was troublesome and required a huge load of boilerplate code. There had to be an easier way. Next up was the JFXtras project, which supplies a wrapper class for JavaFX Scenes. This certainly works, but still felt unnatural.

Eventually we thought about calling JavaFX and letting it do all the work on its own. We defined a start() function in our JavaFX Main class: (Note: don’t use this example, it’s broken!)

public class Main {
  public function start(): Void {
    var design = Main {};

    javafx.stage.Stage {
      title: "Main Application"
      scene: design.getDesignScene()
    }
  }
}

This function could then be called from a Visualizer bean to initialize the interface:

public class Visualizer implements InitializingBean {
  public void afterPropertiesSet() {
    Main gui = new Main();
    gui.start();
  }
}

This was a good start, but it resulted in the application randomly showing or not showing the interface. The fact that this behavior was random points to a Swing threading issue. To fix this, it is not enough to move the calling of the start() function to the Swing thread. The problem lies within the construction of JavaFX objects.

When a JavaFX object is created, the scene graph will also be constructed and any bindings between UI and data will be invalidated. This causes rendering code to be triggered, which should also run in the Swing (or JavaFX) thread. So the construction of the object should also be moved to the event thread. To do this, we need a function which we can defer in the JavaFX runtime.

public void afterPropertiesSet() {
  FX.deferAction(new Function0() {
    public Void invoke() {
      Main gui = new Main();
      gui.start();

      return null;
    }
  });
}

This will correctly and reliably create your JavaFX interface. No more threading issues and all straight from your ‘regular’ Java code.

The above example merely instantiates your JavaFX gui from a Java application. It does nothing yet to provide the GUI with data, or to receive certain commands from the GUI. We also have a working solution for this, but it requires some cleanup. In my next post I will show a way to setup clean communication between your GUI and the processing part of the application.

–Jan-Hendrik (JH)

Originally posted at JCN Java Blog

Posted in JavaFX.