FX Experience Has Gone Read-Only

I've been maintaining FX Experience for a really long time now, and I love hearing from people who enjoy my weekly links roundup. One thing I've noticed recently is that maintaining two sites (FX Experience and JonathanGiles.net) takes more time than ideal, and splits the audience up. Therefore, FX Experience will become read-only for new blog posts, but weekly posts will continue to be published on JonathanGiles.net. If you follow @FXExperience on Twitter, I suggest you also follow @JonathanGiles. This is not the end - just a consolidation of my online presence to make my life a little easier!

tl;dr: Follow me on Twitter and check for the latest news on JonathanGiles.net.

There has been a very interesting thread in the JavaFX OTN forums about FXML and dependency injection. I went and downloaded Google Guice and wrote a small sample to try it all out. Things are not perfect quite yet (I still need full Unified Expression Language support added to FXML), but so very, very, very close!

What I ultimately hope to accomplish with FXML and CSS in JavaFX enterprise applications is that we (a) fully separate the visuals from the “controller”, (b) are left with a very simple “controller” that is highly testable, and (c) everything is highly modular.

CSS allows the designer to come in after the fact and modify the look of a UI without the developer’s help, and without a full build cycle. FXML allows a developer or designer to alter the structure of the view without a full build cycle. So (a) should be well in hand, especially if the controller itself can be injected rather than declared directly in the FXML file as it is today. (I’m OK with, even prefer, saying “I would like to have a FooController for this FXML file, if you please”, but what would really rock is if the FooController were an interface or abstract class rather than always being a concrete class, such that a DI framework like Guice could find the appropriate implementation and supply it).

For (b) to work, I want to get to the point where I don’t use the @FXML annotation at all. Right now we have to use it for binding things up, but when RT-14880 is fixed and we have some implicit reference to the controller, then I’ll be able to have very powerful bindings defined in the FXML document back to state located on the Controller, and I won’t need to handle it manually in java code anymore. This is going to be a big win.

A good dependency injection system like Guice makes (c) dead easy. I like Guice because it is all still very type safe and there is only a minimal amount of magic — in fact I found it very easy to understand. I have a very contrived example here which shows the bare-bones basics, and some simplified use case. Because RT-14880 hasn’t been implemented, I can’t use the really slick binding the way I’d like to, so instead I am using some scripting inside the FXML document to produce the text for the button. This sample revolves around a very simple POJO called Person:

Person.java

package fxmlapp;

public class Person {
    private String firstName = "Richard";
    public final String getFirstName() { return firstName; }
}

SampleController.java

package fxmlapp;
 
import javax.inject.Inject;
 
public class SampleController {
    /**
     * This domain data model is supplied in the constructor
     */
    private final Person person;

    /**
     * Notice this constructor is using JSR 330 Inject annotation,
     * which makes it "Guice Friendly".
     * @param person 
     */
    @Inject public SampleController(Person person) {
        this.person = person;
    }

    /**
     * Used within the FXML document. This is just some state which the
     * controller wants to expose for the view to bind to or use.
     * @return 
     */
    public final String getPersonName() {
        return person.getFirstName();
    }

    /**
     * Some method which I will call from the FXML file.
     */
    public void print() {
        System.out.println("Well done, " + getPersonName() + "!");
    }
}

First is the controller. SampleController is exceedingly simple. It has some model data which is supplied in the constructor, in this case, a Person object. The person is just a good old fashioned POJO in this case, but could be a more complicated object, or perhaps created by JAX-RS or it could be a JDBC ResultSet — anything you like. There is a method “getPersonName” which I added for the sake of the FXML file. Also there is a method called “print” which will print a message. You could use any method here you want. For example you might have a “submit” method which posts back to a server or a “refresh” method which fetches data.

Next up, the FXML file.

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?language javascript?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
 
<StackPane fx:id="root" xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button fx:id="printBtn" onAction="controller.print()" />
        <fx:script>printBtn.text = 'Click Me ' + controller.getPersonName() + '!';</fx:script>
    </children>
</StackPane>

I create a StackPane who’s id is going to be “root”, and give it a single Button as a child. The button has also been given an id, which is important because that is the name by which I can refer to this button in the embedded JavaScript code. The button has an onAction handler which will invoke the print() method on the controller. Notice that this onAction handler is just some JavaScript.

Speaking of which, you will notice there is a processing instruction here which tells FXML that the scripting in this document will be JavaScript. Because I don’t yet have the ability to do complex binding expressions (and in this case I want to do a string concatenation for the Button text), I use an embedded script to get the person name from the controller and concatenate it with some text. And that’s it, very, very simple.

Now I have a Controller (SampleController) and an FXML file (Sample.fxml), but where is the code which associates the one with the other? This code lives in my SampleApp.

SampleApp.java

package fxmlapp;

import com.google.inject.Guice;
import com.google.inject.Injector;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 * A Guice based sample FXML Application
 */
