Consume REST webservices in java using rapa

24 01 2009

I have been reading about REST webservices for quite a while now. But rails still seems to have the best support for consuming REST webservices (ActiveResource). It is magical the way active resource works. Even if the magical and dynamic behavior may not be completely possible in java, it would be helpful if we have basic support for accessing REST webservices.

A few options which immediately pop up on a search would be restlet, apache cxf and jersey (reference implementation of jsr 311). But they are not as easy to use as ActiveResource and not very object oriented. The motivation behind rapa is to fill this gap. In this blog I will take you through creating a simple REST webservice with rails and then use rapa to consume it in an object oriented approach.

Rapa uses the tried and tested Jakarta Commons HttpClient and JAXB. It helps you in consuming REST webservice easily by taking care of the grunt work (making connection to the webservice, transporting data etc). All that said, its time for some code.

Prerequisites for this example:

If you are in a hurry, you can download the sample application code here.

If you want to get your hands dirty building the rails rest webservice and the rapa client yourself, below are the steps.
Firstly create the REST webservice in rails (which we will later consume in java).

rail rest --database=mysqlcd rest

ruby script/generate model customer id:number name:string

ruby script scaffold customer

then edit the database.yml file according to your database settings.

rake db:create
rake db:migrate

gentlemen start your server

ruby script/server

now access your rest webservice by pasting the below url in a browser
http://localhost:3000/customers.xml

<customers type="array">
</customers>

Since there is no data yet you will see only so much.
Now let us consume this webservice with rapa.
Download rapa. You will also need the below supporting jars.

Create a project with your favourite ide. Create a POJO class called Customer (or any name your think appropriate). This class has to implement the org.rest.rapa.resource.Resource interface.

/**
 * Purpose: Value object that represents the RestAPI
 */

import org.rest.rapa.resource.Resource;

public class Customer implements Resource {
	private String name;
	private int id;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

}

Instanciate the org.rest.rapa.RestClientWrapper class as shown below.

RestClientWrapper restClientWrapper = new RestClientWrapper(
"http://localhost:3000/customers", "", "", "localhost", 9000);

The first parameter is the base url of the REST webservice. The second and third parameters are the username and password (In this case they are emty because our sample rails application does not require authentication). The fourth parameter is host name and fifth parameter is the port number.

Now we are ready use the webservice to create, read, update and delete with rapa.

Create

Customer customer = new Customer();
customer.setName("Hari");
restClientWrapper.save(customer);

Read

customer = (Customer) restClientWrapper.getById(1, Customer.class);
System.out.println(customer.getName());

Update

customer = new Customer();
customer.setName("rapa");
customer.setId(1);
restClientWrapper.update(customer);

Delete

customer = new Customer();
customer.setId(1);
restClientWrapper.delete(customer);

Rapa throws a checked exception called org.rest.rapa.RestClientException if an operation was not successful.

All the above operations in the example can be verified by looking at the rails log. For example the create method would result in the below log generated by the rails server.

