Friday, June 3, 2011

RESTful JSON web services with JSOG and Spring

Before I originally developed JSOG, we were using the json.org library and Restlet. Our needs were pretty basic, we were just GETting and POSTing JSON strings between services. Later we ended up using some JSONP.

My biggest beef with Restlet is there doesn't seem to be a good way to unit test it. From what I can tell, the convention is to keep all your logic outside of Restlet and use it only to expose the REST interface. I'm also a heavy spring user, and it occurred to me very quickly that Restlet's integration with Spring was... complicated.

So, instead of jumping through Restlet's hoops, JSOG now integrates very easily with Spring MVC. Note, there are two ways of integrating JSOG with Spring. We're going to be using the "Flexible" approach, and we'll be using a JSON-specific DispatcherServlet that handles all *.json requests. This basic example should show you how add RESTful services to your application.

First, we'll need the dependencies. I'm using Maven; so here's what a minimal POM looks like.
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jsogws</groupId>
<artifactId>jsogws</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>jsogws</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.jsog</groupId>
<artifactId>jsog</artifactId>
<version>0.30</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
<finalName>jsogws</finalName>
</build>
<repositories>
<repository>
<id>jsog.sourceforge.net</id>
<url>http://jsog.sourceforge.net/m2-repo</url>
</repository>
</repositories>
</project>


Well that's a fair chunk of configuration. Basically, we're importing JSOG from the JSOG maven repository. We also import the relevant Spring artifacts and Commons IO.

Next up, we need to configure the DispatcherServlet.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

<!-- JSOG web services -->
<servlet>
<servlet-name>jsog</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsog</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>

</web-app>


Here we map every *.json request to the DispatcherServlet named "jsog".

Now we need to configure the dispatcher servlet. We used the default configuration file name, in this case:
jsog-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- Look for controllers -->
<context:component-scan base-package="jsogws.services.json"/>

<!-- All controllers managed by this servlet should use the JsogViewResolver -->
<bean id="viewResolver" class="net.sf.jsog.spring.JsogViewResolver"/>

<!-- We need this to consume JSOG that has been sent in the request body -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="net.sf.jsog.spring.StringJsogHttpMessageConverter"/>
</list>
</property>
</bean>

</beans>


In this configuration file, we tell the DispatcherServlet to look for "@Controller" annotated classes in jsogws.services.json. Then we setup the JsogViewResolver, which tells Spring to create and use a JsogView whenever we return a JSOG object in a controller. (JsogView does the lion's share of the work). We also setup a MessageConverter so we can consume JSON.

Note that at this point, most of this is pretty standard Spring stuff. The MessageConverter and ViewResolver are the most "abnormal" things in here: about 8 lines of boilerplate configuration.

From this point, creating a JSON-based RESTful service is just about like creating any other RESTful service.

Let's take a look at a Hello World example:

HelloJsog.java

package jsogws.services.json;

import net.sf.jsog.JSOG;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloJsog {

@RequestMapping(value="/hello.json", method=RequestMethod.GET)
public JSOG hello() {
return JSOG.object("Hello", "World!");
}

@RequestMapping(value="/hello.json", method=RequestMethod.POST)
public JSOG hello(@RequestBody JSOG request) {
String name = request.get("name").getStringValue();
return JSOG.object("Hello", name);
}

}


This class defines two methods:
One accepts a GET request and responds with an object: {"Hello", "World!"}.

The other accepts a object via POST, that contains a "name" property. It returns an object with the name: {"Hello", "foo"}.

That's all there is to creating RESTful JSON web services with JSOG and Spring. If you're wondering "what's RESTful about this?" Check out Spring's @PathVariable annotation. Wiring the rest together is elementary.

A quick gotcha: When you send data to your services, you need to use the content type "application/json" and the encoding ISO-8859-1. These are modifiable through properties on the JsogViewResolver and StringJsogHttpMessageConverter.

You also get JSONP for free. Append ?callback=foo and "foo" becomes your callback method. The name of this parameter is settable in JsogViewResolver, but will work with jQuery by default.

Now your web services use the same framework style as everything else in your application, and can easily be unit tested.

Download
jsogws.tgz