public class SampleApp extends Application {
    /**
     * Create the Guice Injector, which is going to be used to supply
     * the Person model.
     */
    private final Injector injector = Guice.createInjector(new Module());
    
    @Override public void start(Stage stage) throws Exception {
        // Create a new Guice-based FXML Loader
        GuiceFXMLLoader loader = new GuiceFXMLLoader(injector);
        // Ask to load the Sample.fxml file, injecting an instance of a SampleController
        Parent root = (Parent) loader.load("Sample.fxml", SampleController.class);
        
        // Finish constructing the scene
        final Scene scene = new Scene(root, 320, 240);
        // Load up the CSS stylesheet
        scene.getStylesheets().add(getClass().getResource("fxmlapp.css").toString());
        // Show the window
        stage.setScene(scene);
        stage.setTitle("Guiced");
        stage.show();
    }

    // Standard launcher
    public static void main(String[] args) {
        launch(args);
    }
}

The first thing I do when the SampleApp is created is I create an instance of a Guice Injector. I need to feed it a “Module”, which looks like this:

Module.java

package fxmlapp;

import com.google.inject.AbstractModule;

/**
 *
 * @author Richard
 */
public class Module extends AbstractModule {
    @Override
    protected void configure() {
        bind(Person.class);
    }
}

The Module, essentially, just tells Guice “hey, if somebody comes looking for a Person class, give it to them”. You can read more about it on the Guice website. But once I’ve created my Injector, it is time to use it.

In the “start” method of the SampleApp, I create a new GuiceFXMLLoader. This is another utility class of my creation which makes it easier to use Guice with FXML loading. I’ll show that class in a minute. With my newly created GuiceFXMLLoader, I’m going to call its “load” method, passing in the FXML file to load along with the type of Controller I’d like to create. The GuiceFXMLLoader then parses the FXML document. Guice then does its magic. It attempts to construct an instance of SampleController, but discovers that the @Inject annotated constructor requires a Person object. It then looks up in the Module, discovers a rule regarding Person objects, and goes off to create a Person object. Once it has the new Person, it creates a new SampleController, which then gets associated with the FXML document, and the root object is returned. I place this in a Scene and show it in the Stage.

Now in the screenshot above, you no doubt noticed things had also been styled via CSS. I didn’t have to do anything in the FXML document, or in the Controller, to style this application. I simply wrote a style sheet and loaded it in the SampleApp. Since my FXML document already had id’s and style classes in place, I didn’t have to do anything special.

fxmlapp.css

.root {
    -fx-background-color: linear-gradient(from 0% 0% to 0% 100%, #cbd0d7 0%, white 100%);
}

#printBtn {
    -fx-text-fill: #e4f3fc;
    -fx-font: 20pt "Tahoma Bold";
    -fx-padding: 10;
    -fx-base: #2d4b8e
}

#printBtn:hover{
    -fx-base: #395bae;
}

I hope you enjoyed this post and maybe it has stimulated your interest to try out dependency injection via Guice (or Spring, though I haven’t tried it in a client context yet) with FXML and JavaFX. In the 2.1 release coming up in the first half of 2012 we’ll fix the above mentioned issues in FXML such that we’ll have more powerful binding and an implicit reference to the controller from FXML, such that you won’t need to use JavaScript in the FXML document to get this behavior. We’ll also have simplified the FXML somewhat such that the “” element isn’t required. All in all, I think there is a lot to be excited about with JavaFX and FXML and Dependency Injection. One of my thought-projects for the next few days will be how we can ensure that this sort of development fits perfectly into the Scene Builder RAD tool experience.

In my parting shot, here is the GuiceFXMLLoader. It is just a hacked up proof of concept, but you’ll need to to try these classes out on your own :-).

GuiceFXMLLoader

package fxmlapp;

import com.google.inject.Injector;
import java.io.InputStream;
import java.net.URL;
import javafx.fxml.FXMLLoader;
import javax.inject.Inject;

/**
 * Uses Guice to inject model state. Basically you create an instance of
 * GuiceFXMLLoader supplying an Injector, and then call load. The load
 * method takes the FXML file to load, and the controller to create and
 * associate with the FXML file.
 */
public class GuiceFXMLLoader {
    private final Injector injector;
    
    @Inject public GuiceFXMLLoader(Injector injector) {
        this.injector = injector;
    }
    
    // Load some FXML file, using the supplied Controller, and return the
    // instance of the initialized controller...?
    public Object load(String url, Class<?> controller) {
        Object instance = injector.getInstance(controller);
        FXMLLoader loader = new FXMLLoader();
        loader.getNamespace().put("controller", instance);
        InputStream in = null;
        try {
            try {
                URL u = new URL(url);
                in = u.openStream();
            } catch (Exception e) {
                in = controller.getResourceAsStream(url);
            }
            return loader.load(in);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) try { in.close(); } catch (Exception ee) { }
        }
        return null;
    }
}