Processing CustomersController#create (for 127.0.0.1 at 2009-01-24 23:20:16) [POST]
Session ID: fda551698b0faca8bed707f7148fe3de
Parameters: {"format"=>"xml", "action"=>"create", "controller"=>"customers", "customer"=>{"name"=>"Hari", "id"=>"0"}}
←[4;36;1mSQL (0.000000)←[0m   ←[0;1mSET NAMES 'utf8'←[0m
←[4;35;1mSQL (0.000000)←[0m   ←[0mSET SQL_AUTO_IS_NULL=0←[0m
←[4;36;1mCustomer Columns (0.031000)←[0m   ←[0;1mSHOW FIELDS FROM `customers`←[0m
←[4;35;1mSQL (0.000000)←[0m   ←[0mBEGIN←[0m
←[4;36;1mCustomer Create (0.000000)←[0m   ←[0;1mINSERT INTO `customers` (`name`, `updated_at`, `created_at`) VALUES('Hari', '2009-01-24 17:50:16', '2009-01-24 17:50:16
')
←[0m
←[4;35;1mSQL (0.031000)←[0m   ←[0mCOMMIT←[0m
Completed in 0.20300 (4 reqs/sec) | Rendering: 0.14100 (69%) | DB: 0.06200 (30%) | 201 Created [http://localhost/customers.xml]

Enjoy consuming REST webservice in java.

This project is still in very initial stages. Please send me your feedback.

Update: Please take a look at the latest improvements at github. The documentation has been updated as well.


Actions

Information

14 responses

27 01 2009
6 02 2009
John Yeary

That is a pretty slick demo. The piece I really like is that you do not have a ton of annotations all over the Customer class file to use the service.

11 02 2009
KatieMac

What an excellent idea. I have been looking for a way to use a Rails-based RESTful backend with a Java frontend. Rapa is what I am looking for.

If possible, could you post an example using Basic or Digest Authentication?

Thanks.

28 02 2009
Luciano

Hi!

Congratulations! This is a excelent tutorial, but I have a question, see:

If you like consuming Twitter Rest services, this works?
So, I tried make one similar class, but, no way… The get method (getById) is limited.

Sorry, maybe I not had understand that your sample.

Thanks in advanced.

2 03 2009
harikrishnan83

Thanks for your comments. Can you please send me the additions to rapa that would make your job easier. I would be happy to add support.
Kindly let me know if you would like to contribute any code changes.

Regards,
HariKrishnan

21 04 2009
Andy Scott

Hi,

Is it possible to collect and unmarshall a group of items, rather than each individually.
The first thing I usually do is retrieve a collection before examing each one for updates etc.

Thanks,
Andy

21 04 2009
harikrishnan83

Great idea. But unfortunately it is not part of the current version.
It is on our high priority list. Soon you can see this feature available in our next releases.

You are welcome to give us more feedback.

4 06 2009
Chris Hubbard

Does this work on J2ME? I am interested in something for the BlackBerry.

7 06 2009
harikrishnan83

Theoretically, it should work. I have not used it with J2ME.
It would be great if you could give me some feedback on the issues you face while using with J2ME.

16 06 2009
Sam Sabey

Hey,

I like this, it’s making RESTful on Java in the right direction. However, I feel that the getById method is quite limiting, in so far as no good system want’s to expose it’s internal id’s, usernames and passwords to the world.

Have you thought about 0auth and token based authentication and identification? It would be awesome to be able to just call the restful resource e.g.

http://host:port//resource/object_identification.format

and get back the json rendered as nice pojos, complete with the necessary method, get, put, delete and post.

Now that would be rocking along rails.

Don’t try to be to much Active Resourcey – it’s a relatively crap thing once you move beyond the hello world demo. It’s predicated on exposing your whole model practically verbatim, doesn’t really do collections (useful for a transactional service) and it’s performance is, well bad bad. Not the sort of thing the twttr uses on their back end.

Keep up the great work!

Sam.
@samotage

16 06 2009
harikrishnan83

Thanks for the comment. I absolutely agree with you on the limiting nature of getById. I am trying to work towards a more generic approach, where you could get a collection of resources which match given set of search parameters.

Currently I am just making use of simple authentication. Service authentication with Oauth would be the right direction. I will definitely mark these as issues on our project.

Appreciate the suggestions.

Thanks,
Hari

23 11 2009
Tola

Hello,

I have a RESTful web service created in Rails that I’m trying to consume using Rapa. Great tool! However, I get the following error:

Exception in thread “main” java.lang.NoClassDefFoundError: javax/xml/bind/JAXB
at org.rest.rapa.RestClientCore.getById(RestClientCore.java:139)
at org.rest.rapa.RestClientWrapper.getById(RestClientWrapper.java:85)
at EmailServiceConsumer.main(EmailServiceConsumer.java:11)

From doing a Google search, it appears that this class is contained in Oracle’s JAXB jar (jaxb-api.jar); but we’re using MySQL, and I can’t seem to get this jar file from anywhere else. The tutorial does not mention that this jar file is needed. I’m contacting you to find out if you could provide guidance on how to proceed? Thanks!

23 11 2009
Tola

Never mind!…Just discovered that the class is available in JDK 1.6 and I was using JDK 1.5…I guess Rapa is not compatible with versions of Java that are earlier than 1.6? Thanks!

26 11 2009
harikrishnan83

Apologies for the delay in reply. I have faced this issue before as well. We are trying to improve the documentation.
Please do mail me or post on the blog if you need any help with rapa.

Leave a reply to Tola Cancel reply