Integrating JavaFX with regular Java – Part 2

In my previous post about integrating JavaFX with regular Java, I briefly showed you how to properly construct a JavaFX object. This post will elaborate on that principle and provide the JavaFX UI a way to communicate with the Java ‘backend’.

Before I continue, I will explain the design of the application in which I used the method described below. This will allow you to decide whether this is also the way to go for your application, or maybe you still want to try something else. Ok, ready? Here we go!

Application Design

The application we were building was going to receive a lot of data, perform analysis on that data, store it and visualize it. The data was received in chunks by means of an embedded web service, then transformed into a stream of events. The processing part of the application monitored this stream of events and given certain patterns it would generate derived events. Once an entire chunk (or stream) of data was processed, the central component would fire off another event signaling newly computed results in the database. This would be the point where the interface would be allowed to finally show and interact with the data.

Since pictures say more than a thousand words, here’s a diagram of the application:

Everything up to the user interface was built in regular Java (the white components). The events were distributed to components by Spring using the technique described in an earlier post of mine. When the time came to develop a user interface, we decided to give JavaFX a try (the blue component). Naturally, we wanted it to integrate neatly with the rest of the application. This meant two things:

  1. the JavaFX interface would somehow have to fit into Spring
  2. the JavaFX interface would have to respond to events from the application and communicate back to the rest of the application

Putting JavaFX in the Spring context

Constructing a JavaFX object outside of the JavaFX runtime’s threads proved to be problematic. Therefor it would also not be possible to just declare it as a bean in the Spring context. We effectively built a wrapper around the JavaFX class to ensure this correct behavior. This wrapper would not just create our GUI classes, it would also feed it with its dependencies. Any dependency the GUI had, would also be a dependency of the wrapper.

Let’s look at the code of this wrapper class, which we called Visualizer. Don’t get thrown off by the size of it just yet, I’ll explain everything in a jiffy…

public class Visualizer implements InitializingBean, EventListener {
  /** Datastore interface. */
  private DatastoreInterface datastoreInterface;

  /** Reference wrapper to user interface. */
  private final UiHolder uiHolder = new UiHolder();

  /** Lock and condition to wait for UI startup. */
  private final Lock uiLock = new ReentrantLock();
  private final Condition uiReady = uiLock.newCondition();

  private class MainHolder extends Function0<Void> {

    private project.javafx.Main ui;

    public Void invoke() {
      uiLock.lock();
      try {
        ui = new project.javafx.Main();
        ui.setDatastoreInterface(datastoreInterface);

        ui.start();

        // Signal UI is ready
        uiReady.signal();
      }
      finally {
        uiLock.unlock();
      }

      return null;
    }

    public Main getUserInterface() {
      return ui;
    }
  }

  public void afterPropertiesSet() throws Exception {
    // Perform GUI creation in FX runtime
    FX.deferAction(uiHolder);

    // Wait for GUI to complete construction
    uiLock.lock();
    try {
      uiReady.wait();
    }
    catch (InterruptedException ie) {
      // Construction was interrupted, stop what we're doing
      return;
    }
    finally {
      uiLock.unlock();
    }
  }

  public void setDatastoreInterface(DatastoreInterface di) {
    this.datastoreInterface = di;
  }

  public void onEvent(final Event e) {
    FX.deferAction(new Function0<Void>() {
      public Void invoke() {
        uiHolder.getUserInterface().postEvent(e);

        return null;
      }
    });
  }
}

Well there you have it. The wrapper class we can put into our Spring context and which will initialize the JavaFX GUI for us. As you can see, it ensures the construction of the JavaFX object happens in the JavaFX runtime by using FX.deferAction. It has gotten a little more complicated since my last post, but for a good reason.

The lock and the condition will prevent Spring from continuing with the construction of the application context while the JavaFX classes are being created. In this application, this was necessary to ensure no events get fired before everything was up and running. Spring cannot detect whether JavaFX has finished instantiating the Main object, so we give it a hand by delaying our own initialization.

Communicating with the GUI

Now that the wrapper class is ready to be placed within the Spring context, we need to enable two way communication between the normal Java parts of the system and the JavaFX GUI.┬áIf you’ve paid attention to the code sample, you will probably have noticed that I am passing something called a DatastoreInterface to my GUI class. This interface defines all actions which require interaction with the rest of the system. You are completely free to make this interface to your own application’s needs.

Within JavaFX, it makes perfect sense to move data around in order to better visualise it. As soon as you want to persist changes, or refresh data, the GUI will call a method in this interface. The nice thing is that this dependency is still managed by Spring, as it gets injected into the Visualizer, which passes it on to the JavaFX class.

That’s one way traffic. The other way is responding to events. In order to facilitate this, the Visualizer itself responds to the events for the user interface. It goes a little beyond simply dispatching them to the JavaFX class. Here comes the threading problem again. Since all updates on the GUI have to happen in the JavaFX runtime and pretty much any change in data will have some effect on the GUI, these data changes must also be performed in the Java FX runtime. That is why the Visualizer wraps each incoming event and defers it to the JavaFX runtime for execution. Make sure you do this for every action that might change data the JavaFX interface binds to!

Well that’s pretty much it. We have created a clean way to integrate the JavaFX GUI with the rest of our code. For our application this approach proved very successful. Mostly because there is a clean cut between actions and code that merely determine how to display data and those that perform business mutations.

I hope this will help you to set up a nice JavaFX frontend for your applications. If you have any questions, feel free to leave me a comment! Cheers!

–JH

Posted in JavaFX.