Pages

Tuesday, December 13, 2011

Spring Web and SmartGWT integration

While working on my current open source project called "openKanban", I got surprised how easy and smooth integration between SmartGWT and Spring Web was.

Server (Spring Web)

I use standard Spring Web Dispatcher Servlet with some controller on server side, which I register by using a component scan. Hence, this is all I need except a scan entry in the applicationContext.xml:

BoardController.java (class header)

To get into action I need a simple Method which is configured by the standard Spring Request Mapping and Request Path Parameter:

BoardController.java (POST method)

There is nothing special about "@RequestMapping" and "@PathVariable". In combination with the class header mapping annotation it means:

Listen to all HTTP-POST requests that address the rescource:
/service/board/[0-n]/activity/add
take the Parameter [0-n] out of the Uri and put it into a variable called parentBoardId.

A little bit more magic comes into play when you watch that method returning a DTO-Object serialized as a JSON String. And that's what SmartGWT or any other requesting client that is calling this method gets. There is no need to configure JSON at any application context. Spring itself checks the classpath and if it finds a JSON serializer, @ResponseBody will return JSON.


I do not use Spring Security in openKanban. To keep it small and simple I decided to use the "JSR-299 CDI Interceptors for Spring Bean"-Implementation and run a self-made @SecurityInterception on any controller method to check if the user is authenticated. I will provide more information about this interceptor in a further posting. For now it's enough to know, that every controller method will be checked for authentication.


In order to demonstrate SmartGWT DataBinding integration I add another HTTP-GET method example:


BoardController.java (GET method)

Summary

There are two available rescources:
  1. /service/board/[0-n]/activity/add (on POST)
  2. /service/board/[0-n]/contact/get (on GET)
Both will call a method that is returning an "automagically" serialized JSON String.

Client (SmartGWT)

SmartGWT provides a very versatile communication. The following example will show you what worked really great in the scope of my project. In this context I preferred using two different kinds of request usage:
  1. Configurated DataBinding on SmartGWT
  2. Using standard GWT RequestBuilder

Configurated DataBinding on SmartGWT

Any SmartGWT widget that implements the interface DataBoundComponent provides a databinding mechanism. In this case it is necessary to define a DataSource, corresponding DataSourceFields and wire it together:

BoardContactTileGrid.java (constructor)

TileGrid is a DataBoundComponent. That's why it is possible to add a DataSource. As usual SmartGWT offers you some configurations. This widget will automatically fetch data when the object of the widget is rendered at the client. That means that it is necessary to have a correct DataSource configured at object creation time.
Let's see what the DataSource looks like:

BoardContactDS.java (constructor)

This is a simple DataSource configuration. After rendering the corresponding widget, an autofetch will trigger a HTTP-GET request to the configured URL. The server response has to be serialized as JSON. We discussed that already in the server section of this posting. It is possible to change the required response to REST, XML or some other custom format. Because I did not need a wsdl contract I decided to use a simple JSON format.

Using standard GWT RequestBuilder

The com.google.gwt.http.client.RequestBuilder seems powerful but some lines of code are necesarry to get data serialized and send to the server. A request is built in four steps:
  1. Creation of RequestBuilder object and passing the HTTP-Method and URL
  2. Configure HTTP-Header
  3. Configure HTTP-Parameters
  4. Send Requests and add a Callback
As there are many different requests needed and as they look pretty much the same, abstraction and generic are very helpful in order to reduce duplicate lines of code. So instead of having around 40 LoC on every call I encapsulated it down to this:

Two lines of Code for a request

This leads us to the following request:
  1. uri: service/user/remove/
  2. parameters: contactMail=stop@hammertime.de&id=42

This works for self defined content as well as for whole objects.


Two lines of code for a request

As you can see I used two ways to wrap all the HTTP stuff. It is either possible to pass static URL parameters or to pass complete Data Transfer Objects (DTO's) to the PreparedRequest to get things done. This has been enabled by using generic typedef, varargs and polymorphism:

PreparedRequest conctructor for static parameters

Java 5 varargs provides flexibility to add as much parameter as I like. The generic DTO to string serialization looks like:

PreparedRequest conctructor for DTO's

The PreparedRequest parameter needs to be typed as a subclass of RequestParameterMap. This relationship enables a polymorphic call to the method "valuesToMap()" which has to be implemented by every DTO.

RequestParameterMap

For a better understanding find below a simple example of an implementation:

ActivityDTO valuesToMap implementation

Although I implemented some level of abstraction, some weak points still remain. If there is a new DTO attribute, it must be added to the valuesAsMap method and there is no "compiletime safety" to make sure it has not been forgotten. Because I could not use Java Reflection API on client side code (remember I am talking about code that is converted to JavaScript), things may get a little bit harsh. Anyway I am still positive about an easy, robust and practicable integration between Spring Web and SmartGWT over HTTP.

4 comments:

  1. i would be very pleased if You could explain how do You check if user is authenticated

    ReplyDelete
  2. Hi Frank thanks for your tutorial
    I was wondering if there is a source code for this tutorial or is there any other tutorials for SmartGWT and RESt webservices.
    good luck

    ReplyDelete
  3. Hello Mohammad, its an open source project, you can see the whole source code on: http://code.google.com/p/openkanban/

    ReplyDelete
  4. Pawel,
    I just use session cookies and a server side user credentials DB. By using spring intercepter, I check every REST resource for accessibility.

    ReplyDelete