Friday Nov 16, 2007
Friday Nov 16, 2007
By Jakub Podlesak and Paul Sandoz
This Tech Tip will show you how to write RESTful web services in Java that conform to the JAX-RS: Java API for RESTful Web Services (JSR-311) specification and its reference implementation - Jersey. You'll learn some of the principles of Representational State Transfer (REST) and get introduced to JAX-RS and Jersey.
The tip uses a sample application to demonstrate some of the JAX-RS concepts and techniques. You can obtain the sample by downloading the latest Jersey snapshot from the Jersey downloads page. The code examples in the tip are taken from the source code of the sample application (which is included in the download package).
An Introduction to RESTful Web Services
Representational State Transfer (REST) is a style of software architecture for distributed systems such as the World Wide Web. The term was introduced in the doctoral dissertation of Roy Fielding in 2000, and has since come into widespread use in the networking community. An important concept in REST is the existence of resources, each of which can be referred to using a global identifier, that is, a URI. In order to manipulate these resources, components of the network, clients and servers, communicate using a standardized interface such as HTTP and exchange representations of these resources.
RESTful web services are services built using the RESTful architectural style. Building web services using the RESTful approach is emerging as a popular alternative to using SOAP-based technologies for deploying services on the Internet, due to its lightweight nature and the ability to transmit data directly over HTTP.
RESTful Web Service Principles
A RESTful web service is based on the following principles:
HTTP GET method to retrieve a corresponding web page
(resource representation) and displays it.JAX-RS and Jersey
JAX-RS provides a standardized API for building RESTful web services in Java. The API basically provides a set of annotations and associated classes and interfaces. Applying the annotations to Plain Old Java Objects (POJOs) enables you to expose web resources. The API is not yet complete. The final version should become part of Java EE 6. You can find more information about JAX-RS in the jsr311 project.
Jersey is a reference implementation of JAX-RS. You can find downloadable distributions of Jersey on the Jersey project downloads page. If you select the latest Jersey snapshot and unzip it, you will see that the Jersey implementation is bundled with several examples demonstrating its use. Let's examine one of those examples.
An Example of JAX-RS in Use: The Bookmark Application
One of the examples distributed with Jersey is a bookmark
application. You'll find it in the examples/Bookmark
subdirectory. The application uses the JAX-RS API to maintain
information about bookmarks saved by users. If you run the
application and specify a specific user, the application returns
data similar to the following:
{sdesc":"test desc","userid":"testuserid","uri": "http://java.sun.com","ldesc":"long test description"}Notice that the application returns the data in JavaScript Object Notation (JSON) format.
If you navigate below the examples/Bookmark subdirectory to the resources subdirectory, you'll find the following resources for the application:
UsersResource: Represents a list of usersUserResource: Represents a specific userBookmarksResource: Represents a list of bookmarks for
a specific userBookmarkResource: Represents a specific bookmarkRecall that to address a resource in REST you specify its URI. However, to communicate with a resource, you also need to specify a communication protocol such as HTTP. Here are the URIs and HTTP methods that correspond to the resources in the Bookmark application:
Resource |
URI Path |
HTTP Methods |
||
|---|---|---|---|---|
UsersResource |
/users |
GET |
||
UserResource |
/users/{userid} |
GET, PUT, DELETE |
||
BookmarksResource |
/users/{userid}/bookmarks |
GET, POST |
||
BookmarkResource |
/users/{userid}/bookmarks/{bmid} |
GET, PUT, DELETE |
||
To understand some of the basics of the JAX-RS, let's look
at two of these resources: UsersResource and UserResource.
UsersResource
Here is a snippet of source code from the UsersResource class:
@UriTemplate("/users/") public class UsersResource { @HttpContext UriInfo uriInfo; @PersistenceUnit(unitName = "BookmarkPU") EntityManagerFactory emf; /** Creates a new instance of Users */ public UsersResource() { } public List<UserEntity> getUsers() { return emf.createEntityManager().createQuery( "SELECT u from UserEntity u").getResultList(); } @UriTemplate("{userid}/") public UserResource getUser(@UriParam("userid") String userid) { return new UserResource( uriInfo, emf.createEntityManager(), userid); } @HttpMethod("GET") @ProduceMime("application/json") public JSONArray getUsersAsJsonArray() { JSONArray uriArray = new JSONArray(); UriBuilder ub = null; for (UserEntity userEntity : getUsers()) { ub = (ub == null) ? uriInfo.getBuilder() : ub.clone(); URI userUri = ub. path(userEntity.getUserid()). build(); uriArray.put(userUri.toString()); } return uriArray; } }Notice that the UsersResource class is annotated by the
@UriTemplate("/users/") annotation. @UriTemplate is a JAX-RS
annotation that identifies the URI path for the resource. Here
the annotation identifies the URI path as /users/:
@UriTemplate("/users/")Annotating the class with a @UriTemplate annotation makes the
class a "Root resource class." It also means that for client
requests that access the /users/ URI path, this resource is
responsible for providing appropriate responses. Note too that
the /users/ URI path is the bootstrap URI path for the entire
Bookmark web application.
Another JSR-311 annotation in the UsersResource class is
@HttpContext.
@HttpContext UriInfo uriInfo;
This annotation injects information into a class field or method
parameter. In the UsersResource class, @HttpContext injects
information about the URI into the uriInfo variable, which is
then available to provide pertinent information about the URI.
UsersResource Methods
The UsersResource class has two methods, getUser and
getUsersAsJsonArray. For the moment, let's skip getUser and
focus on getUsersAsJsonArray. The getUsersAsJsonArray method
returns the URIs for all user resources. Notice that the
method is annotated with two JSR 311 annotations: @HttpMethod
and @ProduceMime. The @HttpMethod annotation indicates that
the annotated method should be used to handle HTTP requests.
The annotation also specifies the type of HTTP request to
which the method will respond. In this example, the annotation
specifies that the getUsersAsJsonArray method serves HTTP GET
requests.
@HttpMethod("GET")Methods like this that serve REST requests are called "Resource methods".
The @ProduceMime annotation specifies the MIME types that a
method can produce. Here, the annotation specifies that the
getUsersAsJsonArray method returns a JSONArray object containing
an array of URIs for all existing user resources.
@ProduceMime("application/json")
Get Users Resources
|
The JSON array object that the method returns to a client might
look like this:
["http://localhost:8080/Bookmark/resources/users/joe", "http://localhost:8080/Bookmark/resources/users/mary"]This JSON array contains URIs, that is, links, to two user
resources, joe and mary.
The getUser method gets information about a specific user. For
example, if a client wants to get information about user joe,
the client accesses the resource at its URI -- in this case,
http://localhost:8080/Bookmark/resources/users/joe. Recall that
the UsersResource class serves all requests for paths beginning
with /users/, including the URI path for joe, that is,
/users/joe.
Here it's important that the getUser method is annotated with
@UriTemplate("{userid}/"). This makes the method a "Sub-resource
locator". Also the getUser method is annotated with @UriParam.
As a result, when the getUser method is invoked, a userid from
the current request URI path is injected into the userid
parameter.
Notice that there is no @HttpMethod annotation associated with
the getUser method. Because of that, the output of the method is
considered a Resource class object. This means that request
processing will be delegated to the Resource class and
appropriate @HttpMethod-annotated methods will be looked up
there. Because the getUser method returns a UserResource object:
public UserResource getUser(@UriParam("userid") String userid) { return new UserResource(...)an appropriate method in the UserResource class is invoked.
Get User Resources
|
UserResource
As just mentioned, request processing for the getUser method in
the UsersResource class is delegated to an appropriate method in
a newly instantiated UserResource object. Here is a snippet of
code from the UserResource class showing one of its methods,
getUser.
@HttpMethod("GET") @ProduceMime("application/json") public JSONObject getUser() throws JSONException { if (null == userEntity) { throw new NotFoundException( "userid " + userid + "does not exist!"); } return new JSONObject() .put("userid", userEntity.getUserid()) .put("username", userEntity.getUsername()) .put("email", userEntity.getEmail()) .put("password", userEntity.getPassword()) .put("bookmarks", uriInfo.getBuilder().path("bookmarks").build()); } Notice that this method also is annotated by @HttpMethod("GET")
and @ProduceMime("application/json"). Here the getUsers method
serves HTTP GET requests and returns a JSONObject object.
The JSONObject object contains a representation of a particular
user, for instance, the representation of the user whose userid
is joe.
You're encouraged to examine the rest of the source code in
UserResource. You'll notice additional JSR 311 annotations, such
as @ConsumeMime, which identifies the MIME types that a method
can accept.
Building and Deploying the Sample Code
The sample code for the tip is available as a NetBeans project. You can build an deploy the sample from the NetBeans IDE or from the command line. In either case:
<sample_install_dir>/jersey, where
<sample_install_dir> is the directory where you installed the
sample application. For example, if you extracted the
contents to C:\ on a Windows machine, then your newly created
directory should be at C:\jersey. Building and Deploying the Sample Code in NetBeans
Building and Deploying the Sample Code From the Command Line
AS_HOME environment variable to the GlassFish v2 installation
directory, for example, (here shown in bash syntax): export AS_HOME= <GF_install_dir><GF_install_dir> is the directory where you installed
GlassFish v2.<sample_install_dir>/jersey directory to
the /examples/Bookmark directory. Build the Bookmark
application by entering the following command on the command
line (here shown in bash syntax): AS_HOME/lib/ant/bin/ant run-on-glassfishRunning the Sample Code
You can run the deployed Bookmark application as follows using Curl, a command line HTTP tool.
curl -i --data "{\"userid\":\"techtip\",\"username\": \"TechTip User\",\"email\":\"techtip@example.com\", \"password\":\"TEST\"}" -H Content-type:application/json -X PUT http://localhost:8080/Bookmark/resources/users/techtip/HTTP GET request is dispatched to the getUser
method in the UsersResource class, which instantiates a new
UserResource object. The request is further dispatched to the
putUser method. HTTP/1.1 204 No Content X-Powered-By: Servlet/2.5 Server: Sun Java System Application Server 9.1_01 Date: Thu, 01 Nov 2007 14:31:53 GMT curl -i -X GET http://localhost:8080/Bookmark/resources/users/getUsersListAsJson method of the
UsersResource class. HTTP/1.1 200 OK X-Powered-By: Servlet/2.5 Server: Sun Java System Application Server 9.1_01 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 01 Nov 2007 14:34:07 GMT ["http:\/\/localhost:8080\/Bookmark\/resources\/users\ /techtip"] curl -i -X GET http://localhost:8080/Bookmark/resources/users/techtip/ HTTP/1.1 200 OK X-Powered-By: Servlet/2.5 Server: Sun Java System Application Server 9.1_01 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 01 Nov 2007 14:35:38 GMT {"userid":"techtip","username":"TechTip User", "email":"techtip@example.com","password":"TEST", "bookmarks":"http:\/\/localhost:8080\/Bookmark\/resources \/users\/techtip\/bookmarks"}Summary
This tip demonstrated how you can write RESTful web services in Java that conform to the JAX-RS: Java API for RESTful Web Services (JSR-311) specification. You can learn more about JAX-RS in the jsr311 project. You can learn more about Jersey, the reference implementation of JAX-RS, in the Jersey project.
About the Authors
Jakub Podlesak is a member of the Jersey project team. Previously, he participated in the development of Metro, the GlassFish v2 web services stack, as a member of the WS-Policy team.
Paul Sandoz is the co-spec lead and implementation lead for JSR 311: Java API for RESTful Web Services. He has participated in the W3C, ISO, and ITU-T standards organizations and contributed various performance-related technologies and improvements to the GlassFish web services stack, particularly in standardization, implementation, integration, and interoperability of Fast Infoset.