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.
Figure 20-1. Customer creation form
Figure 20-2. Creation response
Figure 20-3. Customer output