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.

While on vacation I’ve been watching twitter to see what kinds of things people are doing with JavaFX. One thing I’ve seen a fair amount of tweets on that is pretty gratifying is tying JavaFX to backend data services. One project is a port of Cairngorm from Flex to Java/JavaFX. Another was an earlier demo based on Hessian (which needs to be ported to JavaFX 1.2). Another was about integrating Flamingo to JavaFX.

It’s great to see a lot of people starting to play with this part of the JavaFX space because we haven’t really given much guidance to it yet. We do have some great web service APIs as a starting point (basic HTTP with HttpRequest, RSS and ATOM feed support), but haven’t so far given any guidance as to writing an end to end app. I wanted to take just a moment and outline how we designed the Task API and how it ought to be used by third parties when creating background tasks or services, including communicating with a server.

I also wanted to give a shout out to an excellent blog by my colleague Baechul who has been writing some very detailed and well written articles on JavaFX. Of particular interest to this post is one he wrote on JavaFX 1.2 Async.

The Task class in the javafx.async represents the basic programming model for all asynchronous tasks in JavaFX and should form the base class for all such background Tasks. Its design was heavily influenced by the excellent work of Hans Muller on the Swing Application Framework. All JavaFX Tasks have the notion of “state” and “progress”. The state of the Task is expressed though a series of public API members:

  • started:Boolean
  • stopped:Boolean
  • failed:Boolean
  • succeeded:Boolean
  • done:Boolean

We chose to break these states out into Boolean properties rather than have a single property with an enum for two reasons. First, it makes it a little cleaner in the onDone event handler to find out what state the Task ended up in, especially when checking for several states. Second, this allows you to bind to the state of the Task from an external class using some rather clean code. For example, suppose you had a progress indicator and error icon and wanted to make it so that if the task failed, the error icon would be made visible, otherwise the progress indicator would be visible and display progress appropriately.

Group {
    content: [
        ProgressIndicator {
            visible: bind task.started and not task.failed and not task.stopped
            progress: bind task.percentDone
        }
        ErrorIndicator { // this class doesn't exist, but you could write a CustomNode
            visible: bind task.started and task.failed
            causeOfFailure: bind task.causeOfFailure
        }
    ]
}

You’ll notice in this code block I’m also taking advantage of another really nice feature of Task and ProgressIndicator. A ProgressIndicator has a progress property which, if -1, means it is indeterminate but if between 0 and 1 then it indicates the percent done. Task also has a similar property called percentDone where, if -1, it means that the progress of the Task is also indeterminate. This makes binding a ProgressIndicator to a Task very clean. Simply bind like this: ProgressIndicator { progress: bind task.percentDone } and the rest works exactly like it should (switching between determinate and indeterminate mode automatically).

There are several API members for tasks for tracking progress:

  • progress
  • maxProgress
  • percentDone

To help explain these properties lets imagine that our Task was to read a file from disk. In this case, progress could be used to indicate the number of bytes read from disk. maxProgress would indicate the size of the file in bytes, and percentDone would be automatically calculated based on progress and maxProgress. In this way, you can create a UI which will track not only the percent done, but also the discrete amount of work to be done, and how much has been done.

Another simple example would be computing prime numbers. Suppose your app wanted to compute the first 5 prime numbers. In this case, maxProgress would be 5, and progress would be incremented each time a prime number is computed.

Finally, there are two event handlers that all Tasks share: onStart and onDone. onStart is called whenever the Task’s start() method is called, not when the background task is actually started. For example, your Task implementation might push work onto a worker queue. In this case, onStart should be called immediately after start() is invoked, not when the actual background worker that was pushed on the queue starts working.

The onDone function is called whenever the task stops, for any reason. If the task was cancelled manually, or if there was a failure, or if the task completed successfully. In all these cases onDone will be called. I’d like to add “onSuccess” and “onFailure” and “onStopped” events in the future. It is the developer’s responsibility in onDone to check the state of the Task to determine whether the task succeeded or failed or was stopped by checking the state booleans of the Task.

All background operations in JavaFX should extend from Task. This design gives application developers a single common programming model to use with all background operations developed by the JavaFX team or by third party developers. HttpRequest was refactored to be a Task in 1.2 (hence the reason for some of the API changes between 1.1 and 1.2).

There is one major caveat that I need to make clear. As of JavaFX 1.2, JavaFX Script is single threaded. All background work must be done in Java code. The JavaTaskBase abstract class is the base class intended to be used by all background threading implementations in JavaFX 1.2. Unfortunately, the hookup between JavaTaskBase and RunnableFuture didn’t come out right in 1.2 and you’re left with very little guidance as to how to do this appropriately.

The key thing is to understand that nothing should touch JavaFX variables from a background thread. So the idiom to use is to have the JavaTaskBase subclass to have a Java language peer which does the background work. The JavaTaskBase subclass would register a listener on the peer, and the peer would communicate back to the JavaTaskBase through that listener interface. The JavaTaskBase subclass would then pump events from the peer back onto the JavaFX thread (i.e. event thread) using FX.deferAction.

That’s a whole other blog entry though. For starters, I suggest reading Baechul’s take on it.

There is one last thing I wanted to mention, which is a futures item which Brian Goetz and I have been bouncing around a bit. It’s completely pie-in-the-sky at this point, take it with a grain of salt, etc etc. Having to write background tasks in Java is a pain at the moment primarily because you cannot create JavaFX objects in a background thread. Also, it involves writing a messaging interface, a Java class, and a JavaTaskBase subclass. What I’d like to see is something like this:

var task = BackgroundTask {
    run: function() {
        // code in this function runs in a background thread
        defer(function() {
            // code in this function runs on the JavaFX thread
        });
    }
}

The kicker is, the compiler would check and enforce that you don’t illegally modify application state from the background thread, but that you can when using “defer” or “deferAction”. The thing that gets me all excited about the idea is that we don’t have to expose a full threading model (with synchronicity, concurrent collections, memory barriers, semaphores, or anything else), but can instead simplify the problem by having a much more limited coarse grained approach of “this body of work happens in some background thread”. If you want or need to have a full threading model, then we still make that available by writing a JavaTaskBase subclass, but for the vast majority of uses, you could just use the simplified BackgroundTask model.