Goolge app engine + java + spring + REST + JSON + Flex – Part 1

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

Google App Engine Project Button

you can proceed to the next section.

Step Two – Create new project

Go to new project wizzard

new project wizzard

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

project step 1

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:

project step 2

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 warWEB-INFllib directory:

  1. org.springframework.asm-3.0.x.jar
  2. org.springframework.beans-3.0.x.jar
  3. org.springframework.context-3.0.x.jar
  4. org.springframework.core-3.0.x.jar
  5. org.springframework.expression-3.0.x.jar
  6. org.springframework.oxm-3.0.x.jar
  7. org.springframework.web-3.0.x.jar
  8. 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.

project step 3

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 {    
[/code]

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:

[code lang="java"]
@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;
}

28 thoughts on “Goolge app engine + java + spring + REST + JSON + Flex – Part 1

  1. 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

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

  3. 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.

  4. 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.

  5. 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

  6. I am using following method in Controller
    @ModelAttribute(“users”)
    @RequestMapping(value = “/GetList.json”, method = RequestMethod.GET)
    public @ResponseBody List getList() throws Exception{
    List users = new ArrayList();
    try {

    User user1 = new User();

    user1.setFirstName(“Vijayasekhar”);
    user1.setLastName(“Duvvur”);
    users.add(user1);

    User user2 = new User();

    user2.setFirstName(“Markus”);
    user2.setLastName(“Rogosinsky”);
    users.add(user2);

    } catch (Exception e) {
    return null;
    }
    System.out.println(“Exit GetList method ” + users);
    return users;
    }

    and i am invoking using RestTemplate as follows

    List list = template.getForObject(url, List.class);

    When i am trying to print list its collection of LinkedHashMap objects instead of User objects

    I am not understanding whats going on ..

  7. When I follow this tutorial I end up with running Jetty server – absolutely no error. But if I query 127.0.0.1:8888/api/user … or whatever, I get error page.

    So there isn’t listening anything. I also tried RESTClient to query … no success. Could you help me?

  8. Great blog post, thanks for sharing.

    I got the same error page as the post before me, it said ‘NOT FOUND’, using Eclipse Helios on Windows XP running JRE build 1.6.0_10-b33

    In my case it seemed to be related to how Spring starts up with logging. I added a line to the log4j.properties file to enable logging (to see what was wrong) and then it started working.

    I added this to my log4j.properties:

    log4j.rootLogger=debug,A1

  9. I tried to build your example (withnetbeans) and everything works fine in the local server, I get the right json on localhost:8080/api/user. I uploaded the app to GAE without errors but if I try to GET myapp.appspot.com/api/user it returns me a 404 Error: NOT_FOUND. Any idea of what I’m missing?

  10. Hmm it looks like your website ate my first comment (it was extremely
    long) so I guess I’ll just sum it up what I had written and say, I’m
    thoroughly enjoying your blog. I as well am an aspiring blog writer
    but I’m still new to everything. Do you have any suggestions for inexperienced blog writers?
    I’d definitely appreciate it.

Leave a Reply

Your email address will not be published. Required fields are marked *