Examples for Chapter 5 - JAX-RS Workbook - RESTful Java with JAX-RS 2.0 (2013)

RESTful Java with JAX-RS 2.0 (2013)

Part II. JAX-RS Workbook

Chapter 20. Examples for Chapter 5

Chapter 5 showed you how to use JAX-RS annotations to inject specific information about an HTTP request into your Java methods and fields. This chapter implements most of the injection scenarios introduced in Chapter 5 so that you can see these things in action.

Example ex05_1: Injecting URI Information

This example illustrates the injection annotations that are focused on pulling in information from the incoming request URI. Specifically, it shows how to use @PathParam, @MatrixParam, and @QueryParam. Parallel examples are also shown using javax.ws.rs.core.UriInfo to obtain the same data.

The Server Code

The first thing you should look at on the server side is CarResource. This class pulls the various examples in Chapter 5 together to illustrate using @MatrixParam and @PathParam with the javax.ws.rs.core.PathSegment class:

src/main/java/com/restfully/shop/services/CarResource.java

@Path("/cars")
public class CarResource
{
   public static enum Color
   {
      red,
      white,
      blue,
      black
   }
 
   @GET
   @Path("/matrix/{make}/{model}/{year}")
   @Produces("text/plain")
   public String getFromMatrixParam(
                             @PathParam("make") String make,
                             @PathParam("model") PathSegment car,
                             @MatrixParam("color") Color color,
                             @PathParam("year") String year)
   {
      return "A " + color + " " + year + " "
                  + make + " " + car.getPath();
   }

The getFromMatrixParam() method uses the @MatrixParam annotation to inject the matrix parameter color. An example of a URI it could process is /cars/matrix/mercedes/e55;color=black/2006. Notice that it automatically converts the matrix parameter into the Java enum Color:

   @GET
   @Path("/segment/{make}/{model}/{year}")
   @Produces("text/plain")
   public String getFromPathSegment(@PathParam("make") String make,
                                @PathParam("model") PathSegment car,
                               @PathParam("year") String year)
   {
      String carColor = car.getMatrixParameters().getFirst("color");
      return "A " + carColor + " " + year + " "
                             + make + " " + car.getPath();
   }

The getFromPathSegment() method also illustrates how to extract matrix parameter information. Instead of using @MatrixParam, it uses an injected PathSegment instance representing the model path parameter to obtain the matrix parameter information:

   @GET
   @Path("/segments/{make}/{model : .+}/year/{year}")
   @Produces("text/plain")
   public String getFromMultipleSegments(
                             @PathParam("make") String make,
                           @PathParam("model") List<PathSegment> car,
                         @PathParam("year") String year)
   {
      String output = "A " + year + " " + make;
      for (PathSegment segment : car)
      {
         output += " " + segment.getPath();
      }
      return output;
   }

The getFromMultipleSegments() method illustrates how a path parameter can match multiple segments of a URI. An example of a URI that it could process is /cars/segments/mercedes/e55/amg/year/2006. In this case, e55/amg would match the model path parameter. The example injects the model parameter into a list of PathSegment instances:

   @GET
   @Path("/uriinfo/{make}/{model}/{year}")
   @Produces("text/plain")
   public String getFromUriInfo(@Context UriInfo info)
   {
      String make = info.getPathParameters().getFirst("make");
      String year = info.getPathParameters().getFirst("year");
      PathSegment model = info.getPathSegments().get(3);
      String color = model.getMatrixParameters().getFirst("color");
 
      return "A " + color + " " + year + " "
                  + make + " " + model.getPath();
   }

The final method, getFromUriInfo(), shows how you can obtain the same information using the UriInfo interface. As you can see, the matrix parameter information is extracted from PathSegment instances.

The next piece of code you should look at on the server is CustomerResource. This class shows how @QueryParam and @DefaultValue can work together to obtain information about the request URI’s query parameters. An example using UriInfo is also shown so that you can see how this can be done without injection annotations:

src/main/java/com/restfully/shop/services/CustomerResource.java

@Path("/customers")
public class CustomerResource {
...
 
   @GET
   @Produces("application/xml")
   public StreamingOutput getCustomers(
               final @QueryParam("start") int start,
               final @QueryParam("size") @DefaultValue("2") int size)
   {
     ...
   }

The getCustomers() method returns a set of customers from the customer database. The start parameter defines the start index and the size parameter specifies how many customers you want returned. The @DefaultValue annotation is used for the case in which a client does not use the query parameters to index into the customer list.

The next implementation of getCustomers() uses UriInfo instead of injection parameters:

   @GET
   @Produces("application/xml")
   @Path("uriinfo")
   public StreamingOutput getCustomers(@Context UriInfo info)
   {
      int start = 0;
      int size = 2;
      if (info.getQueryParameters().containsKey("start"))
      {
         start = Integer.valueOf(
                        info.getQueryParameters().getFirst("start"));
      }
      if (info.getQueryParameters().containsKey("size"))
      {
         size = Integer.valueOf(
                        info.getQueryParameters().getFirst("size"));
      }
      return getCustomers(start, size);
   }

As you can see, the code to access query parameter data programmatically is a bit more verbose than using injection annotations.

The Client Code

The client code for this example lives in the file src/test/java/com/restfully/shop/test/InjectionTest.java. The code is quite boring, so I won’t get into too much detail.

The testCarResource() method invokes these requests on the server to test the CarResource class:

GET http://localhost:8080/services/cars/matrix/mercedes/e55;color=black/2006
GET http://localhost:8080/services/cars/segment/mercedes/e55;color=black/2006
GET http://localhost:8080/services/cars/segments/mercedes/e55/amg/year/2006
GET http://localhost:8080/services/cars/uriinfo/mercedes/e55;color=black/2006

The testCustomerResource() method invokes these requests on the server to test the CustomerResource class:

GET http://localhost:8080/services/customers
GET http://localhost:8080/services/customers?start=1&size=3
GET http://localhost:8080/services/customers/uriinfo?start=2&size=2

The request without query parameters shows @DefaultValue in action. It is worth noting how query parameters are handled in the client code. Let’s look at testCustomerResource() a little bit:

