Integrating JavaFX with JavaEE Using Spring and Hessian Protocol Blog

Version 2


    Recently, Sun released JavaFX Version 1.1, a new technology that is focused on developing client-side applications with a better look and feel than before. This technology is used to create rich internet applications that behave like desktop applications. As we know, we can split an enterprise application into a server-side application and a client-side application. If we use JavaFX technology to create the client, the problem we have to face is how to exchange information between the client and the server. In the past, we showed how we can do that using JavaFX HTTP and the XML API, but in this article we want to demonstrate another way to achieve it. Generally speaking, there are several protocols that can be used for this purpose, but we will focus our attention on the Hessian Binary Web Service protocol. In particular, we want to describe how we can integrate JavaFX on the client side and the Spring framework with the Hessian protocol on the server side. To demonstrate this, we will develop a service using Spring and a JavaFX client that calls this remote service using the Hessian protocol. To understand better the topic we will cover, it is useful to briefly introduce the Hessian protocol.

    Brief Hessian Protocol Overview

    The Hessian protocol is a binary protocol used to invoke remote services, and it includes a web service protocol and a serialization protocol. It based on HTTP, in this way avoiding firewall-related problems, so it can be used in intranet and external network applications. This protocol doesn't need to use attachments to carry binary information because it is based on a binary data representation. Moreover, there are several implementations in different languages and it is quite simple to use. One of the main advantages is that it is really efficient and it permits developing remote services quickly. Another useful feature is that it is fully integrated in the Spring framework. All these features make the Hessian protocol suitable to be used as a reference protocol in client-server communication. To create a Hessian service on the server side we need to:

    • Create an interface describing the service methods.
    • Implement the interface using a POJO.
    • Configure the service in the servlet engine.
    On the client side we need to:
    • Create a HessianProxyFactory that can be used to invoke remote methods.

    Architecture Overview

    Before diving into the code analysis, it is useful to have a global overview of the system architecture so that we can understand which components we have to develop.

    System Architecture Overview

    Figure 1. System architecture overview

    As shown above, as soon as a user clicks on the button, the JavaFX GUI sends the request to the remote service through the Hessian/HTTP protocol. The remote service is a POJO managed by the Spring framework. This is one of the main advantages of using Hessian and Spring together. Another advantage of using Spring and Hessian is that we can create our POJO as a remote service just by configuring Spring properties, without having to change a line of code. This means that we can reuse our existing POJOs, and it makes them accessible remotely. This is very useful because we can focus our attention on the client side and reuse the business layer. In more depth, behind the scenes more things happen so that the information exchange process between the client and the server can take place. In particular, when the user sends the request, the client serializes the request data so it can be sent. When the request arrives at our service, the data is deserialized and the business process takes place. When the process completes, the result is serialized again and sent back to the client. In order to show the full process of exporting a service, in this article we'll create a new simple service that reverses the input string and makes it accessible remotely.

    Creating the JavaFX Client

    In this section, we will show how to create a JavaFX client that calls this service and how it is possible to create a highly interactive application exploiting the JavaFX bindingfeature. The class diagram below is really simple, but it is useful for distinguishing between JavaFX classes and Java classes.

    class diagram

    Figure 2. JavaFX client class diagram

    Figure 3 shows how the classes interact when the user clicks on the button.

    Client classes interaction

    Figure 3. Client classes' interaction

    As is clear from the diagram above, the client is built using a mixture of JavaFX and Java. The client GUI class calledMain.fx is built using JavaFX, exploiting the powerful features of this technology. We don't want the GUI class to directly call the remote service, so as to keep this class uncluttered. Therefore, we use another JavaFX class,ServiceLocator.fx, that hides the remote service invocation details. In this scenario, we use a common pattern, widely adopted in J2EE applications, called Service Locator. This class has the task of locating the service. Using this strategy, we decouple classes that handle the GUI details from the classes that find the services on the net. Remembering that the Hessian protocol library is written for the Java language and not for JavaFx, the problem we have to face is how to integrate Java classes inside JavaFX classes. To achieve this goal, we use aServiceWrapper Java class with static methods so that they can be invoked directly by JavaFX classes. Inside this wrapper, we can use the Hessian protocol API, and it makes the remote call to our service. To do that, we use a proxy class that hides the remote communication details. In the end, we've accomplished the goal of making our GUI classes agnostic about how the service is found on the net and how the service is invoked. Because of this, we can reuse the GUI classes with different protocols.

    To better understand the overview described above, it is useful to analyze each class, to gain a deeper knowledge about the role it plays. The ServiceLocator class, written in JavaFX, has a public function, getService, that accepts a fully qualified class name and the service name. The service name is used to build the URL used to invoke the remote service, while the fully qualified class name is used by theServiceWrapper. As we can see from the code below, the service locator uses the ServiceWrapper Java class to create a proxy to call the remote service. If an error occurs, theServiceLocator calls the public onErrorfunction to handle the error. In this class, we just define the error function signature, leaving to the client the task of defining what to do. In our case, the client that uses the function to inform the user with an error message is Main.fx. The complete class code is shown below.

    public class ServiceLocator { public var serverURL : String; protected var serviceObj : Object; public var onError : function(es : ServiceException) ; public function getService(serviceClass : String, serviceName : String) : Object { var serviceURL = "{serverURL}/{serviceName}.service"; try { serviceObj = ServiceWrapper.createService(serviceClass, serviceURL); } catch(se : ServiceException) { onError(se); } return serviceObj; } } 

    In the ServiceWrapper we call the remote service, using the HessianProxyFactory class, as shown in this code snippet:

    HessianProxyFactory factory = new HessianProxyFactory(); try { Class c = Class.forName(serviceClass); Object result = factory.create(c, serverURL); return result; } catch (Throwable t) { throw new ServiceException(t); } 

    This class is a simple Java class, where we load at runtime the class specified in serviceClass. Once we have our class descriptor, we can invoke the create method of the HessianProxyFactory. In the catchclause, we use a custom exception, calledServiceException, that wraps all the possible errors.

    The Main.fx class handles all the details that create the GUI, including input fields and buttons. Our GUI is quite simple: it is built with one input field (where the user inserts the string that he or she wants to reverse), one output field to display the result of remote service invocation, and one button that is used to make the remote call.

    The first step for coding this class is configuring our Service Locator so that we can invoke remote services, as shown below:

    // ServiceLocator that calls Hessian services var serviceLocator : ServiceLocator = ServiceLocator { serverURL : "http://localhost:9090/HessianService"; onError : function (se: ServiceException) { errorMessage = "Error: {se.getCause()}"; } } 
    Notice that we define the onError function to handle errors. In this function, we assign to theerrorMessage value the error message with the exception cause. As we will see later, exploiting the JavaFX binding feature, we can show this message to the user if something goes wrong. To handle the event generated when the user clicks on the button to send the request, we use this code:
    action: function() { var service : ReverseStringService = serviceLocator.getService ("it.azzola.service.reversestr.ReverseStringService", "reverse") as ReverseStringService; try { result = service.reverseString(stringVal); errorMessage = ""; } catch(t: Throwable) { errorMessage = "Error: {t.getMessage()}"; } } 
    In this function, many things happen. This is in a way the heart of the GUI. In the first step, we obtain the reference to the remote service, using the service locator instance defined previously, passing the service name and the service class. The service locatorgetService function returns an Object as its result, so we need to cast it to our service interface. We can do that by appending the keyword as and the interface; that is, ReverseStringService. Once we have our service interface, we can freely invoke methods such asreverseString, which takes a stringValparameter. This variable contains the string inserted by the user in the input field. During the method invocation, some problems can occur, so we surround the method invocation with atry/catch clause. In the catch clause we modify the errorMessage value. Notice that as result of the invocation we modify the result value. To make this result value visible, we usejavafx.ext.swing.SwingTextField with itscontent bound to the result variable value in this way:
    SwingTextField { columns: 10 text: bind result; editable: false } 
    To inform the user that an error has occurred during the communication with the server, we usejavafx.scene.text.Text and we bind its content to theerrorMessage value:
    content: bind errorMessage; 

    Creating and Exporting a Remote Service

    To create and export a service on the server side, we need to define the service interface describing the methods and code its implementation. Our service interface is really simple; it consists of only one method:

    public interface ReverseStringService { public String reverseString(String val); } 
    Now we have to implement this interface. In our case, we simply want to reverse the input string. We can do it in this way:
    public class ReverseStringServiceImpl implements ReverseStringService { public String reverseString(String val) { StringBuffer buff = new StringBuffer(val); return buff.reverse().toString(); } } 
    Below we will look at the main steps necessary to configure and export the service we created before. In the first step, we need to define that our POJO is managed by Spring framework. In theapplicationContext.xml configuration file, we add this line:
    <bean id="reverseService" class="it.azzola.service.reversestr.ReverseStringServiceImpl"/> 
    In this way we declare that theReverseStringServiceImpl is a bean identified by the ID reverseService. Now we have to transform our POJO in a remote service that can be called by a client. The next step, therefore, is to map the service to a specific URL so that it can be invoked remotely. To achieve this, we use the SpringServletDispatcher. We add the following to thedispatcher-servlet.xml file:
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/reverse.service">reverseHessianService</prop> </props> </property> </bean> 
    Here, we are telling to Spring framework that our service can be reached at the /reverse.service URL. The next step is export our service. We have to inform the Spring framework that our service has to be exported using the Hessian protocol. We can do this by using the HessianServiceExporter class. We must define two properties: the service and theserviceInterface. The service property value is a reference to a bean name managed by the Spring framework defined inapplicationContext.xml and calledreverseService, while the service interface is the interface we defined before. The code snippet below shows it:
    <!-- We use the Hessian exporter --> <bean id="reverseHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <!-- Here we reference the bean defined in the applicationContext.xml --> <property name="service" ref="reverseService" /> <!-- Here we define our service interface that has to be exported --> <property name="serviceInterface" value="it.azzola.service.reversestr.ReverseStringService" /> </bean> 

    Final Result

    If everything works fine, we should get the result shown below:

    Final Result

    Figure 4. Final result

    As we can see, the input string is reversed and shown in the client.


    In this article, we showed how we can call remote services using JavaFX and Hessian protocol. We demonstrated how easy it is to create an RIA application, and how an existing server-side POJO can be made accessible by a JavaFX client. This simple example can be further expanded and improved, to make it into a truly useful application. It would be interesting modify the code so the client could perform real-time input field validation. One aspect that would be useful to investigate is how we would set up an authentication and authorization system so that we could limit the access to the remote service.