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.
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.
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:
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. |
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 |
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! ";
}
}
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.
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 {
@GET
public String getXml() {...}
@GET
@Produces("text/html")
public String getHtml() {...}
@PUT
@Consumes("application/xml")
public void putXml(String content) {...}
}
In the above example:
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.
By default, MessageBodyReader and MessageBodyWriter automatically support the following standard types:
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:
....
...
@PUT
@Consumes("application/stockquote+xml")
public void createStock(Stock stock ) {
......
}
........
@Provider
@Produces("application/stockquote+xml")
public class StockProvider implements MessageBodyWriter<Stock> {
.....
}
@Provider
@Consumes("application/stockquote+xml")
public class StProvider implements MessageBodyReader<St> {
.....
}
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.
@Path("/sayHello")
public class SayHello {
public SayHello() {
}
@GET
@Produces("text/html")
public String getHtml(@Context UriInfo uri) {
MultivaluedMap<String, String> map = uri.getQueryParameters();
Set<String> set = map.keySet();
Iterator<String> 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.@GET
@Produces{"text/html"}
public String getHeaders(@Context HttpHeaders headers) {
................
}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 |
| 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. |
| DevX is a division of Internet.com. © Copyright 2010 Internet.com. All Rights Reserved. Legal Notices |