• Nov
    • 03
    • 2011

Client Server with JavaFX 2 and Hessian (+Guice +FXML)

Posted by In Uncategorized
This entry is part 6 of 15 in the series Building JEE applications in JavaFX 2.0

Ok, due to popular demand, I’m going to fast track a bit of this walk through. I had planned to do more on the client end first, but it seems everyone wants to know how to build a client-server app (apparently that Internet thing has really taken off).

So in this post we’re going to build a very simple hello world client-server application, using HTTP. We’re going to use Hessian to make all our client-server communication nice and easy. There are a whole load of other communication options out there but Hessian is simple, lightweight and just works.

By the looks of it people are favouring Guice over Spring at this stage, due to the concept of it being ‘lightweight’. As such, I’m going to stick with Guice as our DI toolkit for the front-end. In later posts we’ll have a look at changing over to Spring, which really starts to show its advantages when we start working with database transactions and security.

This topic is a little more in-depth than some of the previous stuff. If you’ve never written a servlet or basic web application before, you may want to brush up on this first. Also, we’re going to use some JavaFX threading which is only lightly documented at this stage, and the API is just a little raw still too in my opinion. It may be a bumpy ride for some.

The full source code (as a multi-module maven project) can be found at: http://code.google.com/p/jfxee/source/browse/trunk/jfxee7/

Step 1: Design your application

We’re going to keep things as simple as we can. We will have a simple client that allows the user to enter their first and last name. The client will then pass these details to the server and the server will return a simple welcome message based on the name entered.

If we get everything right, our client will end up looking like the following:

Step 2: Create a Service interface

The great thing about Hessian is that it allows us to define our client-server protocol in pure Java objects. It works in a very similar way to RMI, where you define an interface for your server, which your server implements and client accesses. Hessian then provides the magical wiring underneath our interface to serialize our data and send it (over HTTP) to the server, where it gets deserialized and passed into our server implementation. Pretty sweet.

For our example, our service interface is very simple and looks like this:

public interface HelloService
{
    String sayHello(Person person);
}

This is our ‘contract’ between the client and the server. The client will call the sayHello method and the server will implement it.

We could pass two strings into the sayHello method for the first name and last name, but to make things a little bit interesting and show the power of Hessian, I have chosen to encapsulate all this in a Person bean. Hessian will magically handle all the serialization for this.

Our person bean is a standard serializable Java bean and looks like this:

import java.io.Serializable;

public class Person implements Serializable
{
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName()
    {
        return firstName;
    }

    public String getLastName()
    {
        return lastName;
    }
}

Step 3: Create a Service implementation

Now that we’ve defined the contract between our client and server, we need to have our server fulfill that contract. We’re keeping things simple in this example: our server will simply build a message out of the Person details and send this back to the client. In a real world application, your server will likely hit a database, provide some security and do all the other things that a serious application does, but we won’t worry about that here.

Our service is just another Java object that implements the HelloService interface we defined above. The only special thing about it is that it extends HessianServlet, which is a custom servlet provided by the Hessian platform. All the magic happens in here and by simply extending it we have a ready-to-roll server implementation. Too easy.

Our service implementation looks like this:

package com.zenjava.jfxee7;

import com.caucho.hessian.server.HessianServlet;

public class HelloServiceImpl extends HessianServlet implements HelloService
{
    public String sayHello(Person person)
    {
        // in the real world, use a logging API like slf4j or log4j
        System.out.println(String.format("The server received a request from %s %s",
                person.getFirstName(), person.getLastName()));

        return String.format("The server says hello %s %s!",
                person.getFirstName(), person.getLastName());
    }
}

Step 4: Run your server as a web application

The next step is to publish our service as a web service. I’m going to assume you are at least a little familiar with creating normal servlets and building webapps. If not, you probably want to google these topics – there’s a wealth of information out there on this, just ask Google.

To publish our service we create a web.xml that loads our HelloServiceImpl as a servlet. The HelloServiceImpl is no different to any other servlet and can be exposed in exactly the same way:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>HelloService</servlet-name>
        <servlet-class>com.zenjava.jfxee7.HelloServiceImpl</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloService</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

From here you simply have to bundle your application into a war file and then deploy it to a server. I use Maven for all my builds and this makes this step extremely easy. Check out the example POM for this. If you’re using ANT (maybe it’s time to upgrade :) ) then you might find something like this helpful: http://javabeanz.wordpress.com/2009/02/12/how-to-create-a-war-file-using-ant/

