Objective
Once you are finished with this article, you will be able to implement REST services in Google Apps Engine using JAVA, Spring 3.0 and JSON. In the next part you will learn how to add flex application to consume the services.
Step one – Getting all required libraries: Google App Engine SDK, Spring and JSON serializer
If you are using Eclipse (my favorite IDE), you can install google plugin from here. If you choose to go another route, you need to download the sdk, create the project and join us back in the next section. Once you install the plugin, restart eclipse and you can see these buttons

you can proceed to the next section.
Step Two – Create new project
Go to new project wizzard

and select Web application project under google. you should get to the project settings screen

choose your project name (can be anything, not necessarily you google application name) and you base package, click finish and you should see the following project structure:

Step Three – Add Spring Support
Go to springsource.org and download latest 3.0 release, unzip it into a folder where you keep your java libs. Copy the following Jars into war\WEB-INF\llib directory:
- org.springframework.asm-3.0.x.jar
- org.springframework.beans-3.0.x.jar
- org.springframework.context-3.0.x.jar
- org.springframework.core-3.0.x.jar
- org.springframework.expression-3.0.x.jar
- org.springframework.oxm-3.0.x.jar
- org.springframework.web-3.0.x.jar
- org.springframework.web.servlet-3.0.x.jar
Also grab your favorite version of log4j.jar and commons-logging-1.1.1.jar. The trick with the commons-logging is to rename it to something like commons-fix-logging-1.1.1.jar, google app engine replaces this jar with its own version with crippled packages, by providing different name we keep both versions and make spring happy. Once you copied the jars, open project preferences and add those jars to the build library path.

