Creating a simple web service with Restlet

Restlet is a great, lightweight REST framework for Java developers. The only problem, there are not that many examples on how to use this framework available.
Some examples are included after installing the package under the src/org.restlet.example directory. But these examples are simple starting points and do not fully describe how to create a full REST web service with create, update and delete functionality.
I am using the 1.1 M4 version of restlet. The API documentation is available here.
The example code works but does not execute any database lookup. To make this example more useful, it is recommended to replace the :TODO lines with your database lookup.

First, create a simple User class. Which contains a variable id and name.

import java.nio.Buffer;

import org.json.JSONObject;

public class User {

private String id = null;
private String name = null;

/**
* @return Returns the id.
*/
public String getId() {
return id;
}

/**
* @param id
* The id to set.
*/
public void setId(String id) {
this.id = id;
}

/**
* @return Returns the name.
*/
public String getName() {
return name;
}

/**
* @param name
* The name to set.
*/
public void setName(String name) {
this.name = name;
}

/**
* Convert this object to a JSON object for representation
*/
public JSONObject toJSON() {
try{
JSONObject jsonobj = new JSONObject();
jsonobj.put("id", this.id);
jsonobj.put("name", this.name);
return jsonobj;
}catch(Exception e){
return null;
}
}

/**
* Convert this object to a string for representation
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("id:");
sb.append(this.id);
sb.append(",name:");
sb.append(this.name);
return sb.toString();
}
}


We also need some kind of error object to represent an error message. This is a very simple example


import org.json.JSONObject;

public class ErrorMessage {

public JSONObject toJSON() {
JSONObject jsonobj = new JSONObject();
try {
jsonobj.put("error", "An error occured");
return jsonobj;
} catch (Exception e) {
return null;
}
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("error:");
sb.append("An error occured");
return sb.toString();
}
}


Now, let's create our UserResource.


import org.restlet.Context;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.ResourceException;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;

public class UserResource extends Resource {

private User user = null;

public UserResource(Context context, Request request, Response response) {
super(context, request, response);
String userid = null;
userid = (String) getRequest().getAttributes().get("id");
this.user = findUser(userid);
getVariants().add(new Variant(MediaType.TEXT_PLAIN));
getVariants().add(new Variant(MediaType.APPLICATION_JSON));
}

/**
* Allow a PUT http request
*
* @return
*/
public boolean allowPut() {
return true;
}

/**
* Allow a POST http request
*
* @return
*/
public boolean allowPost() {
return true;
}

/**
* Allow a DELETE http request
*
* @return
*/
public boolean allowDelete() {
return true;
}

/**
* Allow the resource to be modified
*
* @return
*/
public boolean setModifiable() {
return true;
}

/**
* Allow the resource to be read
*
* @return
*/
public boolean setReadable() {
return true;
}

/**
* Find the requested user object
*
* @param userid
* @return
*/
private User findUser(String userid) {
try {
if (null == userid)
return null;
// :TODO {do some database lookup here }
// user = result of lookup
// This part should be replaced by a lookup
User u = new User();
u.setId("1");
u.setName("name");
// end replace
return u;
} catch (Exception e) {
return null;
}
}

/**
* Represent the user object in the requested format.
*
* @param variant
* @return
* @throws ResourceException
*/
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (null == this.user) {
ErrorMessage em = new ErrorMessage();
return representError(variant, em);
} else {
if (variant.getMediaType().equals(MediaType.APPLICATION_JSON)) {
result = new JsonRepresentation(this.user.toJSON());
} else {
result = new StringRepresentation(this.user.toString());
}
}
return result;
}

/**
* Handle a POST Http request. Create a new user
*
* @param entity
* @throws ResourceException
*/
public void acceptRepresentation(Representation entity)
throws ResourceException {
// We handle only a form request in this example. Other types could be
// JSON or XML.
try {
if (entity.getMediaType().equals(MediaType.APPLICATION_WWW_FORM,
true)) {
Form form = new Form(entity);
User u = new User();
u.setName(form.getFirstValue("user[name]"));
// :TODO {save the new user to the database}
getResponse().setStatus(Status.SUCCESS_OK);
// We are setting the representation in the example always to
// JSON.
// You could support multiple representation by using a
// parameter
// in the request like "?response_format=xml"
Representation rep = new JsonRepresentation(u.toJSON());
getResponse().setEntity(rep);
} else {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
}
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
}

/**
* Handle a PUT Http request. Update an existing user
*
* @param entity
* @throws ResourceException
*/
public void storeRepresentation(Representation entity)
throws ResourceException {
try {
if (null == this.user) {
ErrorMessage em = new ErrorMessage();
Representation rep = representError(entity.getMediaType(), em);
getResponse().setEntity(rep);
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return;
}
if (entity.getMediaType().equals(MediaType.APPLICATION_WWW_FORM,
true)) {
Form form = new Form(entity);
this.user.setName(form.getFirstValue("user[name]"));
// :TODO {update the new user in the database}
getResponse().setStatus(Status.SUCCESS_OK);
// We are setting the representation in this example always to
// JSON.
// You could support multiple representation by using a
// parameter
// in the request like "?response_format=xml"
Representation rep = new JsonRepresentation(this.user.toJSON());
getResponse().setEntity(rep);
} else {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
}
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
}

/**
* Handle a DELETE Http Request. Delete an existing user
*
* @param entity
* @throws ResourceException
*/
public void removeRepresentations()
throws ResourceException {
try {
if (null == this.user) {
ErrorMessage em = new ErrorMessage();
Representation rep = representError(MediaType.APPLICATION_JSON, em);
getResponse().setEntity(rep);
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return;
}
// :TODO {delete the user from the database}
getResponse().setStatus(Status.SUCCESS_OK);
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
}

/**
* Represent an error message in the requested format.
*
* @param variant
* @param em
* @return
* @throws ResourceException
*/
private Representation representError(Variant variant, ErrorMessage em)
throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.APPLICATION_JSON)) {
result = new JsonRepresentation(em.toJSON());
} else {
result = new StringRepresentation(em.toString());
}
return result;
}

protected Representation representError(MediaType type, ErrorMessage em)
throws ResourceException {
Representation result = null;
if (type.equals(MediaType.APPLICATION_JSON)) {
result = new JsonRepresentation(em.toJSON());
} else {
result = new StringRepresentation(em.toString());
}
return result;
}
}


And implement a restlet server, listening on port 8100.


import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.StringRepresentation;

public class WebServiceApplication extends Application {

public static void main(String[] args) throws Exception {

// Create a component
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8100);

WebServiceApplication application = new WebServiceApplication(
component.getContext());

// Attach the application to the component and start it
component.getDefaultHost().attach(application);
component.start();
}

public WebServiceApplication() {
super();
}

public WebServiceApplication(Context context) {
super(context);
}

@Override
public Restlet createRoot() {

Router router = new Router(getContext());
router.attach("/users", UserResource.class);
router.attach("/users/{id}", UserResource.class);

Restlet mainpage = new Restlet() {
@Override
public void handle(Request request, Response response) {
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.append("<html>");
stringBuilder
.append("<head><title>Sample Application Servlet Page</title></head>");
stringBuilder.append("<body bgcolor=white>");

stringBuilder.append("<table border=\"0\">");
stringBuilder.append("<tr>");
stringBuilder.append("<td>");
stringBuilder.append("<h1>2048Bits.com example - REST</h1>");
stringBuilder.append("</td>");
stringBuilder.append("</tr>");
stringBuilder.append("</table>");
stringBuilder.append("</body>");
stringBuilder.append("</html>");

response.setEntity(new StringRepresentation(stringBuilder
.toString(), MediaType.TEXT_HTML));

}
};
router.attach("", mainpage);
return router;
}
}


You can test the restlet web service with any internet browser except for the PUT and DELETE requests. I prefer curl to test a Restful web service.

GET REQUEST - Show the information of a user:

curl http://localhost:8100/users/1

POST REQUEST - Create a new user:

curl -d "user[name]=John" http://localhost:8100/users

PUT REQUEST - Update an existing user:

curl -X PUT -d "user[name]=Jane" http://localhost:8100/users/1

DELETE REQUEST - Delete an existing user:

curl -X DELETE http://localhost:8100/users/1

10 Comments:

  1. Yash said...
    A nice article demystifying the development of Restlet based Web Services
    Regards
    Bharathkumar V S said...
    Its easy to understand,
    Thanks
    Brandon said...
    Thanks for posting this! As you mentioned, the examples at the Restlet site really aren't much of a help to write a simple webservice-- this is perfect.
    Blogger said...
    Hi there. Let me add my thanks for this page.
    Anonymous said...
    nice article .. thanks a ton!!
    Rajul said...
    This is great.

    But I am having problems running it standalone when I use attach instead of attachDefault.

    If using attach my resource never gets called. This works fine when running in Tomcat.

    Appreciate any help,
    Rajul
    Rajul said...
    I am using restlet 1.1.5
    Anonymous said...
    This post has been removed by a blog administrator.
    mihir said...
    Can you provide an example of how to configure uri and resource in restlet.xml and use it in application class.

    Help will be greatly appreciated
    Philippe Van Nuijs said...
    @mihir

    Look at the Component class, especially the constructor with the reference to a XML configuration file.

    Hope this helps.
    Philippe

Post a Comment



Newer Post Older Post Home