      list = client.target("http://localhost:8080/services/customers/uriinfo")
                   .queryParam("start", "2")
                   .queryParam("size", "2")
                   .request().get(String.class);

The javax.ws.rs.client.WebTarget.queryParam() method is used to fill out the query parameters for the invocation. This is a nice convenience method to use, especially if your values might have characters that need to be encoded.

Build and Run the Example Program

Perform the following steps:

1. Open a command prompt or shell terminal and change to the ex05_1 directory of the workbook example code.

2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.

3. Perform the build and run the example by typing maven install.

Example ex05_2: Forms and Cookies

The ex05_2 exercise includes examples of injecting form data, cookies, and HTTP headers using the @FormParam, @CookieParam, and @HeaderParam annotations. This example is a bit different than former examples, as there is no client code. Instead, to see these annotations in action, you will use a browser as your client.

The Server Code

The example starts off with an HTML form defined in src/main/webapp/index.html:

<html>
<body>
 
<form action="/rest/customers" method="post">
    First Name: <input type="text" name="firstname"/><br/>
    Last Name: <input type="text" name="lastname"/><br/>
    <INPUT type="submit" value="Send">
</form>
 
</body>
</html>

It is a simple form for creating a customer using our familiar CustomerResource service:

src/main/java/com/restfully/shop/CustomerResource.java

@Path("/customers")
public class CustomerResource {
...
   @POST
   @Produces("text/html")
   public Response createCustomer(
                             @FormParam("firstname") String first,
                             @FormParam("lastname") String last)
   {

The HTML form posts data to the createCustomer() method of CustomerResource when users click the Send button:

      Customer customer = new Customer();
      customer.setId(idCounter.incrementAndGet());
      customer.setFirstName(first);
      customer.setLastName(last);
      customerDB.put(customer.getId(), customer);
      System.out.println("Created customer " + customer.getId());
      String output = "Created customer <a href=\"customers/" +
                  customer.getId() + "\">" + customer.getId()
                            + "</a>";
      String lastVisit = DateFormat.getDateTimeInstance(
                DateFormat.SHORT, DateFormat.LONG).format(new Date());
      return Response.created(URI.create("/customers/"
                                          + customer.getId()))
                     .entity(output)
                     .cookie(new NewCookie("last-visit", lastVisit))
                     .build();
 
   }

The createCustomer() method does a couple things. First, it uses the form data injected with @FormParam to create a Customer object and insert it into an in-memory map. It then builds an HTML response that shows text linking to the new customer. Finally, it sets a cookie on the client by calling the ResponseBuilder.cookie() method. This cookie, named last-visit, holds the current time and date. This cookie will be used so that on subsequent requests, the server knows the last time the client accessed the website:

   @GET
   @Path("{id}")
   @Produces("text/plain")
   public Response getCustomer(
                        @PathParam("id") int id,
                          @HeaderParam("User-Agent") String userAgent,
                            @CookieParam("last-visit") String date)
   {

The getCustomer() method retrieves a Customer object from the in-memory map referenced by the id path parameter. The @HeaderParam annotation injects the value of the User-Agent header. This is a standard HTTP 1.1 header that denotes the type of client that made the request (Safari, Firefox, Internet Explorer, etc.). The @CookieParam annotation injects the value of the last-visit cookie that the client should be passing along with each request:

      final Customer customer = customerDB.get(id);
      if (customer == null) {
         throw new WebApplicationException(Response.Status.NOT_FOUND);
      }
      String output = "User-Agent: " + userAgent + "\r\n";
      output += "Last visit: " + date + "\r\n\r\n";
      output += "Customer: " + customer.getFirstName() + " "
                     + customer.getLastName();
      String lastVisit = DateFormat.getDateTimeInstance(
                DateFormat.SHORT, DateFormat.LONG).format(new Date());
      return Response.ok(output)
              .cookie(new NewCookie("last-visit", lastVisit))
              .build();
   }

The implementation of this method is very simple. It outputs the User-Agent header and last-visit cookie as plain text (text/plain). It also resets the last-visit cookie to the current time and date.

Build and Run the Example Program

Perform the following steps:

1. Open a command prompt or shell terminal and change to the ex05_2 directory of the workbook example code.

2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.

3. Perform the build and run the example by typing maven jetty:run. This command is a bit different than our previous examples. This script builds the WAR file, but it also starts up the Jetty servlet container.

You test-drive ex05_2 by using your browser. The first step is to go to http://localhost:8080, as shown in Figure 20-1.

When you click Send, you will see the screen shown in Figure 20-2.

Clicking the customer link will show you a plain-text representation of the customer, as shown in Figure 20-3.

If you refresh this page, you will see the timestamp of the “last visit” string increment each time as the CustomerResource updates the last-visit cookie.

Customer creation form

Figure 20-1. Customer creation form

Creation response

Figure 20-2. Creation response

Customer output

Figure 20-3. Customer output