This document describes an early access implementation of a Java API for RESTful web services development.
The goal of the API is to provide a programming model that enables developers to rapidly build web services in Java (or using the Java Virtual Machine) that are characteristic of the best designed parts of the web. The API and programming model encourage the use of the REST architectural style and the correct use of HTTP 1.1.
The implementation of the API is commonly referred to as a 'runtime'. The runtime is responsible for deploying Plain Old Java Objects (POJOs), which conform to the API and programming model, and dispatching HTTP requests to those Java objects.
The Java documentation for the API can be found here.
Joe Gregorio in his article "How to Create a REST Protocol" presents a prescriptive approach for creating a RESTful Web service. This is an effective way of tackling the problem, so this section follows suit, using the simple employee service example presented in the article to create a RESTful Web service using the API.
Using the prescribed approach, a set of questions are asked in the following order:
This question really means: What are the resources that are identified by the URIs? Using the API, the question becomes: What are the Java classes corresponding to the resources that are identified by the URIs? The simple example presents two resources:
Java classes corresponding to resources are annotated with the
UriTemplate
annotation. The example below shows the employees and employee resources as two Java classes:
@UriTemplate("/employees") public class Employees { ... }
@UriTemplate("/employee/{id}")
public class Employee { ... }
The UriTemplate
annotation defines a URI template as a
URI path relative to the base URI of the container. URI
Template is an Internet-Draft that specifies URI templates to be:
"strings that can be transformed into URIs after embedded variables are substituted. This document defines the structure and syntax of URI Templates."
The API uses URI templates in reverse. Given a set of UriTemplate
annotated Java classes and a URI of an HTTP request, the
runtime deploying the Java classes, will, on receiving the HTTP request, find the most specific Java
class whose URI template matches the URI path of the HTTP request.
NOTE: The matching algorithm of an HTTP request to a Java class is more involved than just matching the URI template as described in this section. It also matches additional properties such as the HTTP method, the media type of the HTTP request, and the acceptable media types for the HTTP response.
Assuming that the Java classes are deployed to the base URI http://example.com/
then the following URIs will match the URI template "/employee/{id}" of
the Employee
class:
http://example.com/employee/1234 http://example.com/employee/johnDoe http://example.com/employee/john/doe
Where the id
variable is substituted, respectively,
with the strings "1234", "johnDoe" and "john/doe". So the Employee
class matches many URIs, as many as there are employees. Whereas only
the URI http://example.com/employees
will match the URI
template "/employees" of the Employees
class.
This question really means: What does a resource consume in terms of HTTP request entities and produce in terms of HTTP response entities, the latter of which are referred to as representations? Or more simply put: What is the information that can be sent and received?
HTTP request entities and representations are identified by MIME media types. (Such MIME media types are used for the Content-Type
HTTP header request/response field or the Accept
HTTP header request field.) The API specifies annotations for declaring what MIME media types may be consumed
(from an HTTP request entity) and produced (by a representation).
The example below shows the Employees
and Employee
classes annotated with
a
ConsumedMime
and
ProduceMime
annotation:
@UriTemplate("/employees") @ConsumeMime("application/employee+xml") @ProduceMime("application/employee+xml") public class Employees { ... }
@UriTemplate("/employee/{id}") @ConsumeMime("application/employee+xml") @ProduceMime("application/employee+xml") public class Employee { ... }
These annotiations declare that the resources consume and produce information identified by the
MIME media type application/employee+xml
. Declaring such annotations on the Java class states that
by default all HTTP methods (see next section) are declared to consume or produce the MIME media types.
More than one MIME media type may be consumed or produced by declaring a comma separated list of MIME media types.
It is also possible to specify the media type at the method level as shown below.
HTTP methods are mapped to Java class methods using the
HttpMethod
annotation. The Java method arguments are populated with information from the
HTTP request, and the Java method return value becomes the HTTP response. The example
below shows the Employees
class that supports the HTTP GET
method to
obtain a list of employees, and the HTTP POST
method to add a new employee.
@UriTemplate("/employees") @ConsumeMime("application/employee+xml") @ProduceMime("application/employee+xml") public class Employees { @HttpMethod public Employees getEmployeeList() { Employees es = ... return es; } @HttpMethod("POST") public Employee createEmployee( Employee employee) { ... return new employee; } }
The names of the Java methods annotated with HttpMethod
are not significant when an argument is supplied with the annotation, otherwise the Java method must begin with a valid and known HTTP method.
The Java method may return void
, T
, Representation<T>
(or an extension of),
HttpResponse
(or an extension of) or Object
that is any type of the former (except void).
Methods that
expect an HTTP body (usually PUT
or POST
) should have a method argument of type T
or
Entity<T>
to accept the HTTP body content, where
T
is the desired format. In this example, we are using JAXB
to convert between the on-the-wire XML data and a Java class representation of the
data.
The API provides support for a number of useful formats:
com.sun.jersey.api.representation
Representation<T>
for
a variety of T
. You can use these classes for returning data from
a method, or develop your own custom class that implements Representation<T>
and return an instance of that class instead. The AbstractRepresentation<T>
class
is provided to simplify the implementation of a custom representation.
InputStream
,
String
, byte[]
,
DataSource
, File
, MimeMultipart
,
FormURLEncodedProperties
, Entry
(ROME library), Feed
(ROME library) and JAXB classesT
for a return type or a method parameter,
Representation<T>
for a return type, or Entity<T>
for a method parameter.
See com.sun.jersey.spi.streaming.TypeStreamingProvider
for documentation
that describes how to add your own custom streaming provider to API.The example below shows the Employee
class that supports the HTTP GET
method to obtain
an employee, the HTTP PUT
method to update an employee, and the HTTP DELETE
method to delete an employee:
@UriTemplate("/employee/{id}") @ConsumeMime("application/employee+xml") @ProduceMime("application/employee+xml") public class Employee { @HttpMethod public Employee getEmployee( @UriParam("id") int id) { Employee e = ... return e; } @HttpMethod public void putEmployee( @UriParam("id") int id, Employee employee) { ... } @HttpMethod public void deleteEmployee( @UriParam("id") int id) { ... } }
Other request information can also be consumed by using method arguments. For example, note the use of the
UriParam
annotation in the example above. Before a method is invoked, the employee ID is extracted
from request URI automatically and cast to the Java primitive type int
. If the employee ID
cannot be cast, then the runtime returns a 400 Bad Request
. So in this example,
the client is restricting employee IDs in the URI to strings that contain only digits. The same technique
can be used for URI query parameters by use of the QueryParam
annotation.
The following types may be annotated with a
UriParam
,
QueryParam
,
HeaderParam
or a
MatrixParam
annotation:
all primitive types (except char
),
primitive wrapper classes (except Character
),
String,
any class that has a static method with the signature valueOf(String)
, and
any class that has a constructor that takes a String parameter.
In addition, the QueryParam
annotation supports List<T>
, where T
is a supported
type as previously stated, for the case where multiple values for a query parameter are present.
It is possible to specify at the level of a Java method what MIME media types are consumed and produced. For example, the
Employees
class may be extended to support additional formats with the following additional methods:
@HttpMethod @ProduceMime("text/xhtml") public DataSource getEmployeeListAsHTML() { ... } @HttpMethod @ConsumeMime("application/x-www-form-urlencoded") public Employee postEmployeeFromForm( FormURLEncodedProperties employee) { ... }
If a client sends an HTTP GET
request to the employees URI stating that the MIME media type text/xhtml
is
preferable over application/employee+xml
, then the getEmployeeListAsHTML
method is invoked
(instead of the getEmployee
method) and returns a representation of the employees as an XHTML document.
If a client sends an HTTP POST
request with a HTTP request entity that was produced from an HTML form, then
the createEmployeeFromForm
method is invoked (instead of the createEmployee
method).
NOTE: A client declares what MIME media types are preferable using the Accept
HTTP request header field.
In the above example, the HTTP request may, for example, include Accept: text/html;q=1.0, application/employees+xml;q=0.6
to indicate that text/html
is preferable over application/employees+xml
.
An application can also return a specific HTTP response. Below is an expanded version of
the Employees
class that uses the HTTP 201 Created
status to indicate that
a new resource has been created.
@UriTemplate("/employees") @ConsumeMime("application/employee+xml") @ProduceMime("application/employee+xml") public class Employees { @HttpMethod public Created postEmployee( Employee employee) { Employee newEmployee = addEmployee(employee); return new Created(new Representation(newEmployee), newEmployee.getUri()); } }
The runtime manages the returning of status codes for many
common errors cases. If a client sends an HTTP POST
request to the employee resource, the runtime returns a 405
Method Not Allowed
response. If a client sends an HTTP PUT
request to the employee resource where the media type of the request
entity is anything other than application/employee+xml
,
then the runtime returns a 415 Unsupported Media Type
response.
Error conditions can be signalled using an exception, for example, the following code can be
used to inform the client that an employee resource has gone.
@HttpMethod("GET")
public Representation<Employee> getEmployee(
@UriParam("id") int id) {
if (!doesEmployeeExist(id)) {
throw new WebApplicationException(410);
}
...
}
The first step in deploying a RESTful Web Service is to compile
your Java classes with APT.
When you compile your resources with APT, the Annotation
Processor is run, which generates some handy artifacts
that make deploying your application easier. One of the
artifacts generated is a WebResources
class. This
class is used by the runtime to load your resources into
the application. The annotation processor can also generate
a web.xml
file that can be used to deploy into a servlet
container, a application.wadl
file, which is a WADL
description of your resources, and a WadlResource
class
that can be used to access the WADL description.
APT can be invoked through an APT ant task supplied with this release.
For APT to find the annotation processor, make sure that thejersey.jar
is on the
classpath passed to APT.
The annotation processor can accept the following options:
Option |
Description |
Command-Line Example |
Default Value |
|
Specify the |
|
|
|
Specify the |
|
|
|
Specify the package to generate the |
|
|
|
Turn off the generation of |
|
|
|
Turn off the generation of the |
|
|
|
Specify the destination directory for the generated |
|
|
All of the examples utilize the APT ant task, so you can refer to them for concrete examples of how to invoke APT
The following is taken from the SimpleServlet example. For the APT ant
task to run, it must have the jersey.jar
and the tools.jar
from Java SE 5 or higher. To execute the APT ant task in Java SE 6 requires that the xEnsorsed="true"
be set on the task. (Note that if there
is a runtime dependency on JAXB 2.1 or JAX-WS 2.1 jars then these jars must be placed in the endorsed directory of the Java SE 6 distribution, or by setting the endorsed directory using the system property "java.endorsed.dirs".)
Notice that the options are specified using the <option>
element.
<taskdef name="rbpt" classname="com.sun.ws.rest.tools.ant.WebResourcesProcessorTask"> <classpath location="${file.reference.jersey.jar}"/> </taskdef> <target name="-pre-compile"> <echo message="Processing resource classes"/> <rbpt fork="true" debug="true" verbose="false" xEndorsed="true" nocompile="false" destdir="${build.classes.dir.real}" sourcedestdir="gen-src" sourcePath="${src.dir}"> <classpath> <path path="${javac.classpath}"/> <pathelement location="${build.web.dir}/WEB-INF/classes"/> </classpath> <option key="webresourcesdestdir" value="../.."/> <option key="webresourcespkg" value="com.sun.ws.rest.samples.servlet.resources"/> <option key="noservlet"/> <source dir="${src.dir}"> <include name="**/*.java"/> </source> </rbpt> <copy todir="${build.classes.dir}"> <fileset dir="${src.dir}" excludes="**/*.java"/> </copy> </target>
To deploy to a Java EE servlet container, you must make sure that the
jersey.jar
and all of the other jars upon which it is
dependent are in the servlet container's classpath. This can be done
by either copying the jar files to one of the servlet container's library
directories, or by simply WARing the jar files into the application WAR file.
The application WAR file should contain your Java classes,
the generated WebResources
class,
and the generated WEB-INF/web.xml
file. This WAR file can then be
deployed to your servlet container.
The following shows the WEB-INF/web.xml
file generated for the
SimpleServlet example. The web.xml
specifies the
com.sun.ws.rest.impl.container.servlet.ServletAdaptor
as the
<servlet-class>. This adapter uses the resourcebean
<init-param> to get the set of Java classes to include in the application.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>Web Application</servlet-name> <servlet-class>com.sun.ws.rest.impl.container.servlet.ServletAdaptor</servlet-class> <init-param> <param-name>resourcebean</param-name> <param-value>com.sun.ws.rest.samples.servlet.resources.WebResources</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Web Application</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping> </web-app>
The following shows the contents of the SimpleServlet sample WAR file.
META-INF/context.xml WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/MasterResourceBean.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/WebResources.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean1.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean2.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean3.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean4.class WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/index.html WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/java.jpg WEB-INF/classes/com/sun/ws/rest/wadl/resource/WadlResource.class WEB-INF/classes/com/sun/ws/rest/wadl/resource/application.wadl WEB-INF/lib/activation.jar WEB-INF/lib/jaxb-api.jar WEB-INF/lib/jaxb-impl.jar WEB-INF/lib/jaxws-api.jar WEB-INF/lib/jdom-1.0.jar WEB-INF/lib/jersey.jar WEB-INF/lib/jsr173_api.jar WEB-INF/lib/jsr250-api.jar WEB-INF/lib/jsr311-api.jar WEB-INF/lib/localizer.jar WEB-INF/lib/mail.jar WEB-INF/lib/persistence-api-1.0.jar WEB-INF/lib/rome-0.9.jar WEB-INF/web.xml index.html
When deploying to a JAX-WS Endpoint container, you do not need the generated
WEB-INF/web.xml
, because a servlet container is not used.
To prevent the generation of this file, you can use the noservlet
option for APT. The following is taken from the SimpleJAXWSEndpoint
example.
It shows how the APT ant task is used with this option.
<target name="-pre-compile">
<echo message="Processing resource classes"/>
<rbpt fork="true" destdir="${build.classes.dir}" xEndorsed="true"
sourcedestdir="gen-src" sourcePath="${src.dir}">
<classpath>
<path path="${javac.classpath}"/>
<pathelement location="${build.dir}"/>
</classpath>
<option key="webresourcesdestdir" value="."/>
<option key="webresourcespkg" value="com.sun.ws.rest.samples.jaxws.resources"/>
<option key="noservlet"/>
<source dir="${src.dir}">
<include name="**/*.java"/>
</source>
</rbpt>
<copy todir="${build.classes.dir}">
<fileset dir="${src.dir}" excludes="**/*.java"/>
</copy>
</target>
To deploy to a JAX-WS endpoint, you must write a simple Main class that can be used to publish the endpoint. The following is the Main class taken from the SimpleJAXWSEndpoint example.
import com.sun.jersey.api.container.ContainerFactory; import javax.xml.ws.Endpoint; import javax.xml.ws.Provider; import javax.xml.ws.http.HTTPBinding; public class Main { /** Creates a new instance of Main */ public Main() { } /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { /** Create a JAX-WS Provider */ Provider provider = ContainerFactory.createContainer(Provider.class, "com.sun.ws.rest.samples.jaxws.resources"); /** Create a JAX-WS Endpoint with the provider */ Endpoint endpoint = Endpoint.create(HTTPBinding.HTTP_BINDING,provider); /** publish the endpoint */ endpoint.publish("http://localhost:9998/endpoint"); System.out.println("JAX-WS endpoint running, visit: http://127.0.0.1:9998/endpoint/start, hit return to stop..."); /** wait of for a CR */ System.in.read(); System.out.println("Stopping JAX-WS endpoint"); /* stop the endpoint */ endpoint.stop(); System.out.println("Server stopped"); } }
The following line uses the ProviderFactory
to create a JAX-WS
Provider
:
Provider provider = ContainerFactory.createContainer(Provider.class, "com.sun.ws.rest.samples.jaxws.resources");
The ContainerFactory.createContainer
method takes two parameters, the
type of container which to create, and package name that contains the WebResources
class. An alternative create
is also available that can be passed a ResourceConfig
should you wish to avoid the apt step.
The following line creates a JAX-WS Endpoint:
Endpoint endpoint = Endpoint.create(HTTPBinding.HTTP_BINDING,provider);
It is passed the HTTPBinding.HTTP_BINDING, which specifies that the XML/HTTP
binding should be used and the provider
created in the previous line.
The following line does the actual publishing of the endpoint:
endpoint.publish("http://localhost:9998/endpoint");
It takes one argument, which is the URL at which the endpoint should be published.
The following line is simply there to wait for the user to press the Return key:
System.in.read();
... before stopping the endpoint with the following line:
endpoint.stop();
When running a JAX-WS Endpoint-based application, the
the API, runtime and dependent jar files, the JAX-WS runtime jar files, the generated WebResources
, and your
Java classes must all be in the classpath.
The SimpleConsole example application shows how to use
the API with the lightweight HTTP server. Note in particular the -pre-compile
target in the
build.xml
file that generates the classes (primarily WebResources
) (as described above).
Note also the use of the noservlet
option to
prevent generation of a web.xml file as in the previous example.
The distribution contains the following examples that utilize the features of the RESTful API (some of which have been described in this document):
In the process of designing and implementing the API, the development team has identified a number of enhancements, planned for future versions of the API, that will improve ease of use and provide new features. In the meantime, we hope that developers will play with the API, build example programs and provide feedback that we may incorporate in future versions. Additional information can be found on the following team members weblogs: