LDSTechForumProjects

Web Services with Apache CXF - Part 3

Other Resources

The SpringOne 2GX 2012 conference had an excellent presentation on designing REST web services.

  • Designing a REST-ful API Using Spring 3 by Ben Hale.
    • Note that while the presenter's training uses Spring MVC for designing REST Web Services, many of the principles, techniques, and best practices discussed in the training apply to all REST service APIs.
    • The presentation also includes valuable information on how you can write functional tests for a REST web service. The Spring APIs the author uses can be used for testing any REST web service, regardless of the technology it was written in.

Presentation

Download the training slides.

Video Presentation 1

Get Adobe Flash player

LAB 1 - Hello World in REST

Objective:

Define a simple "Hello, World!" web service using CXF, <stack-rs:produce/>, and basic JAX-RS annotations.

Before you begin:

  • You will need an HTTP client interface for making manual HTTP GET and POST requests to the lab project. We will be using a browser plugin. The following work reasonably well for Firefox and Chrome:
  • Create a new project with the Java Stack Starter, and import it into your IDE:
    1. Launch the Java Stack Starter.
    2. Select the Web Service Provider project type.
    3. Enter the following information:
      • Project Name: lab
      • Package Name: org.lds.tech.training.lab
      • Project Location: <somewhere on your local drive>
    4. Click Next and select the Security property group:
      • Uncheck Use LDS Account Authentication.
    5. Select the Service Layer property group:
      • Uncheck Use a database?.
    6. Select the Web Services property group:
      • Make sure REST is selected as the CXF Web Service Type.
    7. Click Next and Build
    8. Scroll to the top of the output window, copy the build location, and import into your IDE as a Maven project.
    9. Import the labs project into your IDE. In the case of LDSTech IDE use File->Import...->Maven->Existing Maven Project. Select the project you just created and import all of the projects found.
    10. Add your labs-web project to a new LDSTech server.

Lab Instructions:

  • Create a new Java class: org.lds.tech.training.lab.ws.HelloWebServiceRest.
  • Define a method sayHello with zero arguments and a String return value.
  • Annotate your class as shown below:
    package org.lds.tech.training.lab.ws;

    import javax.ws.rs.GET;
    import org.springframework.stereotype.Controller;

    @Controller
    public class HelloWebServiceRest {

        @GET
        public String sayHello() {
            return "Hello, World!";
        }
    }
  • Locate the CXF controller servlet definition in web.xml.
    • In Stack 3.1.x, the servlet name will be the base name of your project (e.g. lab) and the servlet class will be org.lds.stack.cxf.servlet.StackCXFServlet.
    • In Stack 3.2 (not yet released), this will change to have the default servlet name ws and will use the CXF servle org.apache.cxf.transport.servlet.CXFServlet.
  • In web.xml, take note of the servlet mappings for the CXF controller servlet:
    • Note that the CXF controller will handle all requests under the path /Services/*
  • Locate the Spring configuration file under your WEB-INF folder:
    • In Stack 3.1.x, the name will be the CXF controller servlet name (e.g. lab) with a -servlet.xml suffix.
    • In Stack 3.2, this will change to always have the name cxf-servlet.xml. (This is due to new requirements introduced in CXF 2.4.0)
  • Add the following Spring configuration to WEB-INF/lab-servlet.xml:
    <stack-rs:produce>
        <stack-rs:interfaces>
            <ref bean="helloWebServiceRest"/>
        </stack-rs:interfaces>
    </stack-rs:produce>
  • Run the web application and browse to:
http://localhost/lab/v1.0/Services
  • From the list of "Available RESTful services", click the hyperlink to see the WADL for your service. It should look like the following:
    <application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <grammars/>
        <resources base="http://localhost/lab/Services/rest">
            <resource path="/">
                <method name="GET">
                    <response>
                        <representation mediaType="application/octet-stream">
                            <param name="result" style="plain" type="xs:string"/>
                        </representation>
                    </response>
                </method>
            </resource>
        </resources>
    </application>
  • Invoke your service by entering the following address into your browser or HTTP client:
http://localhost/lab/v1.0/Services/rest
  • The browser will display the following message:
Hello, World!
  • An HTTP client should display a "200 OK" status and response headers similar to the following:
Content-Type: application/octet-stream;charset=UTF-8
Content-Length: 13

Video Presentation 2

Get Adobe Flash player

Video Presentation 3

Get Adobe Flash player

LAB 2 - JAX-RS Annotations and Content Negotiation

Objectives:

  • Use JAX-RS annotations to query and update a categorized item database.

Before you begin:

  • Download the Item class source file and copy it into the org.lds.tech.training.lab.model package.
  • Download the ItemService class source file and copy it into the org.lds.tech.training.lab.service package.

Steps:

  • Create a new JAX-RS service class: org.lds.tech.training.lab.ws.CatalogWebServiceRest
    • Be sure to annotate the class with the Spring @Controller annotation
    • Also supply a @Path annotation with a value of "catalog"
    @Controller
    @Path("catalog")
    public class CatalogWebServiceRest {
    }
  • Inject the ItemService into the CatalogWebServiceRest class:
    @Inject
    private ItemService itemService;
  • Annotate a field with @QueryParam to receive a parameter for sorting:
    @QueryParam("sort")
    private boolean sort;
  • Define a method to GET a single Item, and annotate a method parameter to receive the "itemId" query parameter.
    @GET
    public Item getItem(@QueryParam("itemId") Long id) {
        return itemService.getItem(id);
    }
  • Define a method to GET a list of Item objects from the path "{category}/{subcategory:[^/]*}", annotate it to Produce both "application/xml" and "application/json", and annotate two method parameters to receive the "category" and "subcategory" parameters.
    @GET
    @Path("{category}/{subcategory:[^/]*}")
    @Produces({"application/xml","application/json"})
    public List<Item> getItems(
            @PathParam("category") String category,
            @PathParam("subcategory") String subcategory) {
        List<Item> items = itemService.getItems(category, subcategory);
        if (sort) {
            Collections.sort(items);
        }
        return items;
    }
  • Define a method to POST an Item object so that it can Consume it as either "application/xml" or "application/json", add it to the database and then Produce the new list of items from the database as either "application/xml" or "application/json".
    @POST
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json"})
    public List<Item> addItem(Item item) {
        itemService.addItem(item);
        List<Item> items = itemService.getItems();
        if (sort) {
            Collections.sort(items);
        }
        return items;
    }
  • Open lab-servlet.xml and add your new service bean to the list of <stack-rs:interfaces> under the <stack-rs:produce> configuration.
    • (Optional) To simplify testing, add the extensions="true" attribute to the <stack-rs:produce> configuration.
    <stack-rs:produce extensions="true">
        <stack-rs:interfaces>
            <ref bean="helloWebServiceRest"/>
            <ref bean="catalogWebServiceRest"/>
        </stack-rs:interfaces>
    </stack-rs:produce>
  • Now re-publish your application.
  • To view your new WADL descriptor, browse to:
http://localhost/lab/Services
  • Take particular notice of the <grammars> section as this has the XML schema for your Item type.
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
               attributeFormDefault="unqualified"
               elementFormDefault="unqualified">
        <xs:element name="item" type="item"/>
        <xs:complexType name="item">
            <xs:sequence/>
            <xs:attribute name="id" type="xs:long"/>
            <xs:attribute name="category" type="xs:string"/>
            <xs:attribute name="subcategory" type="xs:string"/>
            <xs:attribute name="name" type="xs:string"/>
        </xs:complexType>
    </xs:schema>
  • Now for some testing. Open an HTTP REST client for making manual GET and POST requests.

TEST getItem

  • Make the following GET request for application/json content:
GET http://localhost/lab/Services/rest/catalog?itemId=1 HTTP/1.1
Accept: application/json
  • It should return the following response body:
{
   "id": 1,
   "category": "animals",
   "subcategory": "dogs",
   "name": "chihuahua"
}


TEST getItems

  • Make the following GET request for application/xml content:
GET http://localhost/lab/Services/rest/catalog/animals/?sort=true HTTP/1.1
Accept: application/xml
  • It should return the following response body:
    <items>
        <item id="6" category="animals" subcategory="lizards" name="chameleon" />
        <item id="5" category="animals" subcategory="lizards" name="iguana" />
        <item id="7" category="animals" subcategory="lizards" name="komodo" />
        <item id="2" category="animals" subcategory="dogs" name="doberman" />
        <item id="1" category="animals" subcategory="dogs" name="chihuahua" />
        <item id="3" category="animals" subcategory="dogs" name="poodle" />
        <item id="4" category="animals" subcategory="dogs" name="mutt" />
    </items>


TEST addItem

  • Make the following POST request that submits application/json content and receives application/xml in return.
POST http://localhost/lab/Services/rest/catalog?sort=true HTTP/1.1
Accept: application/xml
Content-Type: application/json
{
   "id" : "101",
   "name" : "Java Developer",
   "category" : "human",
   "subcategory" : "awesome"
}
  • You should get all the items in the database.

Video Presentation 4

Get Adobe Flash player

LAB III - JAX-RS Response and Exception Handling

Objectives:

  • Implement an ExceptionMapper and use the ResponseBuilder to gracefully handle an IllegalArgumentException

Before you begin:

  • Be sure you have completed lab 2

Steps:

  • Create a new class: org.lds.tech.training.lab.ws.IllegalArgumentExceptionMapper
  • Annotate the new class with the @Provider annotation to mark the class as a JAX-RS provider.
  • Annotate the class with the Spring @Component annotation to make it discoverable by Spring.
  • Implement the javax.ws.rs.ext.ExceptionMapper interface with IllegalArgumentException as the exception type.
@Provider
@Component
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {

    public Response toResponse(IllegalArgumentException exception) {
        ResponseBuilder rb = Response.status(409); // Conflict status code (e.g. duplicate key)
        return rb.build();
    }	
}
  • Modify your Spring configuration to include the new IllegalArgumentExceptionMapper provider:
<stack-rs:produce extensions="true">
        <stack-rs:interfaces>
            <ref bean="helloWebServiceRest"/>
            <ref bean="catalogWebServiceRest"/>
        </stack-rs:interfaces>
        <stack-rs:providers>
            <ref bean="illegalArgumentExceptionMapper"/>
        </stack-rs:providers>
    </stack-rs:produce>
  • Test the exception mapper by attempting to add a new item with an existing item id.
    • You should get a HTTP status 409 error.
This page was last modified on 12 July 2013, at 19:34.

Note: Content found in this wiki may not always reflect official Church information. See Terms of Use.