Search This Blog

Friday, April 25, 2008

REST Web Service Example with Restlet, Spring and Maven...

Time for me to blog something of technical interest again. Ever since I have
joined Overstock.com, I have found myself working with two new technologies, REST and
MAVEN. By no means do I consider myself an expert on the same but feel that
sharing some of my experience might prove beneficial to someone else.

A Restlet 2.0 Example is available in a later POST I would however recommend a read of this one before proceeding to the 2.0 example.

Clearly, I cannot share any OSTK proprietary work...

First some words about Maven. I have always wanted to use Maven even during
my career at PRFT. Sadly, I didn't get to it. Maven is not another ANT, it
so much more. Ever since I have started using Maven, I cannot find myself
not using it when possible.

Noting a few maven terms (some coined by me others maybe not ;-)):

1. Maven Repository:

A Maven repository is a public/private site that hosts different libraries
or jars.

2. Artifact Coordinates:

Every jar in a Maven repository can be identified by coordinates. For example,
if one is trying to find spring-2.5.1.jar, the artifact is represented as:

<groupid>org.springframework</groupid>
<artifactid>spring</artifactid>
<version>2.5.1</version>


Think of group Id as being a top level folder that aggregates multiple artifact
types. An artifact Id describes a particular artifact in the group and the
version helps identify the version of the artifact.

3. Local Repository:

Maven downloads artifacts and places them in your $HOME/.m2 folder. Thus, you
always maintain a copy of the jar, i.e., a nicely shelved away via version. Thus the jar will not
be downloaded all the time but only once.

4. POM:

The foundation of maven lies in what is known as a POM or
Project Object Module. When using Maven, you will often find files titled
pom.xml. These pom files describe your project for you. A Maven project
can serve as an 'Aggregator' project that serves to group children project
under one umbrella. These project are typically called "Multi-Module" projects.

5. Maven Eclipse Plugin:

I use the Q4E Maven Eclipse plugin. Its pretty nice but not yet perfect. With the plugin, I am able to import the different maven projects into eclipse. The visual dependency management feature of this plugin is awesome.

6. Maven Archetype:

An Archetype is a template for creating projects in its likeness. There are different archetype available, for example an archetype to create a J2EE EAR project, a Web Project, a simple java project and App fuse projects as well.

Enough about Maven, a bit about REST!

I am not planning on entering into a REST versus SOAP or what is REST
discussion here but merely wanting to demonstrate a Web Service that uses
REST and Maven.

The stir created by Roy Fieldings dissertation has opened multiple discussions
and thoughts regarding how the Web should be viewed.

An implementation of REST concepts in Java was facilitated by the RESTLET project pioneered by Jérome Louvel.

One of the core classes of the RESTLET framework is the Resource class. Resources tend to represent exactly what their name means, a resource! For example, an Order Resource that
represents Order related data, a Product Resource that represents Product
information etc. I would like to talk more about REST and the RESTLET project
but the posting would be too verbose...maybe a later post.. One can get a good understanding of the RESTLET project via their documentation and starter examples.

Onto the example:

The example demonstrates a simple Restful Web service developed
using Restlet + Spring Framework + JaxB + JSON + Maven + Dozer + JUnit + EasyMock.

The project is organized as follows:


SpringTest
.
-- client

-- common

-- integration-test

-- service

`-- webapp


1. Spring Test:
The Web Service project is a multi module Maven Project. The SpringTest project
is the root level aggregator project that aggregates the modules such
as common, client, service, integration-test and webapp.

2. common:

The common project contains the code that is shared by other modules
of the web service project. It contains the DTO (data transfer objects), common
exceptions, utilities etc.

3. client:

The client project represents the Webservice client code. This project is
pretty much a wrapper around the Restlet API so that consumers of the service
have an easy way to communicate with the web service without having to
concern themselves with specifics of the web service.

4. service:

The service project represents the heart of the web service. The
service is not mixed in with the webapp project as the business logic is
not necessarily tied to RESTLET or REST. If required in the future, a
SOAP or EJB version of the web service could be exposed by re-using the code
from the service project.

The domain model is included in the service project itself. The reason for
the same is; in many cases the model and the service go hand in hand. Now, it
could be argued that the model could be placed in a separate maven module for
re-use. That IMO should be the direction should the model appear to be
re-usable. Also note that the model need not necessarily translate to what
is transferred via the web service. I am therefore making a separation of the
model and the DTO. There is clearly no one size fits all here and depending
on the scope of the project, making the model and the DTO one and the same
might be an option.

5. integration-test:

The project contains a test which will perform end to end integration test.
When an integration test is run, a Jetty Server instance is started using
Cargo and the webapp is deployed onto the server. Then the integration test
is performed which will communicate with the server to assert that the
web service contract is valid and the server will ensure that it performs
as expected. My reason for using Cargo is that one could easily switch to a
different container for testing if required.

Note that one could have additional integration test projects defined when the service
undergoes an upgrade and there is a need to maintain backward compatibility with the Web Service clients.

What does the example do?
The example is a simple mock of an order system. One can get a list of
Products supported by using the Product Service. The Product Service
uses JSON for its transport (just to demonstrate). The Order Service
can place an order, update an order, query an order and delete and order.
The Order Service uses JAXB for marshaling its data.

Standard REST concepts like POST, PUT, GET and DELETE are explored by the
example. I don't believe that I have 100% conformity with REST concepts in the example :-)

The example is a 'simple' one in all senses of the word. Thread safety,
exceptions etc are not even considered. One could easily incorporate the
same if developing a real world web service.

The Project uses Spring as a DI container. Rest Resources are injected with
dependencies using Spring auto-wiring.

Running the example:

1. JDK 1.5.X or Greater:
Ensure you have jdk1.5.X+ installed. The code uses annotations extensively.
From your command line, when issuing a "java -version" make sure that
it is greater than or equals to 1.5.X.

2. Maven Install:

Download Maven and install it. If on a *Nix environment, install maven at
/opt/. Ensure that maven is available in your PATH. What this means is if
you execute a "mvn -version" you should see details of your installation. Additionally, ensure you obtain a 2.0.x version of maven.

3. Eclipse Install:

Download and install an eclipse distribution. Eclipse is not required to run the examples but helps in viewing editing the code.

4. Q4E Maven Plugin:

Open eclipse and install the Q4E Maven plugin. Note that you might need to point the plugin to your maven installation.

5. Download Example:

Download and extract the SpringTest.zip project using your favorite archiving tool. The example has been tested with Restlet 1.1-M4 and Java Version 1.5.X and 1.6.X. If you would like to use a version that uses the API's from Restlet 1.1-M2, then the same can be obtained from HERE.

6. Import into Eclipse:
Import the modules into eclipse as Maven projects. Again this step is optional. It is required if you would like to view the project.

7. Installing Modules:
From the command line, issue a "mvn install" from within the
SpringTest project. What this command does is installs the maven modules in your local repository. As part of the process it will execute JUnit tests and integration tests.

8. An Integration Test:

Run a "mvn integration-test" from the SpringTest folder to see the
integration test in action.

8. Debugging the Web App:
Alternatively, you can watch the project in action by starting jetty
from the webapp project and debugging via remote server attach.

You will need to start jetty from the webapp project using "mvn jetty:run" and then do a remote server attach from eclipse. Ken Steven's blog has a pretty nice description as to how to do the same.

If you have difficulties executing the project, ping me...;-).. Once again the code for the example is available HERE.

The very same example that uses JAXRS (JSR 311) is described HERE.

14 comments:

Anonymous said...

Hi, I could not download the example, looks like a broken link.

Sanjay Acharya said...

Hello there, yeah sorry my hosting provider discontinued my account, I have moved the code to a different place. It should be available.

Unknown said...

I can't find the 1.1-M2 version of the restlet jars from the pom.xml for common -- what 1.1 version ID should I use?

Unknown said...

I figured out that I needed a repository element in order to get the restlet jars. But now I find that the integration-test folder is incorrect. It is referring to Persons rather than Orders. Also, the client folder has an empty .java file: OrderClient.java. Would appreciate an updated download that has all of these things in it.

Sanjay Acharya said...

Jim, I will need to update the code, looks like when my old repo went down, I uploaded a bad zip, old ver. Will try to get one soon. Anyway, regarding the Restlet version, you should be able to use the 1.1-M4 version released.

Sanjay Acharya said...

I have uploaded the correct zip file. Things should work now. Let me know if you still have issues. As an FYI, I am using 1.1-M2 still with maven 2.0.8 and jdk1.6.X.

Unknown said...

Thanks --

The new zip file fixes the big errors I found.

Using Java 6 and the 1.1M2 restlet release, things build cleanly from the zip.

If I change the parent pom to target java 5, I have to add some jaxb dependencies in the client pom:

jax.xml.bind:jaxb-api:2.1
com.sun.xml.bind:jaxb-impl:2.1.3

And in the ProductClientImpl implementation, I must remove an overrides annotation for getProducts

And then the project runs the integration test cleanly for Java 5.

Unknown said...

I do find that the build fails when I try to use 1.1-M4 in place of 1.1-M2 for the restlet stuff. I replaced the version in the 2 places I saw: in the parent pom and in the client pom.

The error is:

Caused by: java.lang.ClassNotFoundException: org.restlet.ext.spring.SpringApplication
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)

Sanjay Acharya said...

Yes running java5, you would need those dependencies as they are included implicitly in java 6. With 1.1M4, one area of failure would be the JaxbRepresentation. Better to use the one from 1.1 M4 moving forward with providing an ObjectFactory. Not sure regarding why the Spring error is occuring. I would check to see if the spring-ext jar contains the class mentioned with the 1.1M4. If it does not, Restlet have changed api. If present, I need to debug.

Thanks.

Sanjay Acharya said...

Example has been upgraded to use Restlet-1.1M4. Also 1.5.X and 1.6.X versions of JDK should work with the example.

Mateu said...

Hola,

Thank you for this helpful tutorial.

Please forgive a newbie question. Could you give a little more detail on step 6, importing the project into Eclipse?

So far, I have tried importing the "SpringTest" directory, which failed when done in the order you describe. I tried importing the 4 next-level directories, "client", common", "service", "webapp", but this resulted in many errors about missing types, especially the Order, LineItem and OrderDTO types. How does one import the project into Eclipse correctly?

thanks,
Mateu

Sanjay Acharya said...

Its been some time since I tried this example. Anyway, first make sure you have Q4E installed (http://code.google.com/p/q4e/) in eclipse.

In eclipse open up the "Import" project wizard to select "Import Maven 2 project" and select each project and import it in.

One additional step, ensure that the source version for the projects are set as 1.5 atleast or it will not compile.

Mateu said...

Hi Sanjay,

Thanks! I don't know what I did wrong, but on the second try, the import went perfectly.

The only change I had to make after that was to the port the web app was using, in its pom.xml file. In case anyone else has this problem, the webapp is using 9090. You can use netstat -aop (ideally as root) to see what programs are using what ports. Just choose one that isn't in use.

adeu,
Mateu

Sanjay Acharya said...

Hi Mateau,

Glad that it worked out. Ofcourse, this example is ages old. Restlet is way ahead and I don't know how the current app behaves when upgraded...:-)

regards.