Technical Article
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
See Also
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 thegetWidget
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:
- The request URI is compared to the URI templates supplied as values of
@Path
annotations. - 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. - The media type of the request body (
Content-Type
header) is compared to the value(s) of @Consumes annotations. - 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!