JBoss Development is brought to you in partnership with:

Bill Burke is an engineer and Fellow at Red Hat. He’s been a contributor, lead, and architect for numerous projects within the JBoss ecosystem for over 10 years, his current one being Resteasy, JBoss’s JAX-RS implementation. He has been Red Hat’s representative on the Java EE 5, EJB 3.0, JAX-RS 1.0 and 2.0 specifications. Bill is also the author of O’Reilly’s RESTful Java with JAX-RS and EnterpriseBeans 3.0. View his blog at bill.burkecentral.com Bill has posted 5 posts at DZone. You can read more from them at their website. View Full User Profile

Putting Java to REST

09.22.2008
| 174899 views |
  • submit to reddit

Last month I gave you an Introduction to REST. It was 100% theory, so now its time to see a little bit of REST in action. Since I am primarily a Java programmer, Part II of this series will focus on writing RESTFul Web services using the Java language. REST does not require a specific client or server-side framework in order to write your Web services. All you need is a client or server that supports the HTTP protocol. In Java land, servlets are a fine instrument for building your distributed apps, but can be a bit cumbersome and require a bunch of glue-code and XML configuration to get things going. So, about a year and a half ago, JSR-311, JAX-RS, was started at the JCP to provide an annotation-based framework to help you be more productive in writing RESTFul Web services. In this article, we'll implement various simple Web services using the JAX-RS specification.   

A Simple JAX-RS Service

JAX-RS uses Java annotations to map an incoming HTTP request to a Java method. This is not an RPC mechanism, but rather a way to easily access the parts of the HTTP request you are interested in without a lot of boilerplate code you'd have to write if you were using raw servlets. To use JAX-RS you annotate your class with the @Path annotation to indicate the relative URI path you are interested in, and then annotate one or more of your class's methods with @GET, @POST, @PUT, @DELETE, or @HEAD to indicate which HTTP method you want dispatched to a particular method.

@Path("/orders")
public class OrderEntryService {
@GET
public String getOrders() {...}
}

If we pointed our browser at http://somewhere.com/orders, JAX-RS would dispatch the HTTP request to the getOrders() method and we would get back whatever content the getOrders() method returned.

JAX-RS has a very simple default component model. When you deploy a JAX-RS annotated class to the JAX-RS runtime, it will allocate OrderEntryService object to service one particular HTTP request, and throw away this object instance at the end of the HTTP response. This per-request model is very similar to stateless EJBs. Most JAX-RS implementations support Spring, EJB, and even JBoss Seam integration. I recommend you use one of those component models rather than the JAX-RS default model as it is very limited and you will quickly find yourself wishing you had used Spring, EJB, or Seam to build your services.

Accessing Query Parameters

One problem with the getOrders() method of our OrderEntryService class is that this method could possibly return thousands of orders in our system. It would be nice to be able to limit the size of the result set returned from the system. For this, the client could send a URI query parameter to specify how many results it wanted returned in the HTTP response, i.e. http://somewhere.com/orders?size=50. To extract this information from the HTTP request, JAX-RS has a @QueryParam annotation:

@Path("/orders")
public class OrderEntryService {
@GET
public String getOrders(@QueryParam("size")
@DefaultValue("50") int size)
{
... method body ...
}
}

The @QueryParam will automatically try and pull the "size" query parameter from the incoming URL and convert it to an integer. @QueryParam allows you to inject URL query parameters into any primitive type as well as any class that has a public static valueOf(String) method or a constructor that has one String parameter. The @DefaultValue annotation is an optional piece of metadata. What it does is tell the JAX-RS runtime that if the "size" URI query parameter is not provided by the client, inject the default value of "50".

Other Parameter Annotations

There are other parameter annotations like @HeaderParam, @CookieParam, and @FormParam that allow you to extract additional information from the HTTP request to inject into parameters of your Java method. While @HeaderParam and @CookieParam are pretty self explanatory, @FormParam allows you to pull in parameters from an application/x-www-formurlencoded request body (an HTML form). I'm not going to spend much time on them in this article as the behave pretty much in the same way as @QueryParam does.

