Skip to main content

Integrating JavaFX with JavaEE Using Spring and Hessian Protocol

June 30, 2009

{cs.r.title}







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 binding feature.

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 called Main.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 a ServiceWrapper 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 the ServiceWrapper.

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, the ServiceLocator calls the public onError function 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.

[prettify]

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;

  }

}

[/prettify]

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

[prettify]

    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);

    }

[/prettify]

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 catch clause, we use a custom exception, called ServiceException, 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:

[prettify]

// ServiceLocator that calls Hessian services

var serviceLocator :  ServiceLocator = ServiceLocator {

    serverURL : "http://localhost:9090/HessianService";



    onError : function (se: ServiceException) {

        errorMessage = "Error: {se.getCause()}";

    }

}

[/prettify]

Notice that we define the onError function to handle errors. In this function, we assign to the errorMessage 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:

[prettify] 

    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()}";

        }

    }

[/prettify]

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 locator getService 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 as reverseString, which takes a stringVal parameter.

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 a try/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 use javafx.ext.swing.SwingTextField

with its content bound to the result variable value in this way:

[prettify]

SwingTextField {

    columns: 10

    text: bind result;

    editable: false

  }

[/prettify]

To inform the user that an error has occurred during the communication with the server, we use javafx.scene.text.Text and we

bind its content to the errorMessage value:

[prettify]

content: bind errorMessage;

[/prettify]

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:

[prettify]

public interface ReverseStringService {

    public String reverseString(String val); 

}

[/prettify]

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:

[prettify]

public class ReverseStringServiceImpl implements ReverseStringService {

    public String reverseString(String val) {

       StringBuffer buff = new StringBuffer(val);

       return buff.reverse().toString();

    }

}

[/prettify]

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 the applicationContext.xml configuration file, we add this line:

  [prettify]

<bean id="reverseService" class="it.azzola.service.reversestr.ReverseStringServiceImpl"/> 

  
[/prettify]

In this way we declare that the ReverseStringServiceImpl 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 Spring ServletDispatcher. We add the following to the dispatcher-servlet.xml file:

[prettify]

    <bean id="urlMapping" 

	  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

        <property name="mappings">

            <props>               

                <prop key="/reverse.service">reverseHessianService</prop>

            </props>

        </property>

    </bean>

[/prettify]

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 the serviceInterface.

The service property value is a reference to a bean name managed by the Spring framework defined in applicationContext.xml and

called reverseService, while the service interface is the interface we defined before. The code snippet below shows it:

[prettify]

	<!-- 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>

[/prettify]

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 java.net is reversed and shown in the client.

Conclusion

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.

Resources

Comments

interesting approach

Hi Francesco, very nice article, informative in a short amount of space. I'd like to suggest looking at this example: http://weblogs.java.net/blog/cajo/archive/2007/09/simple_interjvm.html I think it is a little more flexible, a little more simple, a lot smaller, and significantly faster. Your feedback or suggestions would be most welcome. Just something to consider of course, thanks for your article! All the best, John

interesting approach

does cajo work over http?

interesting approach

I read the link you suggested to me and i think it is a really simple way to make remote call, but i'm wondering if it works via HTTP. If not it would be useful only in an intra-net application and it could be a problem using your tool when the server is behind a firewall. Anyway i think your approach is simple and easy to use. Francesco

interesting approach

Yes it can, with the addition of only one line of code: there is a call to Remote.config() that will configure all communications to run over HTTP automatically, even through a password protected proxy server. However, as the hessian folks will doubtlessly attest; there is a tremendous performance penalty in both speed and performance using HTTP. You are right however, some intensely anile corporations may have their internet firewalls so locked down that a client may only communicate with the outside world over http/https. Irrespective of weather this helps security, it is a fact; and as a result, I would expect that all RPC frameworks have a 'fallback' mode to run over HTTP. This is certainly the case for cajo as well. John

Push from server?

Hi Francesco, Can the Hessian server initiate action on the client? Can it push from the server side? Thanks, Dave

Push from server?

As far as i know the server can't initiate action on the client side.

Thanks for this great

Thanks for this great article. I hoping just to try your project, and the download-able source link is faulty. Could you plz fix it?

The link is fixed now.

The link is fixed now.