Now lets get to the fun part – configuring spring.
Lets get rid of the generated servlet – just delete it. And open web.xml. My generated one looks like this:
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>Rest_json_flex</servlet-name>
<servlet-class>com.lureto.rjf.Rest_json_flexServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Rest_json_flex</servlet-name>
<url-pattern>/rest_json_flex</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
The final version should look like this:
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet>
<servlet-name>rest-json-flex</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest-json-flex</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
As you can see I have moved log4j.properties from src to WEB-INF location. I like all my config files in one place, you can leave it in src or move it somewhere else, just adjust the path accordingly. Second section defines Spring dispatcher servlet, then we map this servlet to /api/* path. All requests with this pattern will get routed to spring dispathcer servlet.
Step Four – Add JSON support
I looked around for good java JSON library and found couple good candidates. Since Spring uses Jackson JSON I decided to go the same route. Grab the library from here and put jackson-core-1.2.1.jar and jackson-mapper-1.2.1.jar file into projects WEB-INF/lib directory. Choose your favorite license when you downloading the jars.
Step Five – Configure Spring
To configure Spring servlet we need to create servletname-servlet.xml file for spring bean configuration. Our file has to be named rest-json-flex-servlet.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="com.lureto.rjf"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
<bean class="com.lureto.rjf.spring.MyContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultContentType" value="application/json" />
<property name="defaultViews">
<list>
<bean class="com.lureto.rjf.spring.JsonView" />
</list>
</property>
</bean>
</beans>
First we tell String to scan “com.lureto.rjf” package for any bean annotations.
<context:component-scan base-package="com.lureto.rjf"/>
Next we define message converter to convert messages sent to the server into beans using MappingJacksonHttpMessageConverter
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list id="beanList">
<ref bean="jsonHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
The last part tells spring to render messages sent from the server to the client using JsonView. Here we need to do some bug fixing, as you may have noticed the classes used are from “com.lureto.rjf.spring” and not from “org.springframework.web”.
<bean class="com.lureto.rjf.spring.MyContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultContentType" value="application/json" />
<property name="defaultViews">
<list>
<bean class="com.lureto.rjf.spring.JsonView" />
</list>
</property>
</bean>
MyContentNegotiatingViewResolver.java – at the moment of writing this the 3.0.0.RC1 version of ContentNegotiatingViewResolver has bug with the list being singleton.
package com.lureto.rjf.spring;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
public class MyContentNegotiatingViewResolver extends ContentNegotiatingViewResolver {
protected List <MediaType > getMediaTypes (HttpServletRequest request ) {
List <MediaType > result = super. getMediaTypes(request );
if (result. size() == 1 )
result = Arrays. asList(result. get(0 ));
return result ;
}
}
JsonView.java – is a copy org.springframework.web.servlet.view.json.MappingJacksonJsonView with one change:
@Override
protected void renderMergedOutputModel (Map <String, Object > model,
HttpServletRequest request,
HttpServletResponse response ) throws Exception {
model = filterModel (model );
JsonGenerator generator = objectMapper. getJsonFactory(). createJsonGenerator(response. getWriter());
if (prefixJson ) {
generator. writeRaw("{} && ");
}
objectMapper. writeValue(generator, model );
}
I had to change response.getOutputStream() to response.getWriter(), since jetty’s implementation of setting content type and encoding uses writer, if you try grabbing a stream after that, you will get an exception.
Step Six – Lets write a little bit of code
First lets define a model object that we will send across the wire. Here is my User.java class:
public class User {
private long id ;
private String email ;
private String name ;
public long getId () {
return id ;
}
public void setId (long id ) {
this. id = id ;
}
public String getEmail () {
return email ;
}
public void setEmail (String email ) {
this. email = email ;
}
public String getName () {
return name ;
}
public void setName (String name ) {
this. name = name ;
}
}
And implement the controller to send and receive data. Here is my UserController.java:
package com.lureto.rjf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping ("/user")
public class UserController {
private static Logger logger = Logger. getLogger( UserController. class );
@ModelAttribute ("users")
@RequestMapping (value = "/", method = RequestMethod. GET)
public List <User > listUsers () throws IOException {
List <User > users = new ArrayList <User >();
User user = new User ();
user. setId(10001 );
user. setEmail("user.one@gmail.com");
user. setName("User UNO");
users. add(user );
return users ;
}
@ModelAttribute ("user")
@RequestMapping (value = "/", method = RequestMethod. POST)
public User saveUser ( @RequestBody User user ) throws IOException {
logger. debug(user );
return user ;
}
}
Lest go step my step through this and look at what all these little thing do.
We have marked our class as @Controller which indicates to sprign framework that this is a controller class. @RequestMapping(“/user”) indicates that this controller will handle requests that start with /user, /api/user to be precise, since spring servlet is configured to handle /api/* mappings.
@ModelAttribute ("users")
@RequestMapping (value = "/", method = RequestMethod. GET)
public List <User > listUsers () throws IOException {
Here we define a methow which will be invoked when /api/user/ path gets called with GET method. If you running standard config http://localhost:8080/api/user/ would be the url to hit in the browser. @ModelAttribute(“users”) tells spring to put the object returned by this method into users model attribute for the view. Since we have wired everything in xml configuration, if we return the object it will be rendered by default view which serializes beans into JSON strings.
Our method for receiving json objects looks like this:
@ModelAttribute ("user")
@RequestMapping (value = "/", method = RequestMethod. POST)
public User saveUser ( @RequestBody User user ) throws IOException {
logger. debug(user );
return user ;
}
@RequestBody annotation tells Spring framework to take request body and convert it into a bean using message converter. We have a single message converter defined in xml configuration file to be MappingJacksonHttpMessageConverter.
Conclusion
As you can see there is little code to write. In next Spring release the 2 bugs we fixed ourselves will probably be fixed, so this project will have only 2 source files, 3 xml files and 2 properties files. All the work is done by Spring framework, we just need to worry about the business logic and model.
Full project can be downloaded from here.
In the next part we will add flex project, so we can read the json object returned and post user object to the server.
UPDATE – October 27, 2009
Uncaught exception from servlet
java. lang. NullPointerException
at com. google. apphosting. runtime. security. shared. RuntimeVerifier. isInspectable(RuntimeVerifier. java:302)
at com. google. apphosting. runtime. security. shared. intercept. java. lang. Class_. getEnclosingMethod(Class_. java:237)
at org. codehaus. jackson. map. util. ClassUtil. isLocalType(ClassUtil. java:88)
at org. codehaus. jackson. map. deser. BeanDeserializerFactory. isPotentialBeanType(BeanDeserializerFactory. java:613)
at org. codehaus. jackson. map. deser. BeanDeserializerFactory. createBeanDeserializer(BeanDeserializerFactory. java:61)
at org. codehaus. jackson. map. deser. StdDeserializerProvider._createDeserializer (StdDeserializerProvider. java:248)
at org. codehaus. jackson. map. deser. StdDeserializerProvider._createAndCacheValueDeserializer (StdDeserializerProvider. java:181)
at org. codehaus. jackson. map. deser. StdDeserializerProvider. findValueDeserializer(StdDeserializerProvider. java:100)
at org. codehaus. jackson. map. ObjectMapper._findRootDeserializer (ObjectMapper. java:1069)
at org. codehaus. jackson. map. ObjectMapper._readMapAndClose (ObjectMapper. java:1002)
at org. codehaus. jackson. map. ObjectMapper. readValue(ObjectMapper. java:818)
...... SNIP ......
After deploying to app engine I found out that jackson json library is using some unsupported introspection calls and had to make a fix inside jackson library to make it run inside google app engine. I have updated the jars inside the project to catch the exception and let parser to continue on its way. Here is the fix:
public static String isLocalType (Class <?> type )
{
try {
// one more: method locals, anonymous, are not good:
if (type. getEnclosingMethod() != null) {
return "local/anonymous";
}
/* But how about non-static inner classes? Can't construct
* easily (theoretically, we could try to check if parent
* happens to be enclosing... but that gets convoluted)
*/
if (type. getEnclosingClass() != null) {
if (!Modifier. isStatic(type. getModifiers())) {
return "non-static member class";
}
}
} catch ( Exception exc ) {}
return null;
}
|
October 27th, 2009 at 12:16 am
Thanks!!!! :* :* :*
Waiting for P.2
October 27th, 2009 at 8:52 am
Hi Tomas
Have you tried your example on the the Google App Engine at Google?
I’ve have have own app that work on the dev server but when deployed to the App Engine at Google is does not work.
Get an error like this:
Nested in org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException:
java.lang.NullPointerException
at com.google.apphosting.runtime.security.shared.RuntimeVerifier.isInspectable(RuntimeVerifier.java:302)
at com.google.apphosting.runtime.security.shared.intercept.java.lang.Class_.getEnclosingMethod(Class_.java:237)
at org.codehaus.jackson.map.util.ClassUtil.isLocalType(ClassUtil.java:88)
at org.codehaus.jackson.map.deser.BeanDeserializerFactory.isPotentialBeanType(BeanDeserializerFactory.java:613)
at org.codehaus.jackson.map.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:61)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createDeserializer(StdDeserializerProvider.java:248)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCacheValueDeserializer(StdDeserializerProvider.java:181)
at org.codehaus.jackson.map.deser.StdDeserializerProvider.findValueDeserializer(StdDeserializerProvider.java:100)
at org.codehaus.jackson.map.ObjectMapper._findRootDeserializer(ObjectMapper.java:1069)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:1002)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:818)
at
And so on…
It might be that jackson uses some api that is not allowed.
Best Regards
Thomas
October 27th, 2009 at 10:28 am
You can grab the updated project with a fix. Yes, jackson was using unsupported introspection calls.
thanks,
Tomas
October 27th, 2009 at 5:08 pm
Blog is picking up some readers I see. Good job
November 5th, 2009 at 2:53 am
Hi Tomas! Thank you for the article. I have been wondering whether Jackson would work on App Engine, given constraints, since I may want to use this combination in future.
Also: it would be great to get your fix included in Jackson codebase; I could help with that. Jackson actually has some feature that is supposed to help with use cases like this (SerializationConfig.CAN_OVERRIDE_ACCESS_MODIFIERS, same for DeserializationConfig), but the problem so far has been that no one from dev team has been developing on app engine, so it has been difficult to test how well these work. Same applies to other limited platforms like Android as well.
Anyway: I will add a Jira entry for Jackson to point out this problem (http://jira.codehaus.org/browse/JACKSON-187).
Thank you for pointing it out!
November 10th, 2009 at 11:14 am
Thank you for the tutorial. I am trying to use your example to get REST working with Spring 3.0RC1 w/o Google. I tried to get the full project source as I could not get my code to work off the tutorial but the source is missing the source files and the XML files. Did I do something wrong? Thanks in advance and great job on the tutorial. I know I am close.
November 12th, 2009 at 9:15 am
Chris,
I checked, the source and all the configs are there in the zipped up project. Google app project is not a “web application” project in eclipse. If you are building regular web app, start with a new project wizard and add the required configurations and libraries from the downloaded one by hand.
November 13th, 2009 at 2:45 pm
Hi,
Is the zipfile broken? I can see the file but i get errors when i try to extract. The JSONView.java is what i want to see, if i copy the code fragment i get errors. I would like to get it working!
thx for the tutorial
November 15th, 2009 at 11:46 am
Erwin,
Yes something is wrong with the zip. I can open it only one of my laptops that has winzip.
I will upload updated zip Monday.
November 16th, 2009 at 11:13 am
I just uploaded new project zip. Old one had winzip only compression settings.
November 17th, 2009 at 11:32 pm
Just wanted to tell you thanks for all the great info found on your site, even helped me with my job recently
keep it up!
November 28th, 2009 at 3:26 am
FWIW, error with Jackson is due to GAE bug: http://code.google.com/p/googleappengine/issues/detail?id=2276