JAX-RS: Developing RESTful Web Services in Java

he simplicity of REpresentational State Transfer (REST), an architectural style for accessing information on the web, has made it a popular way for developers to access services. In the REST architectural style, information on the server side is considered a resource, which developers can access in a uniform way using web URIs (Uniform Resource Identifiers) and HTTP. Because REST uses HTTP as the communication protocol, the REST style is constrained to a stateless client/server architecture.

RESTful web services (i.e., web services that are created and accessed using REST principles) use HTTP protocol methods for the operations they perform (see Table 1 below). For example, a developer can map the HTTP methods POST, GET, PUT, and DELETE to create, read, update and delete (CRUD) operations.

Table 1. HTTP Protocol Methods and the Operations They Perform

For Java developers, JAX-RS (JSR 311) provides an API for creating RESTful web services in Java. Part of the Java EE 6 platform, JAX-RS fully supports REST principles. This article drills down into JAX-RS, exploring its classes and annotations, before demonstrating how to build a simple RESTful web service using JAX-RS.

JAX-RS and the Jersey Project

The JAX-RS API uses annotations to simplify the development of RESTful web services. Annotations, along with the classes and interfaces provided by JAX-RS API, allow you to expose simple POJOs as web resources. Because of its heavy reliance on annotations, JAX-RS requires Java 5 and above.

As with any other Java web application, you bundle JAX-RS applications as a WAR file and deploy them on a container that supports Servlets. You use the Servlets provided by the container to route the requests to the appropriate web resource. A goal of JAX-RS is to enable the portability to deploy web resources across different types of web containers.

Sun offers a reference implementation for JAX-RS code-named Jersey. Jersey uses a HTTP web server called Grizzly, and the Servlet Grizzly Servlet (com.sun.jersey.spi.container.servlet.ServletContainer) handles the requests to Grizzly. You can develop production-quality JAX-RS applications today using Jersey, which implements all the APIs and provides all the necessary annotations for creating RESTful web services in Java quickly and easily. Beyond the set of annotations and features defined by JAX-RS, Jersey provides additional features through its own APIs, such as the Jersey Client API.

You download Jersey separately or acquire it as a bundle with NetBeans 6.5 and GlassFish V3 Prelude.

Developing RESTful Web Services Using JAX-RS

The classes and interfaces you use for creating RESTful web services with JAX-RS are available in the following packages:

  • javax.ws.rs
  • javax.ws.rs.core
  • javax.ws.rs.ext

The Resource Class

JAX-RS defines a resource as any Java class (POJO) that uses JAX-RS annotations to implement a web resource. The annotation @Path identifies a Java class as a resource class. Here is an example:

import javax.ws.rs.Path;@Path("/stockquote")public class StockResource {   .......   ......    public String getStockInfo() {        return "This is Stock Information";    }}

To invoke the above class with a value matching a corresponding URI value, you would:

  1. Package the class as part of a WAR file, with an entry for the Servlet provided in the web.xml file of your JAX-RS implementation.
  2. Deploy the WAR file in a container that supports the JSR 311 specification (GlassFish, for example)
  3. Access the resource using the usual URI: http://hostname:portnumber/WARfilename/stockquote

A new instance of the resource class will be created for each request to that resource. After the object creation, the constructor is invoked, the required dependencies are injected, the appropriate resource method is invoked, and when the response is provided, the object is made available for garbage collection. Figure 1 shows the full resource class lifecycle.

Figure 1. Resource Class Lifecycle: A new instance of the resource class will be created for each request to that resource.

Resource Methods

Resource methods are public methods of a resource class that you identify with a request method designator. Request method designators are annotations that you use to identify the methods that handle the HTTP requests, and JAX-RS defines annotations for HTTP methods such as GET, POST, PUT, DELETE, and HEAD. JAX-RS also allows you to create user-defined custom request method designators.

JAX-RS provides a clear mapping between the HTTP protocol and the URIs through well-defined classes and interfaces (see Table 2). As mentioned earlier, in RESTful web services, the HTTP methods are mapped to the CRUD operations they perform.

Table 2. HTTP Methods and Corresponding Request Method Designators

The type of HTTP method request dictates which request method designator is called. For example, if the request is from a HTTP GET request, the service automatically invokes a method annotated with @GET and provides the response.

