Saturday, August 8, 2009

Restful Service on Google App Engine

I have been playing around with GAE(Google App Engine) and Android recently, and as part of my experimenting I wanted to to be able to call a service running on GAE from Android. I found a great library called Restlet. Restlet provides a simple framework and api for creating and consuming rest services, it even includes libraries for GAE and Android so I thought I would give it a shot. I am going to write a series of posts that will contain a basic GAE application for a rest service, an Android application to consume the Restful service, and finally I will write a post that shows a simple example of writing some information to the GAE datastore. My example will be target toward providing a simple solution to a question on stackOverFlow.

For part one I will go over an example for creating a restful service using the Restlet api. I will be using the eclipse with the GAE plugin installed. In eclipse create a new Google web application project, and enter the project details, I unchecked the use GWT box, as I am not using that. This will create a basic servlet project, we will not use the servlet that is created, but it sets up the basic project structure for us. Next download Restlet from here, and add the org.restlet.gae.jar to the war/WEB-INF/lib folder of your project, and make sure it is in the classpath. We are now ready to add Restlet functionality to our project.

This example will use two classes, so create two classes, I have created TestApplication and TestResource. As you can see below. Test application extends Application, and here we will create our Restlet to direct calls to. The TestResource extends ServerResource. This class allows us to tie our rest services into other logic, such as accessing the datastore or other various config. In this class I have created two methods, represent, and accept representation. These will handle the associated Post and Get requests marked by their corresponding annotation. Take notice of the @Get annotation on the represent method, I have specified that I will return type of "xml". And then looking at the body of the represent method, I create a simple xml document, and populate and create a date element populated with today's date. This date could be hard coded in the class or retrieved from the datastore, but this shows the basic idea of returning the date from a restful service. Take a look at the full classes below.

TestApplication.java

import org.restlet.Application;
import org.restlet.Restlet;
import org.restlet.routing.Router;

public class TestApplication extends Application{

/**
* Creates a root Restlet that will receive all incoming calls.
*/
@Override
public synchronized Restlet createRoot() {
// Create a router Restlet that routes each call to a
// new Resource
Router router = new Router(getContext());

router.attachDefault(TestResource.class);
return router;
}
}


TestResource.java

import java.io.IOException;
import java.util.Date;

import org.restlet.data.MediaType;
import org.restlet.ext.xml.DomRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class TestResource extends ServerResource{

@Get("xml")
public Representation represent(){
// Generate the right representation according to its media type.
try {
DomRepresentation domRep = new DomRepresentation(MediaType.TEXT_XML);
// Generate a DOM document representing the list of
// items.
Document d = domRep.getDocument();
Element r = d.createElement("expire_date");
d.appendChild(r);
Element date = d.createElement("date");
date.appendChild(d.createTextNode(new Date().toLocaleString()));
r.appendChild(date);
d.normalizeDocument();

// Returns the XML representation of this document.
return domRep;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

@Post
public Representation acceptRepresentation(Representation entity){
//Could process some post requests here and possibly write to the datastore
return null;
}
}


The only thing left to do now is configure the web.xml to route to our servlet correctly. Open war/WEB-INF/web.xml, and delete the generated web-app config tags and add the following. Here we are intercepting all requests for simplicity, but these could be changed, and routing everything to our test.TestApplication class, change this to whatever you named your application class above.

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<display-name>first steps servlet</display-name>
<!-- Application class name -->
<context-param>
<param-name>org.restlet.application</param-name>
<param-value>
test.TestApplication
</param-value>
</context-param>

<!-- Restlet adapter -->
<servlet>
<servlet-name>RestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
</servlet>

<!-- Catch all requests -->
<servlet-mapping>
<servlet-name>RestletServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>


That is it, you can now run this with the embedded server provided by the GAE eclipse plugin by running your application as a google web application, and directing your browser to http://localhost:8080/. You should see the xml returned by the request with today's date. When you are ready you can deploy the application to the app server and access it from your appspot address. In my next post I will go over how to consume this request from an android application.


****UPDATE(8-10-09)*****
I have been informed that some distributions of restlet do not include the app engine jar, it should be noted that this is only available on Version 2.0, and Milestone 3 according to the release notes. If you go here and download restlet-2.0m3.zip, it should contain the jar file.

13 comments:

Abhishek said...

Hey...
I downloaded http://www.restlet.org/downloads/2.0/restlet-gae-2.0m5.zip and am unable to locate the org.restlet.gae.jar anywhere. I tried downloading other archives from http://www.restlet.org/downloads/2.0/ however no luck yet to locate this jar. can you give me the exact download link for the RESTlet package you're using....
Am pretty new to Java so forgive any nomenclature. Not sure if it should be called archive or package :).

broschb said...

@Abhishek, if you go to the download link I provided in the post, and download 2.0m3.zip it will contain the correct jar file. You can find it here, http://www.restlet.org/downloads/2.0/restlet-2.0m3.zip. I'm not sure why all the distributions do not include this jar.

Philippe Meyer said...

In fact packaging policy has changed on 2.0m4. Spécific version are built for specific targets. More details in : http://blog.noelios.com/2009/08/06/restlet-2-0-m4-released/

This means that you should download the gae version and use the package org.restlet.jar which no longer post fixed by the platform

Hope this helps !

broschb said...

@Philippe Thanks, I saw that they had different builds, but was confused by them since the jar files were all named the same. Thanks for clearing that up.

Qiss said...

HI, I've done every step by your post, And modify the web.xml. But while I browser to http://localhost:8080/. I only saw the "Hello Word".

I guest that the service is not router to TestApplication. But I don't konw how to fix it. Can you give me a advice?

broschb said...

@Qiss, I would check your web.xml to make sure you are routing the request correctly.

Virtual ALT.NET said...

I'm getting Couldn't write the XML representation: Provider org.apache.xalan.processor.TransformerFactoryImpl not found

Any idea why I would be getting this?

LostInEurope said...

For version 2.0
Add in lib directory: org.restlet.jar, org.restlet.ext.xml.jar and org.restlet.ext.servlet.jar

And don't forget in xml param value needs to be your package com.XXX.XXX.TestApplication

It works for me, thx to author

LostInEurope said...

For version 2.0
Add in lib directory: org.restlet.jar, org.restlet.ext.xml.jar and org.restlet.ext.servlet.jar

And don't forget in xml param value needs to be your package com.XXX.XXX.TestApplication

It works for me, thx to author

Manaswita said...

I cant get it to work. Following is the exception I get on running the application on my local server.

java.lang.NoSuchMethodError: org.restlet.engine.http.HttpServerHelper.handle(Lorg/restlet/engine/http/ServerCall;)V

Balazs Vamos said...

If you are using Restlet 2.1, override createInboundRoot() instead of createRoute().

Nicola Blaine said...

I get an 405 Method Not Allowed error for the POST method. Do u actually have a working example of a POST method in a resource? I am using version 2.1 Restlet and I seen countless articles of others saying they get the same error. The bug was apprently fixed but people are still getting the error.

I read that you must allow the resource permission to execute Post, put etc. Do u have a working example of this?

Thanks

Nicola Blaine said...

I get an 405 Method Not Allowed error for the POST method. Do u actually have a working example of a POST method in a resource? I am using version 2.1 Restlet and I seen countless articles of others saying they get the same error. The bug was apprently fixed but people are still getting the error.

I read that you must allow the resource permission to execute Post, put etc. Do u have a working example of this?

Thanks