Path Parameters and @PathParam

Our current OrderEntryService has limited usefulness. While getting access to all orders in our system is useful, many times we will have web service client that wants access to one particular order. We could write a new getOrder() method that used @QueryParam to specify which order we were interested in. This is not good RESTFul design as we are putting resource identity within a query parameter when it really belongs as part of the URI path itself. The JAX-RS specification provides the ability to define named path expressions with the @Path annotation and access those matched expressions using the @PathParam annotation. For example, let's implement a getOrder() method using this technique.

@Path("/orders")
public class OrderEntryService {
@GET
@Path("/{id}")
public String getOrder(@PathParam("id") int orderId) {
... method body ...
}
}

The {id} string represents our path expression. What it initially means to JAX-RS is to match an incoming's request URI to any character other than '/'. For example http://somewhere.com/orders/3333 would dispatch to the getOrder() method, but http://somewhere.com/orders/3333/entries would not. The "id" string names the path expression so that it can be referenced and injected as a method parameter. This is exactly what we are doing in our getOrder() method. The @PathParam annotation will pull in the information from the incoming URI and inject it into the orderId parameter. For example, if our request is http://somewhere.com/orders/111, orderId would get the 111 value injected into it.

More complex path expressions are also supported. For example, what if we wanted to make sure that id was an integer? We can use Java regular expressions in our path expression as follows:

@Path("{id: \\d+}")

Notice that a ':' character follows "id". This tells JAX-RS there is a Java regular expression that should be matched as part of the dispatching process.

Content-Type

Our getOrder() example, while functional, is incomplete. The String passed back from getOrder() could be any mime type: plain text, HTML, XML, JSON, YAML. Since we're exchanging HTTP messages, JAX-RS will set the response Content-Type to be the preferred mime type asked for by the client (for browsers its usually XML or HTML), and dump the raw bytes of the String to the response output stream.

You can specify which mime type the method return type provides with the @Produces annotation. For example, let's say our getOrders() method actually returns an XML string.:

@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
@Produces("application/xml")
public String getOrder(@PathParm("id") int orderId)
{
...
}
}

Using the @Produces annotation in this way would cause the Content-Type of the response to be set to "application/xml".

Content Negotiation

HTTP clients use the HTTP Accept header to specify a list of mime types they would prefer the server to return to them. For example, my Firefox browser sends this Accept header with every request:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

The server should interpret this string as that the client prefers html or xhtml, but would accept raw XML second, and any other content type third. The mime type parameter "q" in our example identifies that "application/xml" is our 2nd choice and "*/*" (anything) is our third choice (1.0 is the default "q" value if "q" is not specified).

JAX-RS understands the Accept header and will use it when dispatching to JAX-RS annotated methods. For example, let's add two new getOrder() methods that return HTML and JSON.

@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
@Produces("application/xml")
public String getOrder(@PathParm("id") int orderId) {...}

@GET
@Path("{id}")
@Produces("text/html")
public String getOrderHtml(@PathParm("id") int orderId) {...}

@GET
@Path("{id}")
@Produces("application/json")
public String getOrderJson(@PathParm("id") int orderId) {...}
}

If we pointed our browser at our OrderEntryService, it would dispatch to the getOrderHtml() method as the browser prefers HTML. The Accept header and each method's @Produces annotation is used to determine which Java method to dispatch to.

Content Marshalling

Our getOrder() method is still incomplete. It still will have a bit of boilerplate code to convert a list of orders to an XML string that the client can consume. Luckily, JAX-RS allows you to write HTTP message body readers and writers that know how to marshall a specific Java type to and from a specific mime type. The JAX-RS specification has some required built-in marshallers. For instance, vendors are required to provide support for marshalling JAXB annotated classes (JBoss RESTEasy also has providers for JSON, YAML, and other mime types). Let's expand our example:

