The Java API for RESTful Web Services (JAX-RS) -- Rapidly Build Lightweight Web Services

By Marc Hadley, July 2010

JAX-RS makes development of Java Web services built according to the Representational State Transfer (REST) architectural style both straightforward and natural.

INTRODUCTION

This article introduces you to the Java API for RESTful Web Services (JAX-RS), which resulted from Java Specification Request (JSR) 311 and is a component of the Java Enterprise Edition Platform (Java EE 6). The aim of JAX-RS is to make development of Java Web services built according to the Representational State Transfer (REST) architectural style both straightforward and natural for you, the developer. To this end, where possible, the API offers declarative annotations that allow you to:

  • Identify components of the application
  • Route requests to an appropriate method in a selected class
  • Extract data from a request into the arguments of a method
  • Provide metadata used in responses

JAX-RS also offers a number of utility classes and interfaces to aid with the more dynamic aspects of applications.

APPLICATIONS — THE UNIT OF DEPLOYMENT

When you use JAX-RS, the application is the unit of deployment. All of the files that make up an application are packaged together and deployed into some form of container that supplies HTTP services. A JAX-RS application consists of:

  • Application subclass An optional Application subclass that defines the other classes that make up the application

  • Root resource classes One or more root resource classes that define entry points into the URI space used by the application

  • Providers Zero or more providers that supply extended functionality to the JAX-RS runtime.

Here is an example Application subclass:

@ApplicationPath("acme")
public class AcmeApplication extends Application {
}
 

The ApplicationPath annotation specifies the base URI path segment to which all root resource class URIs are relative. By default, all root resource classes and providers packaged with the Application subclass are included in the application. An Application subclass can also specify a subset of the packaged classes by overriding the getClasses method.

ROOT RESOURCE CLASSES

Root resource classes provide entry points into the URI space used by the application. Root resource classes are plain old Java objects (POJOs) that are annotated with @Path. Here is an example root resource class:

@Path("widgets")
public class WidgetsResource {
@GET
public WidgetsRepresentation getWidgetList() {
...
}
}
 

In the above, the root resource class is published at the relative URI widgets. If this class were part of the AcmeApplication shown earlier, then its URI path would be /acme/widgets.

The @GET annotation on the getWidgetList method indicates that the method is a resource method that you want to be called when an HTTP GET request is dispatched for that URI. Additional annotations are provided to support the other common HTTP methods. The set is extensible for other less common methods. The value returned from the method is converted into a response body by an entity provider. Returning a non-void Java type results in a 200 OK response, while a void method results in a 204 No Content response. As you will see shortly, the status returned in a response can be customized.

To expose a single widget as a resource, you can add a sub-resource method to the resource class as follows:

@Path("widgets")
public class WidgetsResource {
@GET
public WidgetsRepresentation getWidgetList() {
...
}
@GET @Path("{id}")
public WidgetRespresentation getWidget(
@PathParam("id") String widgetId) {
...
}
}
 

In the above, HTTP GET requests for /acme/widgets/{id} will be dispatched to the getWidget method. The {id} indicates that the path is a URI template that will match any URI with the prefix /acme/widgets/ and a single following path segment, e.g., /acme/widgets/foo.

EXTRACTING REQUEST DATA

In the previous example, the actual value of the template variable id is extracted from the request with the @Path-Param annotation and supplied as the value of the widgetId method argument. Additional annotations allow you to extract data from other parts of the request: URI query parameters with @Query-Param, URI matrix parameters with @MatrixParam, HTTP headers with @HeaderParam, and, finally, HTTP cookies with @CookieParam.

The type of method argument that carries one of the above annotations is any Java type with a String constructor or with a static method called valueOf or fromString that returns an instance of the type from a single String argument. For HTTP methods that support an entity body, such as POST, you can extract the data in the entity body into a Java method argument. Conversion from the serialized data in the request entity to a Java type is the responsibility of an entity provider, which we will explain shortly.

In addition to the declarative method of extracting request data described above, JAX-RS provides a set of interfaces that may be queried dynamically:

  • Application Provides access to the Application subclass created by the JAX-RS runtime.

  • UriInfo Provides access to the components of the request URI and convenience methods for creating new URIs based on the request URI.

  • Request Provides methods for working with preconditions and dynamic content negotiation.

  • HttpHeaders Provides methods for working with the content of HTTP request headers.

  • SecurityContext Provides methods for determining the security context in which a request is executing.

  • Providers Supports lookup of provider instances. This capability is useful in cases where one provider wants to re-use the functionality of another, e.g., an entity provider that supports some kind of composite entity might use other entity providers to read/write individual parts of the whole.

An instance of any of the above interfaces may be injected into a resource class field or method parameter using the @Context annotation, e.g.:

@Path("widgets")
public class WidgetsResource {
@Context SecurityContext sc;
@GET
public WidgetsRepresentation getWidgetList() {
if (sc.isSecure()) {
// Secure channel
...
} else {
// Insecure channel
...
}
}
}
 

CUSTOMIZING THE RESPONSE

Responses often require a different status code than returned by default and certain types of responses may also require additional metadata in headers. In this case, the method can return an instance of the Response interface. A utility builder class is supplied that simplifies the construction of a custom response, e.g.:

URI locationURI = createResource(...);
Response response = Response.status(CREATED)
.location(locationURI)
.build();
return response;
 

or, more conveniently:

URI locationURI = createResource(...);
Response response = Response.created(locationURI)
.build();
return response;
 

The JAX-RS runtime will convert relative URIs to absolute URIs when necessary and is also responsible for serialization of custom types used as header values.

BUILDING URIS

A RESTful application will use URIs extensively. The JAX-RS UriBuilder provides you with facilities for constructing URIs both from scratch and from existing URIs. It makes it easy for you to construct URIs based on the URI templates contained in @Path annotations. For example:

@Path("widgets")
public class WidgetsResource {
@Context UriInfo ui;
@GET @Path("{id}")
public WidgetRespresentation getWidget(
@PathParam("id") String widgetId) {
...
}
@POST
public Response createWidget(
WidgetRepresentation widget) {
String widgetId = saveWidget(widget);
UriBuilder ub = ui.getRequestUriBuilder();
URI widgetURI = ub
.path(WidgetsResource.class, "getWidget")
.build(widgetId);
return Response.created(widgetURI).build();
}
}
 

The createWidget method constructs widgetURI using:

  • The request URI, e.g. http://.../acme/widgets.
  • The relative URI template of the sub-resource ({id})extracted from the @Path annotation on the getWidget method
  • The value of the URI template parameter contained in widgetId.

Prior to the call to the build method, the URI builder contains: http://.../acme/widgets/{id}

The arguments to the build method are substituted positionally for the URI template variables. Therefore, if widgetId contained the value ”foo” then the final value of widgetURI will be: http://.../acme/widgets/foo

EXCEPTION HANDLING

Exceptions thrown by a resource class method are caught by the JAX-RS runtime and converted to error responses. By default, exceptions are converted to a 500 Server Error response. JAX-RS offers two ways that you can customize the default error response:

  • Methods can throw WebApplicationException which can contain a customized Response.

  • The application can include a bundled exception mapping provider which will be called to create a customized Response when an exception is caught.

The two above methods can be mixed within an application. The second method is particularly well suited to cases where many methods can throw the same exception(s) since it naturally centralizes error response handling in one place.

DECLARATIVE CONTENT NEGOTIATION

As you have seen from prior examples, when dispatching requests to Java methods, the JAX-RS runtime does the following to route the request to the appropriate method:

  1. The request URI is compared to the URI templates supplied as values of @Path annotations.

  2. The request method is compared to method designator annotations (@GET, @POST, etc.).

    JAX-RS also provides annotations that allow you to use different methods depending on the media types of the request and response.

  3. The media type of the request body (Content-Type header) is compared to the value(s) of @Consumes annotations.

  4. The media type(s) that the client is requesting that the response use (Accept header) is compared to the value(s) of @Produces annotations.

The order above denotes significance from most (request URI) to least (desired response media type). The dispatching algorithm proceeds in a number of phases. The first phase identifies the class whose @Path value most closely matches the request URI, therefore it is the most significant aspect. The value of the @Produces annotation is only considered in the final stage of matching -- therefore it is the least significant aspect. The exact algorithm used to match requests to the appropriate Java method is described in section 3.7 of the JAX-RS specification request.

Here is an example of declarative content negotiation:

@Path("widgets")
public class WidgetsResource {
@GET @Produces("application/xml")
public Document getXml() {...}
@GET @Produces("application/json")
public String getJson() {...}
}
 

In the above, a GET request for /acme/widgets with an Accept header value application/xml would be dispatched to the getXml method. Likewise, a GET request for the same URI with an Accept header of application/json would be dispatched to the getJson method.

PROVIDERS: FUNCTIONALITY FOR THE JAVA-RS RUNTIME

Providers are JAX-RS extensions that supply functionality to the JAX-RS runtime. The two main provider types are entity providers and exception mapping providers.

Entity Providers

Entity providers supply serialization and/or deserialization services between resource representations and their associated Java types. An entity provider that supports deserialization of a representation to a Java type implements the MessageBodyReader interface. An entity provider that supports serialization of a Java type to a representation implements the MessageBodyWriter interface.

Inclusion of an entity provider for a particular Java type in a JAX-RS application allows that type to be used as a resource method argument, as the return type for a resource method, or as the type of entity embedded in a returned Response. JAX-RS includes a number of built-in entity providers for common Java types including: String, InputStream, File and application-supplied JAXB classes. Entity providers may use the @Consumes and @Provides annotations to statically declare the media types that they support. They can also determine whether they support a particular media type and Java type at runtime to accommodate more complex providers.

Exception Mapping Providers

Exception mapping providers supply mapping services between Java exceptions and a JAX-RS Response. Exception mapping providers implement the ExceptionMapper interface and are called when a resource method throws a checked or runtime exception. The Response returned by an exception mapping provider is treated the same as a Response returned by a resource method: it may use any status code and may contain headers. Any contained entity will be serialized using an appropriate entity provider.

CONCLUSION

JAX-RS is an annotation-driven Java API that aims to make development of Web services built according to the Representational State Transfer (REST) architectural style in Java both straightforward and intuitive for you, the developer. It should enable you to more rapidly build lightweight web services that conform to the REST software style. Good luck with JAX-RS!

See Also