Skip to main content

Effective JavaFX Architecture Part 3 - Asynchronous calls, Command Pattern and Testability

Posted by srikanth on July 23, 2010 at 5:16 AM PDT

In the previous installment of Effective Architecture, I covered TDD with Model-View-Presenter. However the code I presented had synchronous server calls. In JavaFX (like Swing), the UI code runs in the Event Dispatch Thread (EDT). It is unwise to block the EDT. Hence it is encouraged to execute all server calls on a separate thread.

SwingWorker

Swing provides SwingWorker to execute tasks off the EDT. SwingWorker also allows switching to EDT for things like setting the UI data etc. A snippet is shown below.  When a button is pressed, the SwingWorker executes the code in doInBackground() in background. When completed, the code returns the data as an Object. The SwingWorker then calls process() and done() on the EDT, to allow the user code to update the UI etc.

Listing 1. SwingWorker

final JButton button = new JButton("Save");
ActionListener l = new ActionListener() {
    public void actionPerformed(ActionEvent e) {
    SwingWorker worker = new SwingWorker() {
        protected Object doInBackground() throws InterruptedException {
          //connect to server amd get data
          return data;
        }
        protected void process(Object data) {
          //update ui here
        }
        protected void done() {
          //done
        }
};
worker.execute();

Async Tasks - The JavaFX way

JavaFX provides a similar feature. But it is more generic and basic. We need to customize it to suit our needs. Others have described it in more detail (Baechul and Clarkeman have described it in more detail here - http://blogs.sun.com/baechul/entry/javafx_1_2_async). I will not repeat it here, but instead show a UML diagram with a brief summary.

JavaFX Async design

  1. To run something in a different thread, two classes should be created - The actual task performer (a Java class that implements RunnableFuture interface). The JavaFX class that works like a factory for the former
  2. The task performer implements the run() method and provides the custom code in it.
  3. The caller invokes the JavaFX Factory.start()
  4. Once the task is completed, the task performer can switch back to the EDT (if needed) by using FX.deferAction(..) to perform UI modifications.

Notice that there is lot of boiler plate code to get a Async Task going. The async task execution should ideally be made to perform just like the SwingWorker (but without using the SwingWorker). In a nutshell, the SwingWorker is a Command pattern that is configured and then execute() ed. It is pretty trivial to create a few classes that offload this burden. This is where the Command pattern comes in handy. A Command object can be easily created that wraps all this complexity and reduces it to a simple JavaFX object like this with 3 configured functions:

Listing 2. A sample Async command that runs a service asynchronously

 var command = DefaultAsyncCommand {
    runInBackground: function(modelObjectFromUI:Object) {
        //do all the server calls here
    }
    onSuccess: function(modelObjectFromServer:Object) {
//update UI here
    }
    onError: function(e:Exception) {
//show error dialog and do any other error processing
    }
} ;

When the UI wants to make the server call, it needs to just call 

Command.execute(modelObjectFromUI);

The execute() function is already implemented by the Command and internally initiates a AsyncTask and passes references to the 3 configured functions. The async task runs the runInBackground function in background and the rest in EDT. I will not go into the details of the DefaultAsyncCommand and bore you. It is provided in the links at the end.

AsyncCommand and Unit testing

Unit testing asynchronous calls is a major challenge. There are only two ways to test async calls.

  1. Trigger the call and poll
  2. Call service execute off the EDT and wait in loop till result arrives.

Both are not good options for unit testing. Hence I resort to a third method, perhaps the best option. I will not use async calls in the unit tests, but rather use Mock Async commands. If the Async command framework is well tested, then it can be safely replaced with the MockAsync Command. The Mock Async Command looks like a regular async command, by extending the real async command, but with a twist. It runs the code synchronously on the EDT!!  This makes unit testing  asynchronous calls a breeze. However there are some more details to look at: Listing below shows the now-familiar Model-View-Presenter being wired by Spring (See my previous JavaFX architecture blogs for MVP details).

Listing 3. Spring xml for wiring the service, async command and the presenter

    <bean id="timeConsumingService" class="org.fxobjects.samples.async.LongRunningService"/>

    <bean id="asyncCommand" class="org.fxobjects.command.async.DefaultAsyncCommand"/>

    <bean id="helloWorldNodePresenterBean" class="org.fxobjects.samples.async.HelloWorldNodePresenter"
                                                                           init-method="init">
        <property name="id" value="HelloWorldNodePresenter"/>
        <property name="defaultNodePresenter" value="true"/>
        <property name="timeConsumingService" ref="timeConsumingService"/>
        <property name="asyncCommand" ref="asyncCommand" />
    </bean>

Notice that the remote Service is injected into the presenter. (In reality this could be a Hessian Proxy etc.) Also notice that the async command is not internally created, but injected. The command needs some initialization after the beans are injected into the Presenter. The presenter has a cusom init method to do this. (by configuring the init-method property in Spring, the initialization is called by Spring automatically after bean injection)

Listing 4. Initializing the Async Command in the Presenter init() method. This is called by Spring after the bean initialization

    public function init():Void {
        asyncCommand2.runInBackground = function(modelObj:Object):Object {
            var toWhom:String = modelObj as String;
            var returnMessage:String = timeConsumingService1.sayLongHello(toWhom);
            return returnMessage;
        };
        asyncCommand2.onSuccess = function(returnModelObj:Object):Void {
            var wishMsg:String = returnModelObj as String;
            helloWorldNode.messageText2.content = wishMsg;
            helloWorldNode.processingText.content = "Processing Complete";
        };
        asyncCommand2.onError = function(exc:Exception):Void {
            Alert.inform("An error occured while executing Long Running Service 2");
        };
    }

Notice that I did not use the default JavaFX initialization mechanism here as I showed in Listing 2. Instead of the above mechanism, I could have created and initialized the Command as shown in Listing 2. But, remember – we need to write our code to be testable don’t we? A code written as Listing 2 is not testable because, it already uses the AsyncCommand and does not allow us to inject a MockAsyncCommand. This customization is done so that it allows our test code to inject a mock async command and still get initialized the default way. The Test code is follows:

Listing 5. JUnit Testcase demonstrating the use of MockAsyncCommand to test the asynchronous calls

public class AsyncTest extends TestCase {
    var presenter:HelloWorldNodePresenter;
    var node:HelloWorldNode;
    var jMockContext:Mockery = new Mockery();

    public override function setUp():Void {
        //do all the setup that would otherwise done by spring
        presenter = HelloWorldNodePresenter { };
        var cmd:DefaultAsyncCommand = MockAsyncCommand { };
        presenter.setAsyncCommand(cmd);
        presenter.init();
        loginNode = presenter.getNode() as HelloWorldNode;
    }

    public function testHello():Void {
        var toWhom:String = "Srikanth";
        var expectedRetValue:String = "Sorry, It took me long to say Hello World to you {toWhom}";
        var mockService:ILongRunningService = jMockContext.mock(ILongRunningService.class);
        presenter.setTimeConsumingService(mockService);

        var expectation:Expectations = new Expectations();
        expectation.oneOf (mockService).sayHello(toWhom);
        expectation.will(
            expectation.returnValue(expectedRetValue)
        );
        jMockContext.checking(expectation);

        assertTrue(node.messageText.content, expectedRetValue);
    }

  ..
}

 Until now you saw how I made async calls to the server. This command is independent of the protocol used to connect to server. Be it Hessian, RMI-IIOP or any other fancy protocol, all you need to do is wrap the service call in the generic DefaultAsyncCommand and keep going!!. Things are slightly different with Http (but not very different). I will cover that next.

Async Http calls

JavaFX provides HttpRequest out of the box for executing http calls. HttpRequest is async by nature. And so there is no need to write additional wrapper code for async support. However there are a few twists in Http command:

  1. There is no need to provide the runInBackground() function.
  2. The request body is generally in string format (xml/json). Therefore the UI model objects should be converted into strings using appropriate “request builder”. This is applicable for PUT and POST.
  3. The response arrives in string (xml/json) format. Hence a ResponseBuilder have to be provided as the xml/json response from the http call needs to be converted into objects. (A Get HttpCommand only needs a Response Builder). 

As you will see, there is a lot of advantage using Command pattern here too.  I will introduce a custom GetHttpCommand class below. It is based on the same old and familiar command object initialized in the presenter init method (but without the runInBackground section)

Listing 6. Configuring and initializing the GetHttpCommand in Presenter

public class CafeSearchNodePresenter extends NodePresenter {
    public-init var cafeSearchCommand:GetHttpCommand; //injected by spring
    public function init():Void {
        cafeSearchCommand.onSuccess = function (modelObj:Object) {
            var resultSet:ResultSet = modelObj as ResultSet;
            handleSearchResponse(resultSet.getResult()); //populate the UI with results
        }
        cafeSearchCommand.onError = function(exc:Exception) {
            //handleError(exc, "An error occurred when searching");
        }
    }
}

I have used the same principle of injecting the command first and the initializing it in init() method.

The actual http endpoint to invoke in background is specified using Spring while wiring. (You don’t have to necessarily do it this way, but is a recommended approach) Look at the GetHttpCommand wired below:

Listing 7. Spring xml for wiring the ResponseBuilder, GetHttpCommand and the Presenter

    <context:property-placeholder location="classpath:org/fxobjects/samples/httpcafe/fxobjects-httpcafe-config.properties" />
    <bean id="cafeSearchResponseBuilder" class="org.fxobjects.samples.httpcafe.CafeSearchResponseBuilder">
        <property name="applicationSchema" value="/org/fxobjects/samples/httpcafe/LocalSearchResponse.xsd"/>
    </bean>
    <bean id="localCafeSearchCommand" class="org.fxobjects.command.async.http.GetHttpCommand">
        <property name="urlTemplate">
            <value>${protocol}://${serverName}/${contextPath}/localSearch?appid=null&query=coffee&zip={zipCode}&results=10&output=xml</value>
        </property>
        <property name="responseBuilder" ref="cafeSearchResponseBuilder"/>
    </bean>
   
    <bean id="cafeSearchNodePresenterBean" class="org.fxobjects.samples.httpcafe.CafeSearchNodePresenter" init-method="init">
        <property name="id" value="CafeSearchNodePresenter"/>
        <property name="defaultNodePresenter" value="true"/>
        <property name="cafeSearchCommand" ref="localCafeSearchCommand"/>
    </bean>

 The example I use above connects to the Yahoo Local Service to search for coffee shops.  The same example ships with Netbeans as a JavaFX sample and uses a Pull Parser. I instead use JAXB to generate classes from the xsd and then convert xml into Java objects. This example has HTTP GET method and hence needs only the response builder. The task of converting the xml response into Java objects is performed by the response builder as wired in the above example.

By extending the JAXBResponseBuilder (available in the download), the response builder is really a simple class with hardly 5 lines of code. The source code for the GetHttpCommand and supporting classes is also included in the links at the end of this post.

The Command object for HTTP needs some configuration. In particular, it needs a url template and a response builder. The command thus configured is injected into the presenter. The url template is a feature that makes using the HttpCommand easy. As you can see, the Url template has placeholders identified by braces. The values to be substituted for placeholders can be conveniently passed in a Map to the execute() method in GetHttpCommand and it does the rest.

Listing 8. Presenter method invoked when search button is clicked (Notice the search parameters passed in a Map to the GetHttpCommand)

    public function doSearch():Void {
        //clear previous searches if any
        delete cafes;

        //do validation
        if (node.zipCodeTextBox.text.trim().length() == 0) {
            Alert.inform("I would like to search, but please provide me zipcode");
            return;
        }

        var paramMap = new HashMap();
        paramMap.put("zipCode", node.zipCodeTextBox.text.trim());
        cafeSearchCommand.execute(paramMap);
    }

Testing http calls

Much like a MockAsyncCommand, I have introduced a MockGetHttpCommand as well. This mock command is setup by providing one of 2 expectations: expectedResult and expectedException. If  expectedResult is provided, the onSuccess() function is called with that result, thus making tests really easy without any change to the actual code.

The test code isn't much different from our earlier test. Its just that we dont need to use jMock. Instead the MockGetHttpCommand is configured as needed

Listing 9.

public class YahooCoffeeshopTest extends TestCase {
    var presenter:CafeSearchNodePresenter;
    var node:CafeSearchNode;

    public override function setUp():Void {
       presenter = CafeSearchNodePresenter {
           searchCommand: MockGetHttpCommand { }
       };
       node = presenter.getNode() as CafeSearchNode;
    }

    public function testHello():Void {
       var expectedResultObj = ...
       presenter.searchCommand.expectedResult(expectedResult);

       node.zipCodeTextBox.text = "78729"; //Search coffeeshops in the beautiful Austin
       presenter.doSearch();
       assertTrue(node.table.results, ...);
    }
   ..
}

Additional considerations for async calls

When the async call runs off the EDT, the user is free to interact with the UI. This may be a good or a bad thing based on your application. It is good because user can interact with the UI. Bad because user can change data in the current UI and assume that it is saved, which is not the case. Typical applications run the server calls off the EDT, but then still block additional inputs and provide a option to cancel the running request midstream (perhaps because it is running too long)

  1. Use the modal dialog to accept user input to stop asynctask.stop
  2. Or you could just use a glasspane like rectangle with a progress indicator.

FxObjects: Incubator project

I have been discussing many interesting ways of effectively dealing with real world JavaFX projects in a series of blog posts now. I have captured all of these patterns practices into a open source project and named it FxObjects - https://fxobjects.dev.java.net. It is still in infancy, but is stable for all functionality provided. Take it for a spin. It is Apache License. Most of all, it is very simple. There are 6-7 fully working examples. As I discuss more patterns, I will add it there. Your participation is most welcome and is very much valued.

Async Command Sample Project (5 MB+): fxobjects.dev.java.net/files/documents/11182/151722/async-sample.zip

HttpCommand based Yahoo Search Implementation (5 MB+): https://fxobjects.dev.java.net/files/documents/11182/151723/httpcommand-async-sample.zip