Exercise 2: Advanced JAX-RS/Jersey features
(60 minutes)
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} |
Steps to Follow
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.

- Click Open Project.
Let's create a new resource for system properties:
- In NetBeans, create a new class named PropertyResource by right-clicking
on Source Packages->com.mycompany.jerseyservice and choosing
New->Java Class from the pop-up menu.

- In the New Java Class dialog, set the name to PropertyResource and click
finish.

- Attach
@Path("/properties") annotation to the class.
- NetBeans will indicate there is an error on that line. That is because
the Path annotation needs to be imported. Click on the lightbulb next to
that line and click on "Add import for javax.ws.rs.Path".

- Here is how the code should look like so far:
package com.mycompany.jerseyservice;
import javax.ws.rs.Path;
@Path("/properties")
public class PropertyResource {
}
- Now it will get a bit more interesting. We will add a new method that
will handle HTTP GET requests and return plain text response containing
the name-value pair for the given system property
specified in the last part of the URL. I.e. we want to be able to get
this method invoked for any URL of the following form:
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);
}
Add this method to the PropertyResource class and fix all imports by clicking
on the lightbulbs and following the hints.
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.
- The whole class should now look like this:
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);
}
}
- Run the project.
- The following URL should retrieve a value of "java.home" system property:
http://localhost:8080/jerseyservice/webresources/properties/java.home
Let'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.

- Now, what if we enter a URL containing an invalid property name - i.e.
a system property name that does not exist? Our method handles all requests
conforming to the template. It does not check whether a given property exists.
So, the result will be the same as if the system property did exist and its
value was
null.
Try it for yourself by entering the following URL into the browser as an
example:
http://localhost:8080/jerseyservice/webresources/properties/invalid
The resulting response will be:
invalid = null
- Let's make the code more robust by checking if the property
exists and returning an error if it doesn't. Jersey
makes it easy to return errors by defining Java exceptions corresponding
to the standard HTTP error codes your application can throw. One of those
is
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);
}
- Run the application and try the "invalid" URL again. You will see the
error message.
- To verify that the right status code is returned when the property is
not found, we can use the Poster Add-on for Firefox. To bring up the Poster
window, click on the Poster icon in the Firefox status bar.

- Let's first type in the valid URL (i.e. the URL for the java.home
property) and click the GO button next to the GET action. This sends
the HTTP GET request to a given URL (similarly to when you enter
this URL into a browser.

- We can see the response as well as the HTTP status code. For this
operation the status code is 200 = OK.

- Close the Response dialog and try to change the URL to the "invalid" one:
http://localhost:8080/jerseyservice/webresources/properties/invalid
After 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.

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:
- First we have to create a new JAXB bean to represent a single
system property. To do this, create a new Java class named
PropertyBean in the com.mycompany.jerseyservice
package.
- Annotate the class with the JAXB annotation
@XmlRootElement.
- Add two private String fields -
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.
- Add setters and getters for the two private fields.
- The resulting class should look like this:
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;
}
}
- Now that we have the JAXB bean representing a system property, let's
add another GET method - named
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));
}
}
- Let's run the project and test it using the Poster Firefox Add-on.
In the poster, enter the URL of java.home property.
- Besides this, we will also need to configure the Poster to accept
only a certain MIME type. To do that, click on the GO button next to the
Headers field. This will allow us to add additional attributes to the
HTTP request header.

- In the Request Header dialog put "Accept" into the Name field,
"text/plain" into the Value field, click Add/Change and close the dialog.
This will cause the Poster to specifically request a response of
the text/plain MIME type.

- Now click the GO button next to the GET action. You will see the Poster
will get the standard response produced by the
getProperty()
method of our resource we already saw in the previous steps.
- Let's see what happens if we change the MIME type to some of the new
ones we added by the way of adding the
getJAXBProperty()
method. Close the Response window and click GO next to the Headers field
again.
- This time we will set the Accept attribute to application/xml. To do
this, click on the Accept attribute in the Request Header dialog,
modify its value by typing application/xml into the Value field and
clicking the Add/Change button.

- Close the Request Header dialog and send the request by clicking the GO
button next to the GET action. Now you see the response contains an XML
representation of the system property

- Similarly, you can modify the Accept header attribute to retrieve the
JSON representation of the property (by setting the value to application/json).
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:
- Create a new Java class in the
com.mycompany.jerseyservice
package and name it PropertyWriter.
- Copy the following code into the Java file:
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());
}
}
This class will automatically be picked up by the Jersey framework,
since it is annotated by the @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.
- As you can see, the writter we have created in the last step makes use
of the
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;
}
- Finally, we can remove
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));
}
}
- Run the application and use Poster to send a GET request with
Accept:text/plain in the header - similarly to how we did it for other
MIME types previously. Observe the reception of the correct reponse.
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:
- Add another GET method to the
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;
}
- Note the method is not even annotated by the
@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.
- The updated
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;
}
}
- Jersey framework is able to transform a list of JAXB beans to XML
and JSON, however not to text/plain. So, we need to implement another
message body writer - this time for a list of properties. To do this,
create a new Java class named
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());
}
}
- Let's run the application again and browse to the following URL to see
the list of all system properties:
http://localhost:8080/jerseyservice/webresources/properties
- You can also use Poster to try other MIME types.
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:
- Right-click on Web Pages folder of the project and choose
New->JSP.