For HTTP method requests such as HEAD and OPTIONS, JAX-RS provides some amount of automation support. When a HEAD request comes, a method annotated with the @HEAD request method designator is invoked. If no such request method designator exists, then by default the service invokes @GET without providing any response. However, this will have some impact on performance.

The return values of methods with request designator annotations are generally void, a Java language type, or a response object of the type javax.ws.rs.core.Response. Here is an example:

package com.demo;import javax.ws.rs.Path;import javax.ws.rs.GET;import javax.ws.rs.PUT;@Path("/demo")public class DemoResource {    /** Creates a new instance of DemoResource */    public DemoResource() {    }    /**     * Retrieves representation of an instance of com.demo.DemoResource     * @return an instance of java.lang.String     */    @GET    .......      public String getXml() {        .........    }    /**     * PUT method for updating or creating an instance of DemoResource     * @param content representation for the resource     * @return an HTTP response with content of the updated or created resource.     */    @PUT    ..........    public void putXml(String content) {    }}

Resource methods can have a set of parameters. Some of these parameters will have annotations and others will not. You can annotate the method parameters with one of the annotations in Table 3.

Table 3. Parameter Annotations

package com.demo;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.QueryParam;@Path("/sayHello")public class DisplayDetails {    public DisplayDetails() {    }    @GET    ......    public String getHtml(@QueryParam("empname") String empName,    @QueryParam("empnumber") int empNumber,    @QueryParam("empunit") String empUnit,    @QueryParam("empmail") String empMail) {    StringBuilder str = new StringBuilder(" Hello, Mr./Ms.  "+empName+"  Welcome to the world of JAX-RS");    str = str.append("
Your Employee Number is: "+empNumber+""); str = str.append("
Your Unit is: "+empUnit+""); str = str.append("
Your Email ID is: "+empMail+""); return str.toString(); }}

When the method is invoked, the appropriate parameters are mapped according to the semantics of the request. Parameters that are not annotated are called as entity parameters whose value is mapped from the request entity body. The JAX-RS specification does not permit more than one entity parameter per method.

As previously discussed, the return value of a resource method could be void, a response object, or any other Java type. For HTTP requests, you use the JAX-RS API’s MessageBodyReader class to map the request entity body to method parameters. Similarly, for HTTP responses, you use MessageBodyWriter to map a return value to the response entity body. These classes take care of the conversion between the Java types and entity bodies. Methods that need to return additional types that are not part of the standard Java types should return an instance of the response object. Resource methods can also throw any checked or unchecked exception.

Table 4 explains how a return value of a resource method maps to the response.

Table 4. Return Value of Resource Method

URI Template

As discussed earlier, you identify a root resource class using the @Path annotation. The value of the annotation can have a relative URI path template between the curly braces {,}, with the deployment context providing the reference to the base URI. A URI path template acts as a placeholder for a relative path URI. It generally is a string with zero or more embedded parameters in it, and it forms a valid URI path when values are applied for the parameters. Here is an example:

package com.demo;import javax.ws.rs.Path;@Path("/sayHello/{username}")public class SayHello {    ......    ......    ......}

In this example, the SayHello resource class is identified by the relative URI path sayHello/abc where abc is the value of the username parameter. However, { and } will not appear in the URI, as they are not valid. They are used only to specify the URI template.

You can retrieve the value of the username parameter by using the @PathParam annotation as the parameter value in part of the URI path. URI template parameters can optionally have a regular expression as well.

package com.demo;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;@Path("/sayHello/{username}")public class SayHello {    public SayHello() {    }    @GET    ......    public String hello(@PathParam("username") String userName) {        return " Hello,  "+userName +"  +Welcome to the world of JAX-RS! ";    }}

Similarly, the parameter annotation @FormParam will be helpful to get the information from the form elements in the request.package com.demo;

import javax.ws.rs.POST;import javax.ws.rs.Path;import javax.ws.rs.FormParam;@Path("/sayHello")public class SayHello {    public SayHello() {    }    @POST    ......    public String hello(@FormParam("username") String userName) {        return " Hello,  "+userName +"  +Welcome to the world of JAX-RS! ";    }}

Sub Resources

Apart from the resource class, you can also annotate methods of a resource class with the @Path annotation. This annotation specifies that the methods will be called as sub resource methods or sub resource locators.

Sub Resource Methods

Methods in a resource class that are annotated with @Path along with a request method designator annotation such as @GET, @POST, etc. are called as sub resource methods. Sub resource methods will be invoked for a URI request that is created by concatenating the URI template of the method with the URI template of the resource class.

package com.demo;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.FormParam;@Path("/sayHello")public class SayHello {    public SayHello() {    }    @GET    @Path("lastname")    public String hello() {..............    }}

In the above example, a GET request from the URI /sayHello/lastname will be handled directly by the hello() sub-resource method in the SayHello resource class.

Sub Resource Locators

Methods in a resource class that are annotated with @Path, which dynamically identifies the object that will handle the request, are called sub resource locators. The return value for these methods is generally an object of a resource class that will handle the request. Sub resource locators are similar to normal resource methods, but they cannot have an entity parameter.

MIME Types Supported by JAX-RS for Request and Response

You use MIME media types to identify HTTP request entities and representations. A resource class can produce or consume any type of MIME. You use annotations to specify the MIME media type for request and response on a resource class or resource method. You use the annotation @Produces to specify the MIME type for the response (a representation that can be produced by a resource and sent back to the client) and @Consumes to specify the MIME type for the request (a representation of the specific content types that a resource can accept from an HTTP request entity, or evidence that it can consume the content sent by client).

To specify a simple text/plain MIME, you use @Produces (“text/plain”) and @Consumes (“text/plain”).

package com.rest.demo;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;@Path("/helloworld")public class Hello {    @GET    @Produces("text/plain")    public String sayHello() {        return "Hello, Welcome to the World of REST";    }}

If the MIME type is HTML, then you use “text/html” as the value for @Produces and @Consumes.

package com.demo;import javax.ws.rs.Path;import javax.ws.rs.GET;import javax.ws.rs.PUT;import javax.ws.rs.Produces;import javax.ws.rs.Consumes;@Path("/myrest")public class DemoResource {        public DemoResource() {    }    /**     * Retrieves representation of an instance of com.demo.DemoResource     * @return an instance of java.lang.String     */    @GET    @Produces("text/html")    public String getHtml() {        return ("Hello, This is demo for REST application");    }    /**     * PUT method for updating or creating an instance of DemoResource     * @param content representation for the resource     * @return an HTTP response with content of the updated or created resource.     */    @PUT    @Consumes("text/html")    public void putHtml(String content) {    }}

The following example demonstrates the use of multiple MIME media types by employing @Produces and @Consumes:

package com.demo;import javax.ws.rs.Path;import javax.ws.rs.GET;import javax.ws.rs.PUT;import javax.ws.rs.Produces;import javax.ws.rs.Consumes;@Path("/sayHello")@Produces("application/xml")public class SayHelloResource {@GETpublic String getXml() {...}@GET@Produces("text/html")public String getHtml() {...}@PUT@Consumes("application/xml") public void putXml(String content) {...} }

In the above example:

  • The getXml resource method will be called for GET requests that specify a MIME type of application/xml. It returns an object of string.
  • The getHtml resource method will be called for GET requests that specify a MIME type of text/html. It returns a string containing text/html.
  • The putXml resource method will be called for PUT requests that return an XML representation.

If the value of a resource method’s @Produces annotation is not supported by the HTTP request, then the resource method will not be invoked. Similarly, if the value of a resource method’s @Consumes annotation does not match the header, the resource method will not be invoked. If these annotations are not present, then any MIME type (*/*) is supported.

Providers

JAX-RS entity providers help in the mapping between entities and associated Java types. The two types of entity providers JAX-RS supports are:

  1. MessageBodyReader: a class that you use to map an HTTP request entity body to method parameters
  2. MessageBodyWriter: a class that you use to map the return value to the HTTP response entity body.

By default, MessageBodyReader and MessageBodyWriter automatically support the following standard types:

  • byte[]: All media types (*/*)
  • java.lang.String: All media types (*/*)
  • java.io.InputStream: All media types (*/*)
  • java.io.Reader: All media types (*/*)
  • java.io.File: All media types (*/*)
  • javax.activation.DataSource: All media types (*/*)
  • javax.xml.transform.Source: For XML Media types only (text/xml, application/xml, application/*+xml)
  • javax.xml.bind.JAXBElement and application-supplied JAXB classes: For XML Media types only (text/xml, application/xml, application/*+xml)
  • MultivaluedMap: Form content (application/x-www-form-urlencoded)
  • StreamingOutput: All media types (*/*), only for MessageBodyWriter

If the application chooses not to use any of the standard types, a method can return an object of response, which is built using the ResponseBuilder class.

JAX-RS allows you to write custom mapping from/to a representation and an entity body. The classes that provide custom mapping are annotated with @Provider and they implement the MessageBodyReader or MessageBodyWriter classes.

An entity provider is a resource class annotated with @Provider that implements the JAX-RS API. You can use the @Provider annotation along with @Produces and @Consumes as demonstrated below:

[email protected]@Consumes("application/stockquote+xml")public void createStock(Stock stock ) {......}........@Provider@Produces("application/stockquote+xml")public class StockProvider implements MessageBodyWriter> {.....}@Provider@Consumes("application/stockquote+xml")public class StProvider implements MessageBodyReader> {.....}

The value of the stock parameter will be mapped from the request entity body using the StProvider class. Similarly, when you need to map from the representation to the response entity body, you use the StockProvider class.

Context

To help you get a handle on the deployment environment and the individual requests, JAX-RS provides different types of context to resource classes and providers. You use the annotation @Context to get the Java types related to a request or response. These are the different types of context JAX-RS provides:

  • UriInfo: UriInfo provides information about the components of a request URI, whether static or dynamic. However, the information provided is valid only per request. An instance of UriInfo is injected into the field of a class or into the parameter of a method using @Context.
    @Path("/sayHello")public class SayHello {    public SayHello() {    }    @GET    @Produces("text/html")    public String getHtml(@Context UriInfo uri) {        MultivaluedMap map = uri.getQueryParameters();        Set set = map.keySet();        Iterator iterator = set.iterator();        String content = "";        while (iterator.hasNext()) {            content = content + map.get(iterator.next()) + "*******";        }        return content;    }}

    The above example uses context to inject an object of type UriInfo as the method parameter whose methods provide access to request URI information.

  • Headers: HttpHeaders provide information about a request header. Generally, the information is provided in the form of a map. An instance of HttpHeader is injected into the field of a class or into the parameter of a method using @Context. The methods of HttpHeaders provide access to request information.
    @[email protected]{"text/html"}public String getHeaders(@Context HttpHeaders headers) {................}
  • Request: The request interface is provided for negotiating content and evaluating certain preconditions. A request instance is injected into the field of a class or into the parameter of a method using @Context.
  • Security: SecurityContext provides information about the security context of the request. With SecurityContext, you can retrieve information about the user, group, and roles from the request. It also helps in identifying the authentication scheme used and determining whether the request is using HTTPS or not. An instance of SecurityContext is injected into the field of a class or into the parameter of a method using the @Context annotation.

All the above contexts are available to all containers where a JAX-RS root resource class or a provider is deployed. Apart from these standard ones, you can use @Context to specify container-specific resources too. For example, in a Servlet container-based environment, you can use @Context to inject dependencies of type HttpServletRequest, HttpServletResponse, ServletConfig, and ServletContext.

Table 5 lists the Java annotations defined by JAX-RS.

Table 5. List of Annotations Defined as Part of JAX-RS

Simple REST-Based Web Services

By defining annotations for creating and consuming REST-based web services, JAX-RS provides good support for building web services in a simple and elegant way. Using the JAX-RS API and annotations, you can expose a POJO as a web service and any request coming to a web resource will be served by a resource class and resource methods. JAX-RS also has the added benefit of sending and receiving objects of any type.

Acknowledgements: The author would like to sincerely thank Mr. Subrahmanya, SV, VP, ECOM Research Group, E&R, for ideas, guidance, support, and constant encouragement and Mr. Parameswaran Seshan, Senior Technical Architect, ECOM Research Group, E&R, for kindly reviewing this article.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles:

©2023 Copyright DevX - All Rights Reserved. Registration or use of this site constitutes acceptance of our Terms of Service and Privacy Policy.

Sitemap