The resulting war file can be deployed to any servlet friendly server. I use good old Tomcat but you can use Jetty, Resin or any of the other free ones out there. If you’re insane, you can pay for one of the commercial JEE servers (but really, don’t bother).

Step 5: Create a view and controller

Our server is now deployed and running, and we’re ready to build our client. The first part is easy, we just build a GUI to service our needs. I’m going to use the same Guice + FXML approach as in my last post but you could use the Spring approach, or even roll it all by hand and use no Dependency Injection at all if you’re into an old-fashion style.

Here’s our view:

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

<?language javascript?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox fx:id="view"
      fx:controller="com.zenjava.jfxee7.HelloController"
      spacing="6"
      xmlns:fx="http://javafx.com/fxml">

    <styleClass><String fx:value="enterDetails"/></styleClass>

    <children>

        <Label fx:id="messageLabel" text="Waiting for your input">
            <styleClass><String fx:value="message"/></styleClass>
        </Label>

        <Label text="First Name"/>
        <TextField fx:id="firstNameField"/>

        <Label text="Last Name"/>
        <TextField fx:id="lastNameField"/>

        <Button text="Connect to Server" onAction="#submit"/>

    </children>
</VBox>

And here’s our controller:


import com.google.inject.Inject;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class HelloController
{
    @FXML private Node view;
    @FXML private Label messageLabel;
    @FXML private TextField firstNameField;
    @FXML private TextField lastNameField;

    public Node getView()
    {
        return view;
    }

    public void submit(ActionEvent event)
    {
        // todo implement this to talk to the server
    }
}

Step 7: Use Guice to load the UI and inject the service

In order for our client to connect to the server, all it needs to do is use the HelloService interface we created earlier and use Hessian to load this. The following code will do the trick:

HessianProxyFactory factory = new HessianProxyFactory();
String serverUrl = "http://localhost:8080/hello";
HelloService helloService = (HelloService) factory.create(HelloService.class, serverUrl);
// use the helloService object now and hessian will magically make the calls on the server for you

This is basic Hessian stuff – we use the HessianProxyFactory to create a new instance of our HelloService for us, passing in the URL of our server. The factory then creates a ‘proxy’ (in this case, you could call it a ‘stub’), that implements the interface by making HTTP calls onto the servlet on the server side. The Proxy and the servlet work together to serialize the data between each other and make the actual call on the server. From our end, it’s all just nice simple Java.

We could create this proxy anywhere in our code and use it via plain Java calls and for this simple little scenario that would do the job. In our last post however we looked at ways to use Guice to load our controllers and provide Dependency Injection and this is a great candidate for this. As our project gets bigger and we end up with more controllers wanting to use more services, this DI is really going to pay off.

In order to inject our service using Guice, we first add the following line to our HelloController:

@Inject private HelloService helloService;

And now we can just wire up everything in our Guice module. I’m going to use instance injection in this case, but you could use the provider style approach just as easily. The instance injection approach looks like so:

import com.caucho.hessian.client.HessianProxyFactory;
import com.google.inject.AbstractModule;
import javafx.fxml.FXMLLoader;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;

public class HelloClientModule extends AbstractModule
{
    protected void configure()
    {
        try
        {
            HessianProxyFactory factory = new HessianProxyFactory();
            String serverUrl = "http://localhost:8080/hello";
            HelloService helloService = (HelloService) factory.create(HelloService.class, serverUrl);
            bind(HelloService.class).toInstance(helloService);

            bind(HelloController.class).toInstance((HelloController) loadController("/Hello.fxml"));
        }
        catch (MalformedURLException e)
        {
            // NOTE: this is not best-practice for exception handling. Check the
            // Guice documentation for better ways.
            throw new RuntimeException(String.format("Error connecting to server"), e);
        }
        catch (IOException e)
        {
            // NOTE: this is not best-practice for exception handling. Check the
            // Guice documentation for better ways.
            throw new RuntimeException(String.format("Error loading FXML file"), e);
        }
    }

    protected Object loadController(String url) throws IOException
    {
        InputStream fxmlStream = null;
        try
        {
            fxmlStream = getClass().getResourceAsStream(url);
            FXMLLoader loader = new FXMLLoader();
            loader.load(fxmlStream);
            return loader.getController();
        }
        finally
        {
            if (fxmlStream != null)
            {
                try
                {
                    fxmlStream.close();
                }
                catch (IOException e)
                {
                    System.out.println("Warning: failed to close FXML file");
                }
            }
        }
    }
}

And then to launch our application, we simply have:


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;

public class HelloClient extends Application
{
    public static void main(String[] args)
    {
        launch(args);
    }

