JavaOne Hands-On LabProject Jersey: Building RESTful Web Services in JavaLAB-5542Copyright 2009 Sun Microsystems |
Last Updated: May 20th, 2009
Project Jersey is the reference implementation of JAX-RS (JSR-311) - Java API for building RESTful Web Services. This API makes RESTful Web Services development with Java and some other VM-based languages very easy and straightforward. Besides the features defined by the standards, Jersey defines additional set of API's that make the developers' life even easier.
In this lab, we will start from basics - showing how to build a Hello World type of application and then show some more advanced features of JAX-RS and Jersey such as support for various media types, basic error handling, MVC support and client-side API's.
Copyright 2009 Sun Microsystems, Inc. All rights reserved. Sun, Sun Microsystems,
the Sun logo, Solaris, Java, the Java Coffee Cup logo, JavaOne, the JavaOne logo,
and all Solaris-based and Java-based marks and logos are trademarks or registered
trademarks of Sun Microsystems, Inc. in the United States and other countries.
This hands-on lab assumes you have some basic knowledge of, or programming experience in, the following technologies:
You will need to refer to the accompanying presentation 5542_restwebservice.pdf during the lab.
Please install the following set of software. If you have any questions on installation, please feel free to send questions to the lab forum mentioned below.
Please refer to Exercise 0 of this lab for the guidance on how to install these.
In addition to the contents of this lab's zip file you should have the following installed on your computer:
Java SE Development Kit, version 1.5.0_11 or 1.6.0 or more recent. Most likely it is already installed on your machine. If not, you can get it from here. Make sure JAVA_HOME environment variable points to the root of the Java installation.
Maven 2.0.9 or a more recent stable release. See Maven Download for detailed instructions.
NetBeans 6.5 or 6.5.1, either the "Java" or the "All" bundle from http://www.netbeans.org/downloads/index.html. These bundles include GlassFish v2(.1), so there is no need to install GlassFish separately.
Firefox 3 web browser - can be downloaded from www.firefox.com.
After installing Firefox we need to install the Poster add-on, which will enable us to send any kind of HTTP requests to our RESTful service and see the raw responses.

We also need to install the Maven plugin into the NetBeans IDE. Here is how to do this:
When NetBeans has started, select Tools->Plugins and select "Maven" in the Available Plugins tab.

To make sure the Maven NetBeans plugin is using the right version of Maven:
Set the patch to your Maven installation in External Maven Home field.

In NetBeans click on the Services tab.

Now
expand the Servers node in the Services tab (if possible). If you don't
see GlassFish v2 under that node, right-click it and choose Add Server.

In the Add Server Instance dialog that will pop up, select GlassFish V2 and click Next.

This excercise provides a quick introduction into the basics of the JAX-RS/Jersey programming model. For more details, please see the Jersey Tutorial available on the web.
In this lesson we are going to build a very basic "Hello world" type of RESTful web service. It will provide an introduction to how you can quickly get started building RESTful sevices using Jersey. You will see the basic concepts of the JAX-RS/Jersey programming model.
RESTful web services are services that are built to work best on the web.
Representational State Transfer (REST) is an
architectural style that specifies constraints, such as the uniform
interface, that if applied to a web service induce desirable
properties, such as performance, scalability, and modifiability, that
enable services to work best on the Web. In the REST architectural
style, data and functionality are considered resources, and these
resources are accessed using Uniform Resource Identifiers (URIs),
typically links on the web. The resources are acted upon by using a set
of simple, well-defined operations. The REST architectural style
constrains an architecture to a client-server architecture, and is
designed to use a stateless communication protocol, typically HTTP. In
the REST architecture style, clients and servers exchange
representations of resources using a standardized interface and
protocol. These principles encourages RESTful applications to be
simple, lightweight, and have high performance.
RESTful web services typically map the four main HTTP methods to the
operations they perform : create, retrieve, update, and delete. The
following table shows a mapping of HTTP methods to the operations they
perform.
| HTTP Method | Operations Performed |
|---|---|
| GET | Get a resource |
| POST | Create a resource or other operations as it has no defined semantics |
| PUT | Create or update a resource |
| DELETE | Delete a resource |
Project Jersey is an open source, production quality reference implementation for JSR 311: JAX-RS: The Java API for RESTful Web Services. It implements support for the annotations defined in JSR-311, making it easy for developers to build RESTful web services with Java and the Java VM. Jersey also adds additional features not specified by the JSR.
The goals and philosophy of Project Jersey are to:
In this exercise we're going to see what it takes to build a new RESTful web service from scratch using Jersey.
Step 1: Creating a New Project
In the New Project wizard, select Maven->Maven Project and click Next.