@XmlRootElement(name="order")
public class Order {
@XmlElement(name="id")
int id;

@XmlElement(name="customer-id")
int customerId;

@XmlElement("order-entries")

List<OrderEntry> entries;
...
}

@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
@Produces("application/xml"
public Order getOrder(@PathParm("id") int orderId) {...}
}

JAX-RS will see that your Content-Type is application/xml and that the Order class has a JAXB annotation and will automatically using JAXB to write the Order object to the HTTP output stream. You can plug in and write your own marshallers using the MessageBodyReader and Writer interfaces, but we will not cover how to do this in this article.

Response Codes and Custom Responses

The HTTP specification defines what HTTP response codes should be on a successful request. For example, GET should return 200, OK and PUT should return 201, CREATED. You can expect JAX-RS to return the same default response codes.

Sometimes, however, you need to specify your own response codes, or simply to add specific headers or cookies to your HTTP response. JAX-RS provides a Response class for this.

@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
public Response getOrder(@PathParm("id") int orderId)
{
Order order = ...;
ResponseBuilder builder = Response.ok(order);
builder.expires(...some date in the future);
return builder.build();
}
}

In this example, we still want to return a JAXB object with a 200 status code, but we want to add an Expires header to the response. You use the ResponseBuilder class to build up the response, and ResponseBuilder.build() to create the final Response instance.

Exception Handling

JAX-RS has a RuntimeException class, WebApplicationException, that allows you to abort your JAX-RS service method. It can take an HTTP status code or even a Response object as one of its constructor parameters. For example

@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
@Produces("application/xml")
public Order getOrder(@PathParm("id") int orderId) {
Order order = ...;
if (order == null) {
ResponseBuilder builder = Response.status(Status.NOT_FOUND);
builder.type("text/html");
builder.entity("<h3>Order Not Found</h3>");
throw new WebApplicationException(builder.build();
}
return order;
}
}

In this example, if the order is null, send a HTTP response code of NOT FOUND with a HTML encoded error message.

Beyond WebApplicationException, you can map non-JAXRS exceptions that might be thrown by your application to a Response object by registering implementations of the ExceptionMapper class:

public interface ExceptionMapper<E extends Throwable>
{
Response toResponse(E exception);
}

For example, lets say we were using JPA to locate our Order objects. We could map javax.persistence.EntityNotFoundException to return a NOT FOUND status code.

@Provider
public class EntityNotFoundMapper
implements ExceptionMapper<EntityNotFoundException>
{
Response toResponse(EntityNotFoundException exception)
{
return Response.status(Status.NOT_FOUND);
}
}

You register the ExceptionMapper using the Application deployment method that I'll show you in the next section.

Deploying a JAX-RS Application

Although the specification may expand on this before Java EE 6 goes final, it provides only one simple way of deploying your JAX-RS applications into a Java EE environment. First you must implement a javax.ws.rs.core.Application class.

public abstract class Application
{
public abstract Set<Class<?>> getClasses();
public Set<Object>getSingletons()
}