- Name it "properties" and click Finish.

- This new JSP will be used to represent the list of properties.
Jersey automatically makes the "model" (i.e. data to be rendered by
this JSP) accessible as a variable named
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" %>
This will enable us to use tags from this library prefixed by "c"
namespace (as defined in the prefix attribute in the above tag).
The tag we are interested in is forEach that will enable
us to iterate through the elements of the property list.
TROUBLESHOOTING: The taglib declaration will be
underlined as an error in NetBeans. Please ignore this, it has no effect
on the lab. It is a minor
issue in NetBeans 6.5 handling maven projects.
The problem is resolved
in NetBeans 6.7.
- Let's change the title of the JSP to "System Properties":
<title>System Properties</title>
- Update the heading with the same:
<h1>System Properties</h1>
- Finally, here is how we can iterate throught the list of properties (passed
in by Jersey in the above mentioned variable named
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>
- The whole page should now look like this:
<%@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>
- Create another new JSP page and name it "property" (this one
will be used to represent a single property).
- Model for this page will be the PropertyBean (representing the
property) itself. Change the title of the page to "System Property - ${it.name}":
<title>System Properties - ${it.name}</title>
- Change the heading to the property name itself - i.e.
${it.name}.
- Under the heading the only text we will put in is the property
value - i.e.
${it.value}
- The whole JSP should look as follows:
<%@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>
- Now we need to add GET methods to our PropertyResource
to return the HTML representations of the list of properties and an
individual property. Open
PropertyResource class by double-clicking on it in the
Project Explorer in NetBeans.
- Right after
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.
- All we need to do in this method is to return a new instance of
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).
- Now add another GET method (this time for the list of properties) named
getHTMLProperties(). Same as the previous one, annotate it with
@Produces("text/html") and make it return Viewable.
- In the body of the method we need to return a new instance of
Viewable similarly to the previous method:
@GET
@Produces("text/html")
public Viewable getHTMLProperties() {
return new Viewable("/properties", getProperties());
}
- To make running the exercise more conveniently, we will make NetBeans open
the browser at the right URL when running the project. To do this,
Right-click on the project, choose Properties (at the end of
the pop-up menu).
- In the Project Properties dialog select the Run category and set
Relative URL to /webresources/properties - this will ensure that each time
we run the application it will open the page with the list of properties.

- Let's run the project.
- You can see that the properties are now rendered into HTML - you can see
a list of properties with the proper links.

- By clicking on a link you can
see the HTML page for the individual property.

Finally we will add operations to make updates to the system properties.
- Add the following method to the
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:
- The first parameter is annotated by the
@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.
- The second parameter should look familiar - it is the one the value
of which will automatically be extracted from the URI.
- The last parameter -
value will be populated by the
body of the request.
The parameter values injection makes the implementation of the PUT method
quite simple.
- Another method we are going to add is the DELETE method. Copy &
paste the following into the
PropertyResource class:
@DELETE
@Path("/{property}")
public void deleteProperty(@PathParam("property") String propertyName) {
checkPropertyExists(propertyName);
System.getProperties().remove(propertyName);
}
This method is very simple. It has no return value, so Jersey will
automatically generate a response with the status code set to 204 =
No content. Otherwise the code should be straightforward.
- That's it - let's run the application again.
- Open the following URL in the web browser to retrieve the list of properties:
http://localhost:8080/jerseyservice/webresources/properties
- Now use Poster to add a new property named "aaa" with the value of
"This is a property value". To do this, fill in the following into the URL
field of the Poster window:
http://localhost:8080/jerseyservice/webresources/properties/aaa
Enter "This is a property value" into the "Content to Send" field,
in the Actions dropdown box select PUT, and click GO next to it.

- In the response dialog observe that the right status code got returned,
(i.e. 201) and also the Location header attribute contains the URI of the new
property.

- Refresh the browser window containing the list of all system
properties and you should see our new property "aaa" added to the beginning
of the list.
- In the Poster, change the value in the "Context to Send" field to
"This is an updated property value" and send another PUT request.
Now you should see status code 200.
- Refresh the brower window and observe that the value of the property
"aaa" got updated.
- Finally, let's send a DELETE request to the same URI by setting
the Action in the Poster window to DELETE and clicking GO next to it.
- Observe that the right status code got returned - 204.
- You can see another identical DELETE request using Poster (by clicking
the GO button again) and see this time we get 404 (i.e. Not Found) status
code back.
- Refresh the browser window containing the list of all properties and
see our "aaa" property is no longer appears listed.
This concludes the second exercise.
Summary
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..
Back to top
Next exercise
|