Expand "Archetypes from remote Maven Repositories", select "Jersey Archetype: Web App with WAR packaging" and click Next.

In the next screen of the wizard, name the project as "jerseyservice" and click the Finish button.

NOTE: If this is the first time you are creating a new project based on Jersey maven archetype, it may take more than a minute to create the new project after hitting the Finish button. During this time maven automatically downloads all the necessary dependencies.
Without the NetBeans IDE
All that we've done so far can easily be done without NetBeans. The steps are equivalent to executing the following on the command line in the directory where you want to create the new project:
prompt> mvn archetype:generate -DarchetypeCatalog=http://download.java.net/maven/2 -DinteractiveMode=false -DarchetypeArtifactId=jersey-quickstart-webapp -DarchetypeGroupId=com.sun.jersey.archetypes -DgroupId=mycompany.com -DartifactId=jerseyservice -Dpackage=com.mycompany
Step 2: Building the Project
Now that the new project is created, let's build it.

Since this is the very first time we are building a project depending on Jersey libraries, the build may take several minutes. So, in the meantime, we will go ahead to the step 3 and explore the project structure.
Without the NetBeans IDE
To build the project without NetBeans you can execute the following in the project root directory:
prompt> mvn install
Step 3: Exploring the Project Structure
Let's look more closely at how our new project looks like.
Under Web Pages->WEB-INF open the web.xml file for editing by right-clicking on it and selecting Edit from the pop-up menu. This file is a standard web application deployment descriptor.


3 <servlet> 4 <servlet-name>Jersey Web Application</servlet-name> 5 <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> 6 <init-param> 7 <param-name>com.sun.jersey.config.property.packages</param-name> 8 <param-value>com.mycompany.jerseyservice</param-value> 9 </init-param> 10 <load-on-startup>1</load-on-startup> 11 </servlet> 12 <servlet-mapping> 13 <servlet-name>Jersey Web Application</servlet-name> 14 <url-pattern>/webresources/*</url-pattern> 15 </servlet-mapping>
Lines 4 and 5 configure
com.sun.jersey.spi.container.servlet.ServletContainer
as the servlet class. This class is an internal class from the Jersey
framework that can handle the HTTP requests coming from the clients and
route the requests to the implementations of our resources. It knows
where too look for the resource classes from the value of com.sun.jersey.config.property.packages
property. This property is set on lines 7 and 8 of the deployment
descriptor to
com.mycompany.jerseyservice, so
Jersey will automatically look for the resource classes in
that package and all of the subpackages.
Lines 12 to 15 then define a mapping of the servlet
to a URL pattern. It tells the server, request
to which URLs should be delegated to the Jersey servlet. In this case
it is all URLs starting with /webresources/
right after the application context URL prefix.
8 /** Example resource class hosted at the URI path "/myresource" 9 */ 10 @Path("/myresource") 11 public class MyResource { 12 13 /** Method processing HTTP GET requests, producing "text/plain" MIME media 14 * type. 15 * @return String that will be send back as a response of type "text/plain". 16 */ 17 @GET 18 @Produces("text/plain") 19 public String getIt() { 20 return "Hi there!"; 21 } 22 }
The @Path
annotation on line 10 indicates this class is a resource available at
path /myresource relative to the
application servlet URL. The method of this class is annotated by two
annotations (lines 17 and 18). The first one - @GET -
indicates this method should be used to handle HTTP GET requests, the
second one - @Produces - declares
the MIME type of the data the method produces (text/plain in this case).
Step 4: Running the Project
Now that we know how the project looks like and why, let's try to run it. To run the project, we first need to configure it to use GlassFish V2 as the deployment server:
/jerseyservice.
http://localhost:8080/jerseyservice/.
This displays the index.jsp from our project in the browser.
getIt() method will be
called which returns a plain text resonse "Hi there!". So, let's click
on the link to see if it really works.
Without the NetBeans IDE
To run the project without NetBeans you can execute the following in the project root directory:
prompt> mvn install prompt> asadmin start-domain prompt> asadmin deploy target/jerseyservice.war
Step 5: Making Modifications
Let's see how we can customize the project a little bit:

@Path annotation)
for the resource from /myresource to
/hello. Update the comment as well.getIt()
method to getXML(), change the
produced MIME type to text/xml and let the method return
"<greeting>Hello world!</greeting>". Update
the comments as well. Here is how the resulting class should look like:
/** Example resource class hosted at the URI path "/hello" */ @Path("/hello") public class HelloResource { /** Method processing HTTP GET requests, producing "text/xml" MIME media * type. * @return String that will be send back as a response of type "text/xml". */ @GET @Produces("text/xml") public String getXML() { return "<greeting>Hello world!</greeting>"; } }
<p><a href="webresources/hello">Hello resource</a>

In this exercise, we explained the basic concepts of REST architectural style and JAX-RS/Jersey programming model. You saw what it takes to create a simple RESTful web application using Jersey.
So far, we have been using only the very basic functionality of the Jersey framework. Our resource supported just a single HTTP method - GET, and it did not do much - returned a hardcoded value. In the following exercise we will explore some more advanced server-side features.
In this exercise we are going to turn the project we developed in the last exercise into a web application for manipulating system properties. This will allow us to show some of the more advanced JAX-RS and Jersey features. We will do this by adding a new resource class that will contain methods for handling updates to the system properties as well as returning their representations in the form of plain text, XML and even JSON.
Before we start coding, it is important to think about the structure of the URLs we want to expose. Choosing the right URL structure is quite important for RESTful web services.
For this exercise, we will use the following URL structure:
| URL template | Operation | Description |
|---|---|---|
| /properties | GET | List of all system properties |
| /properties/{property} | GET | Returns a representation of a property named {property} |
| PUT | Adds/updates a property named {property} | |
| DELETE | Removes a property named {property} |
Step 1: Creating a New Resource
Make sure you have the project from the last exercise open in NetBeans. If not, follow these steps to open it:
In NetBeans, select File->Open Project and browse to the jerseyservice directory.

Let's create a new resource for system properties:


@Path("/properties") annotation to the class.
package com.mycompany.jerseyservice; import javax.ws.rs.Path; @Path("/properties") public class PropertyResource { }
http://localhost:8080/jerseyservice/webresources/properties/{property}
where {property} is the name of the property we want to retrieve.
Here is how such method looks like:
@GET @Path("/{property}") @Produces("text/plain") public String getProperty(@PathParam("property") String propertyName) { return propertyName + " = " + System.getProperty(propertyName); }
As you can see from the code above, you can have variables surrounded by
curly braces as part of the path template specified in the
@Path annotation. These are then accessible by annotating
any method parameter using the @PathParam annotation.
Jersey then automatically passes the value of that variable into such annotated
method parameter.
package com.mycompany.jerseyservice; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/properties") public class PropertyResource { @GET @Path("/{property}") @Produces("text/plain") public String getProperty(@PathParam("property") String propertyName) { return propertyName + " = " + System.getProperty(propertyName); } }
http://localhost:8080/jerseyservice/webresources/properties/java.homeLet's see if it works. Copy & Paste it into the browser's address bar and hit Enter. You should see your Java home directory path.

null.
Try it for yourself by entering the following URL into the browser as an
example:
http://localhost:8080/jerseyservice/webresources/properties/invalidThe resulting response will be:
invalid = null
com.sun.jersey.api.NotFoundException that we will be using
in this case. We will add a new method to implement the check and call
it from our GET method. The code will look like this (make sure you fix the
imports after updating the code):
private static void checkPropertyExists(String propertyName) { if (!System.getProperties().containsKey(propertyName)) { throw new NotFoundException("Error - Property Not Found: " + propertyName); } } @GET @Path("/{property}") @Produces("text/plain") public String getProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); return propertyName + " = " + System.getProperty(propertyName); }


http://localhost:8080/jerseyservice/webresources/properties/invalidAfter clicking GO, you can see that not only the error message gets returned, but as Poster shows also the status code has the right value - 404 = Not found.