The getClasses() method returns a list of classes you want to deploy into the JAX-RS environment. They can be @Path annotated classes, in which case, you are specifying that you want to use the default per-request component model. These classes could also be a MessageBodyReader or Writer (which I didn't go into a lot of detail), or an ExceptionMapper. The getSingletons() method returns actual instances that you create yourself within the implementation of your Application class. You use this method when you want to have control over instance creation of your resource classes and providers. For example, maybe you are using Spring to instantiate your JAX-RS objects, or you want to register an EJB that uses JAX-RS annotations.

You tell the JAX-RS runtime to use the class via a <context-param> within your WAR's web.xml file

<context-param>
<param-name>javax.ws.rs.core.Application</param-name>
<param-value>com.smoke.MyApplicationConfig</param-value>
</context-param>

Other JAX-RS Features

There's a bunch of JAX-RS features I didn't go into detail with or mention. There's a few helper classes for URI building and variant matching as well as classes for encapsulating HTTP specification concepts like Cache-Control. Download the specification and take a look at the Javadocs for more information on this stuff.

Test Drive JAX-RS

You can test drive JAX-RS through JBoss's JAX-RS implementation, RESTEasy available at http://jboss.org/resteasy.

About the Author

Bill Burke is an engineer and Fellow at JBoss, a division of Red Hat. He is JBoss's representative on JSR-311, JAX-RS and lead on JBoss's RESTEasy implementation.

Published at DZone with permission of its author, Bill Burke.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Bill Burke replied on Mon, 2008/09/29 - 8:47am in response to: cowwoc

[quote=cowwoc]

It looks like I didn't explain myself properly. What I was trying to say is that in return for learning how to use JPA, versus say JDBC, I get to leverage the power of an ORM.

[/quote]

JPA is ORM, JAX-RS is OMM (Object-Message-Mapping).

[quote=cowwoc]

On the other hand, in return for learning how to use JAX-RS I get very little versus say the Servlet API. There is nothing wrong with REST or creating a framework for it. I'm just saying that given how little REST adds on top of the HTTP stack, your learning curve must be equally small.

[/quote]

Maybe I'm wrong, but I think JAX-RS annotations are pretty self describing and looking at a basic example you can figure out what is going on.  Also,

The Servlet 2.x ( nor the Servlet 3.0) specification has no notion of expression mapping.  You can't even do something as simple as:

/orders/*/entries

You also cannot dispatch in servlet land based on mime-type and Accept header.  Just these 2 things alone are a non-trivial amount of code.

cowwoc replied on Mon, 2008/09/29 - 11:32am

Okay, you've convinced me. I'll give it a try :)

Bruno Girin replied on Fri, 2008/10/03 - 11:54am

Excellent article, thanks! What about a follow up article to explain how to integrate authentication and authorisation with SAX-RS? For example, I might allow anybody to use GET or HEAD methods but would want to restrict access to PUT, DELETE or POST to authenticated users. How would that work?

Also, when using @Path and other annotations that require a parameter, do those parameters have to be resolved at compile time or can they be resolved at run time? For instance, could I do something like this: @Path(MyConfigSingleton.getInstance().getValue("myPath")) with MyConfigSingleton being a class that picks up those values from a configuration resource?

 Finally, how do you handle exceptions that are thrown by the JAX-RS service? For instance, in your example, how would you handle a request that tries to access a path other than the one handled by OrderEntryService? For example, if a user typed /order (without the 's') instead of /orders, how can I handle this gracefully?

Bill Burke replied on Mon, 2008/10/06 - 11:21am

I don't know about Apache CXF, but RESTEasy and Jersey are implemented as a servlet.  Since they are implemented as a servlet, you use web.xml security constraints to set up authentication.  Authorization (role-based-security) is a bit trickier as the servlet spec has *very* limited url-pattern expressions.  In this case you could use either EJBs, Spring, or some other component model that has a security infrastructure, to do your fine-grain security.

For your second question, @Path is limited to whatever Java annotations support.  So I don't think your particular example would work.

 Your third question, how can you handle exceptions thrown by the JAX-RS service?  Well, I did talk about ExceptionHandlers. Beyond that, there really is no standard way to errors on the server.  Errors do usually have a HTTP mapped resposne.  For example, a bad representation sent to the server might get a status code of 400 or 406.  If the server doesn't support a particular http method, 405 would be returned with an Allow header, etc.  In your example, the client would receive a status code of 404 NOT FOUND.  All this stuff is standard HTTP.  I suggest reading the HTTP 1.1 specification.  It really is an easy read.

cowwoc replied on Mon, 2008/10/06 - 11:35am

ExceptionMapper needs a way to catch RuntimeException but exclude JAX-RS-specific exceptions. It doesn't look like there is a way to do this currently.

Bruno Girin replied on Mon, 2008/10/06 - 7:05pm in response to: Bill Burke

Bill, thanks for the reply. I think web.xml authentication is a non-starter because apart from the CLIENT-CERT authentication method, web.xml authentication relies on human interaction and is very limited: nothing more elaborate than user name and password. As an example, it would be good to be able to do more complex stuff, such as supporting custom systems in the Authorization HTTP request header (which interestingly enough is used for authentication). I suspect for the time being this would need to be done through custom code. Same for role based authorisation: custom code.

I suppose that for an implementation that is done as a servlet, the best way to provide authentication and authorisation would be through a couple of servlet filters: you would go through the security management code before hitting the web service methods and you would have access to the original HttpRequest object. Either filter could then return an HTTP status 401 or 403.

Ed Barnes replied on Thu, 2009/06/04 - 6:31pm

Bill, thanks so much.  This article has been the single best resource for JAX-RS I've found, and I think I've found  all of them now.  There is one thing that I can't reconcile between your two articles.  The first article suggest that a sub resource should be represented by it's uri ex: <order>...<customer>http://host/customer/123</customer>...</order>

Using JAX-RS and JAXB as described in the second article doesn't provide a simple solution to do this.  That is, a JAXB object of Order with a customer id field, is going to marshal just the field value.  Even if you try to manually build the uri at run time, you have no real way of knowing the path to the another resource dynamically and hard coding could breakdown if you include versions and such in your path.  

So I'm torn between the efficency of the tools described in the second article and the principles in the first.  Please let me know if there's a good solution to get JAXB content marshalling and URIs for sub resources.  Thanks.

Subramanyam Yel... replied on Tue, 2010/07/27 - 8:30pm

Is there way to timeout request (RESTful URI) that has landed on server? The session-timeout property which is default 30, doesn’t help since it is for user session validation,  however I don’t find equivalent property in WEB.XML or in ServletAdaptor to timeout request which has gone bad (Service Mode) on tomcat server.

Looking for solution set timeout at RESTful URI level, so that behaving badly can timeout and tomcat thread release.

Thanks,
subbu

Suresh Bhaskaran replied on Fri, 2010/08/27 - 7:45pm in response to: Subramanyam Yeleswarapu

Yes, there is a way! This is how I did it in my rest easy client: But please note that I am using the client proxy approach as described in Bill Burke's Book: "Restful Java with JAX-RS". It's all done in your spring appContext xml file Step#1: Hook up the restEasy client proxy with HttpClient bean id="myClientProxy" class="org.jboss.resteasy.client.spring.RestClientProxyFactoryBean" p:serviceInterface="com.zyrisinc.googlecheckout.service.AccountService" p:baseUri="${account.provider.baseUri}" p:httpClient-ref="httpClient" Step# Define the parameters of httpClient bean id="httpClient" class="org.apache.commons.httpclient.HttpClient" constructor-arg bean class="org.apache.commons.httpclient.params.HttpClientParams" p:authenticationPreemptive ="false" p:connectionManagerClass="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" constructor-arg constructor-arg bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" p:params-ref="httpConnectionManagerParams" constructor-arg bean bean id = "httpConnectionManagerParams" class="org.apache.commons.httpclient.params.HttpConnectionManagerParams" p:maxTotalConnections="${http.maxTotalConnections}" p:connectionTimeout="${http.connectionTimeout}" p:soTimeout="${http.soTimeout}" Note: I omitted opeing and closing cone brackets as they do not appear in this comment for some reason. soTimeout is the socketTimeout you are looking for. Java throws a ReadTimeout when the happens. Hope that helps. Suresh Bhaskaran.

Sombriks Dark replied on Thu, 2011/01/13 - 8:00am

please note that newer jersey versions don't use the context-param anymore. jersey 1.4 uses init-param and the property name is "javax.ws.rs.Application".

See http://download.java.net/maven/2/com/sun/jersey/contribs/bill-burke-book/ex03_1/1.5-ea09/ex03_1-1.5-ea09-project.zip

for more details.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.