    public void start(Stage stage) throws Exception
    {
        Injector injector = Guice.createInjector(new HelloClientModule());
        HelloController helloController = injector.getInstance(HelloController.class);
        Scene scene = new Scene((Parent) helloController.getView(), 320, 240);
        scene.getStylesheets().add("styles.css");
        stage.setScene(scene);
        stage.setTitle("JFX2.0 Sprung");
        stage.show();
    }
}

Step 8: Call the server in a background thread

So we have a server and a client, and our controller has access to the server via the HelloService. Now we can just make the following call in our controller right?

public void submit(ActionEvent event)
{
    Person person = new Person(firstNameField.getText(), lastNameField.getText());
    String messageFromServer = helloService.sayHello(person);
    messageLabel.setText(messageFromServer);
}

There’s two problems with this though: threads and exceptions.

Now that the call to sayHello() is actually a remote call to a server it could be slow. Depending on the network speed it could take seconds for the client and server to do all the socket and serialization work. Java FX (like Swing) uses a single thread for application processing. If we lock this thread up waiting for the server to respond then our UI will become unresponsive as the application thread won’t be able to process mouse moves or even redraw the screen (this is a slight simplification of how threads and rendering work in JFX but from our point of view this is enough for us to know).

As well as the threading issue, we’ve also introduced a lot more room for things to go wrong: the server may be down, the client may have lost internet connection, the wrong URL may have been entered, etc. These will all be thrown as runtime exceptions so we could just ignore them and have the system crap out but this is not very nice for our users. Ideally we need to start handling these errors and show the user a friendlier message, such as “Hey mate, did you forget to plug in your network cable again?”, so they can actually do something about it and try again.

Fortunately JFX has an answer to both of these problems, and that answer is the Worker/Task/Service framework. Unfortunately this is not a simple toolkit to use (although if you are familiar with SwingWorker you will have a good head start) and even more unfortunately there is extremely little documentation on this as yet. The only real guide that I’ve found is this post from Richard that uses an early version of the framework while still in beta. The API has been tidied up a little since and Richard also neglected to give us too much detail on how to actually handle the response from the server, or handle errors.

We can muddle through it however, and by piecing all the bits together here’s a threading approach that works:

public void submit(ActionEvent event)
{
    // extract person details from the screen
    final Person person = new Person(
            firstNameField.getText(),
            lastNameField.getText()
    );

    // pass the details to the server in a worker thread
    final Task task = new Task()
    {
        protected String call() throws Exception
        {
            return helloService.sayHello(person);
        }
    };

    // handle the result
    task.stateProperty().addListener(new ChangeListener()
    {
        public void changed(ObservableValue source,
                            Worker.State oldState, Worker.State newState)
        {
            if (newState.equals(Worker.State.SUCCEEDED))
            {
                messageLabel.setText(task.getValue());
            }
            else if (newState.equals(Worker.State.FAILED))
            {
                 // always log your system errors with stack trace (please!!!)
                messageLabel.setText("Oh no, a system error, we're all going to die!");
                Throwable exception = task.getException();
                exception.printStackTrace();
            }
        }
    });

    // start the task in a thread
    messageLabel.setText("Contacting server...");
    new Thread(task).start();
}

As you can see this approach uses a ‘Task’, which is the simplest unit of the Worker framework. The Service base class is much more powerful (you can cancel active threads for example) but is more complex to use. For an example of using a Service, check out this forum topic: https://forums.oracle.com/forums/thread.jspa?messageID=9928424&#9928424

We also have pretty simple exception handling here. Ideally we would check the type of the Exception thrown and provide an error message to match. To do this we just use a big if-else statement and instanceof like so:

else if (newState.equals(Worker.State.FAILED))
{
    Throwable exception = task.getException();
    if (exception instanceof HessianConnectionException)
    {
         messageLabel.setText("Couldn't connect to the server, sorry about that.");
    }
    else
    {
         // always log your system errors with stack trace (please!!!)
         messageLabel.setText("Oh no, a system error, we're all going to die!");
         exception.printStackTrace();
    }
}

A note on building, deploying and running

Above are all the bits and pieces we need for our client and server communication and if you’re familiar with web applications and this style of development that’s probably all you need to know. Simply deploy your generated WAR file to your web server, edit the URL in the client to match and then run HelloApplication. Sorted.

For anyone who is not familiar with this stuff however, there are a few gotchas to be aware of when it comes to deploying and running. Firstly is how to package all of this up. You have two separate applications now, a client and a server, but they both share the common service API. You could bundle all the classes together and deploy it this way if you’re being lazy, but you should really separate them to avoid bloat, especially the client which typically is downloaded.

In my setup, I have one module (bundles into a jar) for the ‘common’ code, which includes the HelloService interface and the Person bean. Then my ‘client’ module has all my client code (bundled into a jar) and my ‘server’ module has all my server code (bundled into a war). The client and server then reference back to the ‘common’ module as if it were a third party library. It’s a little more complicated but it is the cleanest separation – you are free to make your own setup work for you.

When you deploy your web application the server will have a base address (such as http://zenjava.com) where the server (e.g. Tomcat) is running. When you run locally this address will typically be http://localhost:8080. Depending on how you deploy your WAR file it may then be allocated a sub-domain. In Tomcat for example, if you name your WAR file ‘helloserver.war’ and then put this into Tomcat’s ‘webapps’ directory, the URL you will need to use in your HelloClientModule is “http://localhost:8080/helloserver/hello”.

Ultimately you will likely want to deploy your client via webstart and then you will want it all bundled into your server (but not on the server’s classpath just for some added complexity). I’m not going to confuse people further with this here, if you can’t work out the packaging side of it yourself just run the client from within the IDE for now. I am planning to post something on doing this sort of complex deployment using Maven (our best friend in this situation) in a future post.

The above is hopefully enough to get you started on the road to developing client-server Java FX 2 applications over the web.

Enjoy!

The full source code for this post (as a multi-module maven project) can be found at:http://code.google.com/p/jfxee/source/browse/trunk/jfxee7/

Series Navigation<< Generic ControllersJFX Flow early access >>

15 Comments

  • November 3, 2011

    Very nice series, and extremely instructive. Would you consider repurposing this material for a series of technical articles on JavaFX and Java EE on the Oracle Technology Network (OTN) or Java Magazine? You can contact me on Twitter (@javafx4you) or by email javafx4you at sun dot com (yes those email addresses still work)

  • John Smith
    November 3, 2011

    Very, very nice post. It is so good to see new work on taking the mountain of experience in server side Java and better integrating with client side Java.

  • November 16, 2011

    Very nice post. Since I was trying JavaFX on Linux, I used this as second test to see if wine could communicate with linux.

  • Marco
    September 4, 2012

    this post is very interesting, tank you,
    i had try to deploy it with intyellj IDEA and Maven with the same directory and files, but i see an error that i can’t fix in the IDE:
    In the file HelloClientModule, in the catch field, there are two instructions where appear “String.format” and my Ide say: cannot resolve the metod “format(java.lang.String)”.
    I had remove the argumets of the RuntimeException and type at the command line “mav clean package”
    but the application in the target folder don’t work…
    what’s my error ?
    thank you very much

  • Daniel Todt
    September 11, 2012

    Hello, I found your article very interesting.

    However, I have some problems to run the jfxee7.

    I’m running on JBoss, to test, and shows nothing, does not load the GUI.

    Is there anything that is not on that source from SVN?
    Well I just downloaded the source, I changed the path and name of the JavaFX war after build.

    I’m trying to make a JavaFX application running in the browser from a web application that is running on the JBoss.

    Is it possible?

    Thanks for the help.

  • December 13, 2012

    I am quite sure I’ll understand plenty of new stuff right here! All the best . on the table!

  • January 5, 2013

    hi!,I really like your writing very so much! proportion we be in contact more approximately your article
    on AOL? I require a specialist in this area to resolve my problem.

    May be that’s you! Looking ahead to see you.

  • TEST
    February 21, 2013

    Hello! I simply wish to offer you a big thumbs up for your
    great information you have right here on this post.
    I’ll be returning to your website for more soon.

  • March 3, 2013

    Howdy! I know this is kinda off topic nevertheless I’d figured I’d ask.
    Would you be interested in trading links or maybe guest authoring a blog
    post or vice-versa? My site discusses a lot of the same topics as yours and I feel we could greatly benefit from each other.
    If you happen to be interested feel free to shoot me an e-mail.
    I look forward to hearing from you! Great blog by the way!

  • April 23, 2013

    Hi, I do believe this is a great web site. I stumbledupon it
    ;) I am going to return once again since i have saved as a favorite it.

    Money and freedom is the greatest way to change, may you be rich and
    continue to guide other people.

  • May 18, 2013

    It feels like I’ve visited this blog before on but after looking at some of the articles I realized it’s new for me.
    Still, I’m going to bookmark this blog and begin coming here regularly.

  • June 9, 2013

    I couldn

  • June 20, 2013

    This paragraph is genuinely a fastidious one it helps
    new net viewers, who are wishing in favor of blogging.

Leave a Comment