Step 2: Supporting More MIME Types
Let's now try to extend our resource to be able to return several different representations of a system property, based on the requested MIME type. We want to support XML and JSON in addition to the currently supported plain text.
Jersey makes this very easy by providing a support for JAXB beans to represent resources using both XML and JSON formats. All you need to do is return a JAXB bean from the GET operation and Jersey takes care of translating it into the right MIME type the client expects.
So, let's add this functionality to our service:
PropertyBean in the com.mycompany.jerseyservice
package.@XmlRootElement.name and value,
and two public constructors - one with no arguments and the other one taking
name and value as arguments and setting the private
fields.package com.mycompany.jerseyservice; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class PropertyBean { private String name; private String value; public PropertyBean() { } public PropertyBean(String name, String value) { this.name = name; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
getJAXBProperty() to our
resource. This method will make use of the PropertyBean we've created
to support text/xml, application/xml and application/json MIME types:
package com.mycompany.jerseyservice; import com.sun.jersey.api.NotFoundException; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/properties") public class PropertyResource { private static void checkPropertyExists(String propertyName) { if (!System.getProperties().containsKey(propertyName)) { throw new NotFoundException("Error - Property Not Found: " + propertyName); } } @GET @Path("/{property}") @Produces("text/plain") public String getProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); return propertyName + " = " + System.getProperty(propertyName); } @GET @Path("/{property}") @Produces({"text/xml", "application/xml", "application/json"}) public PropertyBean getJAXBProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); return new PropertyBean(propertyName, System.getProperty(propertyName)); } }


getProperty()
method of our resource we already saw in the previous steps.getJAXBProperty()
method. Close the Response window and click GO next to the Headers field
again.

Step 3: Implementing a Message Body Writer
To further improve our code, it would be nice if we could have just one method handling the GET requests, instead of having the two separate ones as we are having currently (one for the plain text and the other one for XML and JSON).
By default, Jersey does not know how to generate a plain text
representation of a JAXB bean - i.e. our PropertyBean class in
this case. However, we can plug this in using the SPI Jersey provides.
What we need to do is to create a "Provider" class implementing
MessageBodyWriter interface and handling the serialization
of PropertyBean plain text representation.
Here is how to do this:
com.mycompany.jerseyservice
package and name it PropertyWriter.package com.mycompany.jerseyservice; import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; @Provider public class PropertyWriter implements MessageBodyWriter<PropertyBean> { public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return MediaType.TEXT_PLAIN_TYPE.equals(mediaType) && PropertyBean.class.isAssignableFrom(type); } public long getSize(PropertyBean t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } public void writeTo(PropertyBean t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { entityStream.write(t.toString().getBytes()); } }
@Provider annotation. When
creating a reponse, Jersey will call the isWritable()
method of this provider to see if it can convert a given type of object
into a stream of a given MIME type. The writeTo() method
is then called by the framework to perform the conversion.toString() method to convert the PropertyBean
to text. Let's override the toString() method in the
PropertyBean to make sure we get the desired output. Add the following code
to the PropertyBean class:
@Override public String toString() { return name + " = " + value; }
getProperty() method from the
PropertyResource class and add "text/plain" MIME type to the
list of MIME types getJAXBProperty() method produces. The
whole class should now look as follows:
package com.mycompany.jerseyservice; import com.sun.jersey.api.NotFoundException; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/properties") public class PropertyResource { private static void checkPropertyExists(String propertyName) { if (!System.getProperties().containsKey(propertyName)) { throw new NotFoundException("Error - Property Not Found: " + propertyName); } } @GET @Path("/{property}") @Produces({"text/plain", "text/xml", "application/xml", "application/json"}) public PropertyBean getJAXBProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); return new PropertyBean(propertyName, System.getProperty(propertyName)); } }
Accept:text/plain in the header - similarly to how we did it for other
MIME types previously. Observe the reception of the correct reponse.Step 4: List of Properties
Now that we are able to retrieve various representations (text, XML, JSON) of a single property, let's see how to return a list of all properties:
PropertyResource class. Let's
name this method getProperties().
This time we won't add the @Path annotation, since we want this
method to be invoked for the path /properties itself (this
path is already attached to the whole class so no need to put it on the
method). The method will return a list of properties (sorted by property
names - using TreeSet) - i.e.
List<PropertyBean> and take no parameters:
@GET public List<PropertyBean> getProperties() { List<PropertyBean> result = new ArrayList<PropertyBean>(System.getProperties().size()); for (Object name : new TreeSet(System.getProperties().keySet())) { result.add(new PropertyBean((String) name, System.getProperty((String) name))); } return result; }
@Produces
annotation. This is because instead of duplicating this annotation on both
GET methods, we will simply move the annotation to the class level
- i.e. remove it even from the first GET method and attach it to the class
instead.PropertyResource class should look as follows:
package com.mycompany.jerseyservice; import com.sun.jersey.api.NotFoundException; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/properties") @Produces({"text/plain", "text/xml", "application/xml", "application/json"}) public class PropertyResource { private static void checkPropertyExists(String propertyName) { if (!System.getProperties().containsKey(propertyName)) { throw new NotFoundException("Error - Property Not Found: " + propertyName); } } @GET @Path("/{property}") public PropertyBean getJAXBProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); return new PropertyBean(propertyName, System.getProperty(propertyName)); } @GET public List<PropertyBean> getProperties() { List<PropertyBean> result = new ArrayList<PropertyBean>(System.getProperties().size()); for (Object name : new TreeSet(System.getProperties().keySet())) { result.add(new PropertyBean((String) name, System.getProperty((String) name))); } return result; } }
PropertyListWriter and
copy & paste the following code into it:
package com.mycompany.jerseyservice; import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.List; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; @Provider public class PropertyListWriter implements MessageBodyWriter<List<PropertyBean>>{ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return MediaType.TEXT_PLAIN_TYPE.equals(mediaType) && List.class.isAssignableFrom(type); } public long getSize(List<PropertyBean> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } public void writeTo(List<PropertyBean> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { StringBuffer result = new StringBuffer(); for(PropertyBean propertyBean : t) { result.append(propertyBean.toString()).append('\n'); } entityStream.write(result.toString().getBytes()); } }
Step 5: Using the MVC Feature of Jersey
The resource we've developed so far is quite hard to play with in a web browser. It would be nice to return an HTML representation as well and do that in such a way that the list of properties would provide direct links to the individual property resources. We can nicely achieve this by the MVC feature of Jersey.
First we will create two simple JSP pages - one for representing the list of properties and the other one for representing a single property:


it. In this case
it will contain the list of system properties. Let's
update this new JSP to render the list of properties properly. First,
right under the line with <%@page> tag, we will
declare a usage of the core JSP tag library as follows:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
forEach that will enable
us to iterate through the elements of the property list.<title>System Properties</title>
<h1>System Properties</h1>
it)
and add links to the individual property resources.
<ul> <c:forEach var="p" items="${it}"> <li><a href="properties/${p.name}">${p.name}</a></li> </c:forEach> </ul>
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>System Properties</title> </head> <body> <h1>System Properties</h1> <ul> <c:forEach var="p" items="${it}"> <li><a href="properties/${p.name}">${p.name}</a></li> </c:forEach> </ul> </body> </html>
<title>System Properties - ${it.name}</title>
${it.name}.${it.value}<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>System Properties - ${it.name}</title> </head> <body> <h1>${it.name}</h1> ${it.value} </body> </html>
PropertyResource class by double-clicking on it in the
Project Explorer in NetBeans.getJAXBProperty() method, add another
GET method named getHTMLProperty() with the same parameter
as the getJAXBProperty() method plus annotated with
@Produces("text/html") and returning
Viewable from com.sun.jersey.api.view package.Viewable, passing the name of the JSP template for representing
a single property as the first parameter, and the data/model itself
(i.e. the PropertyBean object representing the requested
property as the second parameter (to get this we will simply reuse the
getJAXBProperty() method):
@GET @Path("/{property}") @Produces("text/html") public Viewable getHTMLProperty(@PathParam("property") String propertyName) { return new Viewable("/property", getJAXBProperty(propertyName)); }
Make sure you fix imports (to import Viewable class).
getHTMLProperties(). Same as the previous one, annotate it with
@Produces("text/html") and make it return Viewable.
Viewable similarly to the previous method:
@GET @Produces("text/html") public Viewable getHTMLProperties() { return new Viewable("/properties", getProperties()); }



Step 6: Operations for Making Updates
Finally we will add operations to make updates to the system properties.
PropertyResource class:
@PUT @Path("/{property}") public Response setProperty(@Context UriInfo uriInfo, @PathParam("property") String name, String value) { boolean isNew = !System.getProperties().containsKey(name); System.setProperty(name, value); PropertyBean result = new PropertyBean(name, value); if (isNew) { return Response.created(uriInfo.getAbsolutePath()).entity(result).build(); } else { return Response.ok(result).build(); } }
Fix imports and make sure the Context class is imported
from javax.ws.rs.core package.
As you can see, this is a PUT method. The method receives the property name and value as parameters and adds/sets the system property. In this example you can see another way of building the response - instead of returning the entity directly, or throwing exceptions, you can directly build and return the Response object. We are using this to return the right status code based on whether a new property was added (results in status code 201 = Created), or an existing property was set (results in status code 200 = OK). The property itself gets returned in the body of the response. Also, when returning the "Created" status code, we need to pass in the URI of the newly created property. That URI is the same as the request URI, so we can retrieve that from the UriInfo object the method takes as a parameter.
Now, let's look at the parameters more closely:
@Context
annotation - that causes the value of this parameter to be automatically
injected by the Jersey framework. The value will contain information
on the URI of the request that triggered this method.value will be populated by the
body of the request.PropertyResource class:
@DELETE @Path("/{property}") public void deleteProperty(@PathParam("property") String propertyName) { checkPropertyExists(propertyName); System.getProperties().remove(propertyName); }
http://localhost:8080/jerseyservice/webresources/properties/aaaEnter "This is a property value" into the "Content to Send" field, in the Actions dropdown box select PUT, and click GO next to it.


This concludes the second exercise.
In this exercise, you saw how to create a RESTful web service using the features provided by JAX-RS and Jersey. You saw how a resource could produce data in more than one representation. You also saw how to map the resource methods to the HTTP methods of GET, PUT and DELETE.
For the development purpose we saw how we could use the web browser, or Poster to send various HTTP methods and see what the response would be.
In the following exercise, we will see how we can use the Jersey Client API to consume the services we've created so far..
In this exercise we will see how to consume a RESTful web service using the Jersey Client API. We will see how easily we can send requests to a service and get back a response.
We will create a Java Swing
Application, which will consume the jerseyservice
we created in the previous exercise. Using this application we will be able
to list the system properties, and update/add/delete a particular property.
In the previous exercise we have seen how to create a RESTful Web Service using maven. To show how you can use Jersey without Maven, in this exercise we will download the required Jersey jars manually and use them in an ant-based project.
Step 1: Creating a Client Project
In order to save some time, the lab provides a pre-built skeleton application, which already contains all of the UI components. The only thing we will be adding during this lab is the business logic. We will start by opening this pre-built project and walking through its structure a bit.
jerseyclient project located in
<lab_root>/restwebservice/exercises/exercise3.
com.mycompany.jerseyclient package has three classes:
MainWindow - Implements the main window of the application
containing a list box for listing all the properties and buttons to perform
operations on the properties.
EditPropWindow - Implements a dialog window used to
display/gather user input for a particular property.
DataProvider - A simple class containing methods that
should perform the actual operations of retrieving, updating and deleting
the properties - the UI components delegate to these methods - so this
class is the only one we will need to touch.All these classes are reasonably documented, so you can browse through their code to find out more.
DataProvider.getPropertyNames() method, which currently always
returns hard-coded values - property1, property2 and property3. The same call
is made when you click on Refresh button.EditPropWindow dialog will appear.
You can enter some name and value. Upon clicking OK, a call to
DataProvider.setPropertyValue() is made passing the property name
and value you entered. Currently this method does nothing. The application also
does a refresh (by calling DataProvider.getPropertyNames()
to update the list box content).DataProvider.getPropertyValue() passing the property name.
This method currently always returns "dummy". Once user changes the value
in the dialog and clicks OK, the application calls
DataProvider.setPropertyValue() and does a refresh.DataProvider.deleteProperty()
(which currently does nothing) and does a refresh.Step 2: Adding the Required Libraries to the Classpath
Before we start properly implementing methods of the DataProvider class,
we need to make sure we have the required Jersey jar files on the classpath.
Every release of Jersey provides a snapshot of
the dependencies page
showing which libraries you need to depend on when using certain features of Jersey.
For developers not using maven, it provides links for downloading the JAR files directly.
We will use the Jersey Client API. To unmarshall the data from XML we will
use JAXB (similarly to using it in jerseyservice for
marshalling to XML/JSON). As a result, we will need the following jar files for this exercise:
For your convenience, the needed files can be found in lib
directory under the root of jerseyclient project.
jerseyclient project
and choose "Add JAR/Folder..."

Step 3: Implementing DataProvider Methods
Now we can finally get to the main topic of this exercise. We will use the
Jersey Client API to properly implement methods of the DataProvider
class.
DataProvider class for editing by double-clicking on it
in the Projects tab.WebResource class that makes it
easy to invoke operations on web resources (such as PropertiesResource
we developed in the previous exercise). Let's declare a static variable
of type WebResource we will use to access the properties resource:
private static WebResource propsResource = Client.create().resource( "http://localhost:8080/jerseyservice/webresources/properties");
PropertyBean class from the jerseyservice project
we developed in the previous exercise by right-clicking on it in the Projects tab,
choosing Copy from the pop-up menu and then right-clicking on com.mycompany.jerseyclient
package, choosing Paste->Refactor Copy and clicking Refactor in the Copy Class dialog.
We will use this class for representing a single property. Since it is a JAXB bean,
Jersey will be able to automatically unmarshall XML in server responses into
its instances.DataProvider class and replace getPropertyNames()
method implementation with the following:
List<PropertyBean> properties = propsResource.accept("application/xml") .get(new GenericType<List<PropertyBean>>() {}); String[] propNames = new String[properties.size()]; int i = 0; for (PropertyBean property : properties) { propNames[i] = property.getName(); i++; } return propNames;
accept() method of WebResource class to indicate
what media types our client accepts and then issue an HTTP GET request by
calling get() method on the resource. We are indicating what
type the response should be unmarshalled into by passing an instance of
GenericType representing List<PropertyBean>.
This ensures Jersey will try to process the server response using an
appropriate reader (in this case JAXB)
to construct a result of the requested type - so we can assign the returned
result directly to a variable of List<PropertyBean>.
The rest of the code is just constructing an array of property names from
the list of property beans.get() method, WebResource defines
methods corresponding to the remaining HTTP methods - most importantly put()
and delete(). We can get to a particular sub-resource by calling
path() method on a resource. So now it should be straightforward
how to implement the remaining methods of DataProvider. Replace
the deleteProperty() implementation with the following:
propsResource.path(propertyName).delete();
getPropertyValue() should also be straightforward:
PropertyBean property = propsResource.path(propertyName) .accept("application/xml").get(PropertyBean.class); return property.getValue();
setPropertyValue() method:
propsResource.path(propertyName).put(propertyValue);
package com.mycompany.jerseyclient; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; import java.util.List; /** Class that handles all the communication with the RESTful web service * for manipulating remote system properties. */ public class DataProvider { private static WebResource propsResource = Client.create().resource( "http://localhost:8080/jerseyservice/webresources/properties"); /** Sets the property of a given name to a given value. * * @param propertyName Name of the property to set or create. * @param propertyValue A new value of the property. */ public static void setPropertyValue(String propertyName, String propertyValue) { propsResource.path(propertyName).put(propertyValue); } /** Retrieves the value of a given property. * * @param propertyName Name of the property to retrieve the value of. * @return value of the property. */ public static String getPropertyValue(String propertyName) { PropertyBean property = propsResource.path(propertyName) .accept("application/xml").get(PropertyBean.class); return property.getValue(); } /** Deletes the property of a given name. * * @param propertyName Name of the property to delete. */ public static void deleteProperty(String propertyName) { propsResource.path(propertyName).delete(); } /** Retrieves names of all properties. * * @return array of names of the properties. */ public static String[] getPropertyNames() { List<PropertyBean> properties = propsResource.accept("application/xml") .get(new GenericType<List<PropertyBean>>() {}); String[] propNames = new String[properties.size()]; int i = 0; for (PropertyBean property : properties) { propNames[i] = property.getName(); i++; } return propNames; } }
jerseyservice application has been deployed.
This concludes the third exercise.
In this exercise, you saw how the Jersey Client API could be used to consume a RESTful Web Service. You have seen how to:
path() method. get() , put() and delete() methods. accept() method.
Congratulations! You have successfully completed LAB-5542: Project Jersey Hands-on lab.
You can send technical questions via email to the public Jersey users alias: