Search This Blog

Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Friday, April 17, 2015

Reactive Programming with Jersey and RxJava

Introduction


I have the reactive programming fever right now and am having fun using RxJava (Reactive Extensions for Java).  When I found out about reactive support for Jersey, I figured a BLOG might be warranted.

Problem Domain

It's kinda nice to have problem domain. In our case company Acme had a monolithic retail web app application that sold products. There were many modules composing the monolith such as CheckOut, Account details, etc all contributing to a single deployable. One module among them was Product which had had core product information, pricing information and inventory that were obtained from a database.



The solution worked really well at first. However, over time, the company felt the following pain points -
  • One Deploy thus One Rollback - All or nothing
  • Long build time and testing
  • Information such as price, reviews etc were needed elsewhere as there was some logic associated with these thus having it centralized for consumption would be nice.
  • Coordination nightmare among teams working on different areas to make sure that all came together.
The Architects  decided to extract the concept of Product into separate services.  Over time, the company created separate web services for the base product, inventory, reviews and price. These were aligned with the different teams working on the respective areas and allowed for each area to evolve and scale independently.



All the web service calls were written using JAX-RS/Jersey. The company had totally adopted the micro services bandwagon.  A product would be created for the Web Site by calling the different services and aggregating the result as shown below  -

The following is a sample code that uses just one jersey client that demonstrates the above :
   @GET
   public void getProduct(@PathParam("id") Long productId,
    @Suspended final AsyncResponse response) {
    try {
      // Get the Product
      BaseProduct baseProduct = client.target(serviceUrls.getBaseProductUrl()).path("/products")
          .path(String.valueOf(productId)).request().get(BaseProduct.class);

      // Get reviews of the Product
      Reviews reviews = client.target(serviceUrls.getReviewsUrl()).path("/productReviews")
          .path(String.valueOf(productId)).request().get(Reviews.class);

      // Create a Result Product object - Base Product object does not have price and inventory
      Product resultProduct = resultProductFrom(baseProduct, reviews);

      // Obtain the Price for Result Product
      resultProduct.setPrice(client.target(serviceUrls.getPriceUrl()).path("/productPrice")
          .path(productId.toString()).request().get(ProductPrice.class).getPrice());

      // Get Price and Inventory of Product Options and set the same
      for (Long optionId : optionId(resultProduct.getOptions())) {        
        Double price = client.target(serviceUrls.getPriceUrl()).path("/productPrice")
            .path(optionId.toString()).request().get(ProductPrice.class).getPrice();

        ProductInventory inventory = client.target(serviceUrls.getInventoryUrl())
            .path("/productInventory").path(optionId.toString()).request()
            .get(ProductInventory.class);

        ProductOption option = resultProduct.getOption(optionId);

        option.setInventory(inventory.getCount());
        option.setPrice(price);  
      }

      response.resume(resultProduct);
    }
    catch (Exception e) {
      response.resume(e);
    }
  }

ACME soon found that calling these services had a performance degradation compared to when they were a monolithic product service that obtained all product information using a database operation.The performance degradation was primarily attributed to the serial nature in which the requests were being invoked and results subsequently composed not counting the fact that a database join among disparate tables was far more performant. As an immediate fix, the direction from the Architects was to ensure these services were called in parallel when appropriate -
  • Call the Product Service to obtain core product data serially - You need base product information and its options
  • Call the Review Service asynchronously
  • Call the Pricing Service asynchronously to obtain the price of the Product and Options
  • Call the Inventory Service asynchronously to obtain the inventory of the different Product Options
The above worked and things were more performant, however, the code looked a mess due to the different CountDownLatches, composition logic and Futures they had in play. The Architects met again, hours were spent locked in a room while pizza's where delivered under their door until they heard of Reactive Programming in general and Reactive Jersey with RxJava in particular! Bling, Bling, bulbs coming to light illuminating their halo's and away they go reacting to their new idea. They found that Reactive Jersey promotes a clean API to handle parallel execution and composition while not having to worry about Countdown latches and the likes. The resulting code developed looked like -
  
 @GET
  public void observableProduct(@PathParam("id") final Long productId,
    @Suspended final AsyncResponse response) {

    // An Observable of a Result Product from 
    Observable<Product> product = Observable.zip(baseProduct(productId), reviews(productId),
      new Func2<BaseProduct, Reviews, Product>() {

        @Override
        public Product call(BaseProduct product, Reviews review) {
          return resultProductFrom(product, review);
        }
      });

    // All Product
    Observable<Long> productIds = productAndOptionIds(product);
    
    // Observable of Options only
    Observable<Long> optionIds = productIds.filter(new Func1<Long, Boolean>() {

      @Override
      public Boolean call(Long prodId) {
        return !prodId.equals(productId);
      }

    });
    
    // Set Inventory Data
    product
        .zipWith(inventories(productId, optionIds).toList(), new Func2<Product, List<ProductInventory>, Product>() {

          @Override
          public Product call(Product resultProduct, List<ProductInventory> productInventories) {
            for (ProductInventory inventory : productInventories) {
              if (!inventory.getProductId().equals(resultProduct.getProductId())) {
                resultProduct.getOption(inventory.getProductId())
                    .setInventory(inventory.getCount());
              }
            }
            return resultProduct;
          }
        })
        // Set Price Data
        .zipWith(prices(productIds).toList(), new Func2<Product, List<ProductPrice>, Product>() {

          @Override
          public Product call(Product resultProduct, List<ProductPrice> prices) {
            for (ProductPrice price : prices) {
              if (price.getProductId().equals(resultProduct.getProductId())) {
                resultProduct.setPrice(price.getPrice());
              }
              else {
                resultProduct.getOption(price.getProductId()).setPrice(price.getPrice());
              }
            }
            return resultProduct;
          }
        }).observeOn(Schedulers.io()).subscribe(new Action1<Product>() {

          @Override
          public void call(Product productToSet) {
            response.resume(productToSet);
          }

        }, new Action1<Throwable>() {

          @Override
          public void call(Throwable t1) {
            response.resume(t1);
          }
        });
  }
  
  /**
   * @return an Observable of the BaseProduct
   */
  private Observable<BaseProduct> baseProduct(Long productId) {
    return RxObservable
    .from(
      client.target(serviceUrls.getBaseProductUrl()).path("/products").path(String.valueOf(productId)))
    .request().rx().get(BaseProduct.class);
  }
  
  /**
   * @return An Observable of the Reviews
   */
  private Observable<Reviews> reviews(Long productId) {
    return RxObservable
        .from(client.target(serviceUrls.getReviewsUrl()).path("/productReviews").path(String.valueOf(productId)))
        .request().rx().get(Reviews.class);
  }
  
  /**
   * @return An Observable having Product and Option Ids
   */
  private Observable<Long> productAndOptionIds(Observable<Product> product) {
    return product.flatMap(new Func1<Product, Observable<Long>>() {

      @Override
      public Observable<Long> call(Product resultProduct) {
        return Observable.from(Iterables.concat(
          Lists.<Long> newArrayList(resultProduct.getProductId()),
          Iterables.transform(resultProduct.getOptions(), new Function<ProductOption, Long>() {

            @Override
            public Long apply(ProductOption option) {
              return option.getProductId();
            }
          })));
      }
    });
  }
  
  /**
   * Inventories returns back inventories of the Primary product and options. 
   * However, for the primary product, no web service call is invoked as inventory of the main product is the sum of 
   * inventories of all options. However, a dummy ProductInventory is created to maintain order during final concatenation.
   *
   * @param productId Id of the Product
   * @param optionIds Observable of OptionIds
   * @return An Observable of Product Inventory
   */
  private Observable<ProductInventory> inventories(Long productId, Observable<Long> optionIds) {
    return Observable.just(new ProductInventory(productId, 0))
        .concatWith(optionIds.flatMap(new Func1<Long, Observable<ProductInventory>>() {

      @Override
      public Observable<ProductInventory> call(Long optionId) {
        return RxObservable
            .from(client.target(serviceUrls.getInventoryUrl()).path("/productInventory").path("/{productId}"))
            .resolveTemplate("productId", optionId).request().rx().get(ProductInventory.class);
      }
    }));
  }
  
  /**
   * @return An Observable of ProductPrice for product Ids
   */
  private Observable<ProductPrice> prices(Observable<Long> productIds) {
    return productIds
        .flatMap(new Func1<Long, Observable<ProductPrice>>() {

          @Override
          public Observable<ProductPrice> call(Long productId) {
            return RxObservable
                .from(client.target(serviceUrls.getPriceUrl()).path("/productPrice").path("/{productId}"))
                .resolveTemplate("productId", productId.toString()).request().rx()
                .get(ProductPrice.class);
          }
        });
  }

Phew! A lot of code for something simple huh? The above code is demonstrated using jdk 7. With jdk 8 Lambda's, it should be far more succinct. However, I will admit that there is more code but not code that is not clear (hopefully). The important thing is that we are not dealing with the 'callback hell' associated with Java Futures or Invocation callbacks. That said, the performance difference between the serial execution and the Reactive Jersey version appears to strongly favor the Reactive Jersey version by a significant magnitude. By taking the serial call and turning in into parallel execution, I am certain you will achieve closer numbers to the Reactive Jersey version but at the cost of having to maintain latches etc.

Running the Example


An example application can be obtained from https://github.com/sanjayvacharya/sleeplessinslc/tree/master/reactive-jersey. The example does not have the Acme web site but has a Product-Gateway that serves as an orchestration layer. There are two resources in the Product-Gateway, one that returns the product using serial orchestration and a second that uses Reactive Jersey clients.  An Integration Test project exists where a client is used to invoke both the Serial and Reactive resources and logs an average response time.  Checkout the project, and simply execute a 'mvn install' at the root level of the project.

Summary

  • Reactive programming appears more verbose. Possible that I have not optimized it well enough, tips would be appreciated. jdk-8 Lamda's would regardless reduce the footprint.
  • Requires a mind shift regarding how you write code.
  • Aysnc could be introduced as required using Observable.subscribeOn() and Observable.observeOn() features as an when required. Read more about it on a Stack Overflow post.
  • It's a style of programming not a particular technology. RxJava seems to be emerging as the library of choice for Java.
  • Question whether something benefits from being Reactive before introducing Reactive code.
  • Jersey Reactive is a Glassfish implementation and not part of a JSR. So....
  • Monlith to Microservices might only be trading one problem for another so observe caution and be judicious in your selection of what you choose to make into a microservice
  • Batch request that fetches multiple items is not necessarily faster than multiple requests for a each item.
Finally, if there is a way to make my Reactive jersey code less verbose, let me know...all I ask is don't say collapse the services back to the monolith :-)

Monday, October 20, 2008

SOAPUI REST Support

Some time ago, I had blogged lamenting the absence of a nice testing tool for REST. At that time, a gentleman from the SOAP UI team mentioned the upcoming support for REST from Soap UI.

I could not help but try out the Beta release of the same. SOAP UI should probabaly change their name to WsUI (Web Service UI). These guys seem to be doing a pretty neat job. If their SOAP UI was any thing to go by, their REST support should turn out to be quite solid.

So I went to the SOAP UI site, downloaded the free version (SOAP UI 2.5 beta1) and installed the same. SOAP UI supports reverse engineering from WADLS quite like they do with WSDLs. An example of using WADL with SOAP UI is documented on their site.

In my current organization we do not as 'yet' use WADL, I wanted to test out the ease in which one could test REST calls using SOAP UI. Operations such as GET/POST etc.

A simple web service that services two resources, "/products" returning a list of JSON products and "/order", a resource that allows for POST/GET/PUT and DELETE of Orders.

When the SOAP UI application opens, clicking on "New SOAPUI project" brings forth a dialog where in one can either choose use specify a WADL or provides the Option for a REST Service as shown below:







Clicking OK on the above, brings forth a dialog that allows one to create a Service as shown below:





Note the end point supplied points to the Web app. In addition, select the check box to create a Rest Resource before clicking OK to bring forth the Rest Resource Dialog:



After accepting the above dialog, proceed to the Dialog shown below to be able to execute a call to the web service to obtain products.



In the above dialog although I specified application/json as the media type, the JSON tab on return of JSON content does not display the same. However, in the RAW tab, one can view the JSON content.




The same process above can be followed in order to create an order resource with different types of HTTP operations supported by the resource.

Pretty neat..makes testing REST Web Services rather easy. In particular this looks like a boon to the Tester as well as they now have an easy way to test out Rest services. One can also set up load tests, intercept request/response using tcp mon and or pull SOAP UI into eclipse as a plugin.

I have attached herewith a Maven project that represents a simple web service. There are two resources provided, "/products" and "/order". The former returns a JSON representation of products. On the latter resource, issuing a GET via "/order/1" will return back an Order. You can create an order easily by using POST. The xml for the same is:

<?xml version="1.0" encoding="UTF-8" standalone="yes">
<order>
<lineItem>
<itemId>12123</itemId>
<itemName>XBOX 360</itemName>
<quantity>2</quantity>
</lineItem>
</order>


A sample SOAPUI project for the same can be obtained from HERE. Import the same into SOAP UI. Execute "mvn jetty:run" on the Maven project to start the webapp. Subsequently execute each resource call using SOAP UI, Create Load Tests, Create a WADL, Enable TCP monitoring, Enable JAXB support, WADL.....Rock on! :-)))

Friday, August 8, 2008

JAXRS, JBoss RestEasy, Maven, Spring...rock on!

I'm drowning my audience with Rest related code. What can I do, the hunger is biting me bad! JSR 311/JAXRS is an API. Different providers provide their own implementations of the same. I have already explored RESTLET's version of the implementation and how it applies to my SpringRestlet example. Note the common theme running here..I need a framework to be able to provide a Spring Hook otherwise, I turn my head the other way :-). Yeah, a Spring fan (those who remember the movie "Swim fan" feel my obsession)!

That said, we move on to the example. RestEasy is a JBoss project that allows one to build RESTful webservices. I like their philosophy where they say, the goal is to build an easy way to speak REST.

As of this blog, Rest Easy JAXRS is at : 1.0 BETA 5. Some of the things I liked about the implementation right away:

1. Support for a Client API to talk to Restful Webservice
2. Easy Spring Integration
3. Maven Support
4. Decent Documentation
5. Easy to get going with a Starter Webservice
6. JBoss Backing..we have Hibernate after all ;-)
7. Last but not the least led by a member of the JSR

In my previous blog with the RESTLET framework support for JAXRS and Spring I had developed my own custom Servlet. With Rest Easy, I did not have to do the same. My webapp's web.xml has exactly the same configuration as mentioned in the RestEasy documentation. One major issue that I encountered is that I could not get auto discovery of Spring managed Resources and Providers (as advertised by the framework) without annotating the corresponding classes with the Spring @Component annotation. Maybe there is something I am missing. Regardless, I have filed a bug report with the RestEasy jira, lets see what surfaces from the same.

With the Resteasy version of my Spring/Restlet example , the webapp module has only 3 classes! An OrderNotFoundProvider class that translates all OrderNotFoundExceptions to meaningful REST responses, an OrderResource and a ProductsResource. I do not have any other JAVA artifacts in this module. A provider class for the Products that managed the marshalling specifics (
JSON marshalling/unmarshalling) has been moved to the common maven module to be shared among client and web modules. Regardless, look at my webapp module now, all we have is Resource and Exception management classes :-). Gone are the Application, Servlet, ApplicationConfig ...etc etc classes.

Unlike in the previous example of JAXRS that I had provided, in this example, I have changed the Client to use RestEasy's JAXRS Client support. I must admit, I am rather impressed by their effort with the same. So what are the changed in the client, the OrderClient has changed to:
@ConsumeMime("application/xml") 
public interface OrderClient {

/**
* Create an Order.
*
* @param orderDTO Order DTO
* @return OrderDTO with created id
* @throws IOException If an error occurs
*/

@POST
@Path("/order")
@ProduceMime("application/xml")
@ConsumeMime("application/xml")
public OrderDTO createOrder(OrderDTO orderDTO) throws IOException;

/**
* Updates an Order.
*
* @param orderDTO Order DTO
*/

@PUT
@Path("/order/{id}")
@ProduceMime("application/xml")
public void updateOrder(OrderDTO orderDTO, @PathParam ("id") Long id);

/**
* Retrieves an Order with the specified <code>orderId</code>.
*
* @param orderId Order Id
* @return OrderDTO
* @throws OrderNotFoundException if order is not found
* @throws IOException if an error occurs
*/

@GET
@Path("/order/{id}")
@ProduceMime("application/xml")
public OrderDTO getOrder(@PathParam("id") Long orderId) throws OrderNotFoundEx
ception, IOException;

/**
* Deletes an Order with the specified <code>orderId</code>
*
* @param orderId order Id
* @throws OrderException If an error occurs
*/

@DELETE
@Path("/order/{id}")
@ProduceMime("application/xml")
public void deleteOrder(@PathParam("id") Long orderId) throws OrderException;
}


Note the use of JAXRS annotations in the Order Client. Code is being shared, always a good thing :-). It is sufficient with the OrderClient defintion to talk to the webservice using Reseasy code. The same can be accomplished with the following lines from a consumer:

ResteasyProviderFactory.initializeInstance(); 
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());

OrderClient client = ProxyFactory.create(OrderClient.class, "http://localhost:9090/IntegrationTest");

The first two lines initialize the Resteasy client code. The third line is where a Proxy is created for the OrderClient. In the spirit of my rest/spring example, I have decided to provide an abstraction where I delegate to the proxy mentioned above as shown below ;-) :
public class OrderClientImpl implements OrderClient { 
private final OrderClient delegate;

/**
* @param uri Server Uri
*/

public OrderClientImpl(String uri) {
delegate = ProxyFactory.create(OrderClient.class, uri);
}

public OrderDTO createOrder(OrderDTO orderDTO) throws IOException {
return delegate.createOrder(orderDTO);
}

public void deleteOrder(Long orderId) throws OrderException {
delegate.deleteOrder(orderId);
}

public OrderDTO getOrder(Long orderId) throws OrderNotFoundException, IOExcept
ion {
return delegate.getOrder(orderId);
}

public void updateOrder(OrderDTO orderDTO, Long id) {
delegate.updateOrder(orderDTO, id);
}

}

I choose to depend on my Integration test and/or consumer of the service to initialize the Resteasy framework via plugins/listener what have you.

Above said, the code just works nice. I feel with the Resteasy JAXRS implementation, I have reduced the amount of coding required to build REST web service that integrates with my favorite framework Spring. Onward and upward you Resteasy folks!

The Resteasy version of the JAXRS Spring/RestEasy/Maven/Dozer project can be downloaded from HERE!
The example was run using JDK 1.6.X and Apache maven 2.0.9 on Linux environment.
Enjoy! I am off to look at CXF from the apache foundation next..:-) ...

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:

&lt;groupid>org.springframework&lt;/groupid>
&lt;artifactid>spring&lt;/artifactid>
&lt;version>2.5.1&lt;/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.