Wet Feet – Online Marketing and Technology Blog

   Goolge app engine + java + spring + REST + JSON + Flex – Part 1.
 
Goolge app engine + java + spring + REST + JSON + Flex – Part 1
Written by Tomas, October 26th, 2009   

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 war\WEB-INF\llib 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 {

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; }
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • MySpace
  • Propeller
  • Slashdot
  • StumbleUpon
  • Yahoo! Buzz
  • Add to favorites
  • email
  • LinkedIn
  • Twitter
  • Yahoo! Bookmarks
  • Live
  • Reddit

12 Responses to “Goolge app engine + java + spring + REST + JSON + Flex – Part 1”
  1. Duong tuNo Gravatar Says:

    Thanks!!!! :* :* :*
    Waiting for P.2

  2. Thomas OlssonNo Gravatar Says:

    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

  3. TomasNo Gravatar Says:

    You can grab the updated project with a fix. Yes, jackson was using unsupported introspection calls.

    thanks,
    Tomas

  4. MarkNo Gravatar Says:

    Blog is picking up some readers I see. Good job

  5. Cowtown CoderNo Gravatar Says:

    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!

  6. ChrisNo Gravatar Says:

    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.

  7. TomasNo Gravatar Says:

    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.

  8. ErwinNo Gravatar Says:

    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

  9. TomasNo Gravatar Says:

    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.

  10. TomasNo Gravatar Says:

    I just uploaded new project zip. Old one had winzip only compression settings.

  11. Spyware Removal TipsNo Gravatar Says:

    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!

  12. Cowtown CoderNo Gravatar Says:

    FWIW, error with Jackson is due to GAE bug: http://code.google.com/p/googleappengine/issues/detail?id=2276


Leave a Reply



«     »