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 {
public static final JAXBContext context = ...
@HttpMethod("GET")
public Representation<Employees> getEmployeeList() {
Employees es = ...
return new JAXBRepresentation(es, context, "application/employee+xml");
}
@HttpMethod("POST")
public Representation<Employee> createEmployee(
Representation<Employee> employee) {
...
return new JAXBRepresentation(employee, context, "application/employee+xml");
}
}
The names of the Java methods annotated with HttpMethod
are not significant, but they are required to return
either void or Representation<T>. Methods that
expect an HTTP body (usually PUT or POST) should have a method argument of type
Representation<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.ws.rest.api.representationRepresentation<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 in a
Representation<T> used for a method parameter.
See com.sun.ws.rest.spi.representation.provider.RepresentationProvider for documentation
that describes how to add your own custom input representation 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 {
public static final JAXBContext context = ...
@HttpMethod("GET")
public Representation<Employee> getEmployee(
@UriParam("id") int id) {
Employee e = ...
return new JAXBRepresentation(e, context, "application/employee+xml");
}
@HttpMethod("PUT")
public void updateEmployee(
@UriParam("id") int id,
Representation<Employee> employee) {
...
}
@HttpMethod("DELETE")
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 or a
QueryParam 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("GET")
@ProduceMime("text/xhtml")
public Representation<DataSource> getEmployeeListAsHTML() {
...
}
@HttpMethod("POST")
@ConsumeMime("application/x-www-form-urlencoded")
public Representation<Employee> createEmployeeFromForm(
Representation<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 access additional request information and add
additional information to
responses by using the
HttpContext. 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 {
@Resource
HttpContext context;
...
@HttpMethod("POST")
public Representation<Employee> createEmployee(
Representation<Employee> employee) {
Employee newEmployee = addEmployee(employee);
HttpResponse response = context.getHttpResponse();
response.setStatus(201);
response.getHttpHeaders().put("Location", newEmployee.getUri());
return new JAXBRepresentation(newEmployee, context, "application/employee+xml");
}
}
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.
Returning application-specific status codes such as 201
Created (see example above), 300 Multiple Choices, and 410 Gone
is possible with the current API, but we hope to simplify it in future releases.
Error conditions can be signalled using an exception, for example, the following code can be
used to inform the client that
an employee does not exist or has gone.
@HttpMethod("GET")
public Representation<Employee> getEmployee(
@UriParam("id") int id) {
if (!doesEmployeeExist(id)) {
throw new WebApplicationException(
"Employee " + id + " is gone", 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 RESTBeansResources 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 the command line, or you can use the APT ant
task included with JAX-WS.
The documentation for JAX-WS's APT ant task is available.
For APT to find the annotation processor,
make sure that the restbeans-impl.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 through the JAX-WS APT ant task.
The following is taken from the SimpleServlet example. For the APT ant
task to run, it must have the jaxws-tools.jar and the tools.jar
from JDK 1.5 or higher.
Notice that the options are specified using the <option>
element.
<path id="apt.classpath.id">
<pathelement location="${file.reference.tools.jar}"/>
<pathelement location="${file.reference.jaxws-tools.jar}"/>
</path>
<taskdef name="apt" classname="com.sun.tools.ws.ant.Apt">
<classpath refid="apt.classpath.id"/>
</taskdef>
<target name="-pre-compile">
<apt fork="true" debug="true" verbose="false"
nocompile="false"
destdir="${build.classes.dir.real}"
sourcedestdir="gen-src"
sourcePath="${src.dir}">
<classpath>
<path refid="apt.classpath.id"/>
<path path="${javac.classpath}"/>
<pathelement location="${build.web.dir}/WEB-INF/classes"/>
</classpath>
<option key="restbeansdestdir" value="../.."/>
<option key="restbeanpkg" value="com.sun.ws.rest.samples.servlet.resources"/>
<source dir="${src.dir}">
<include name="**/*.java"/>
</source>
</apt>
</target>
To deploy to a Java EE servlet container, you must make sure that the
restbeans-impl.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 RESTBeansResource 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>RESTBeans 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.RESTBeansResources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>RESTBeans Application</servlet-name>
<url-pattern>/restbean/*</url-pattern>
</servlet-mapping>
</web-app>
The following shows the contents of the SimpleServlet sample WAR file.
/index.html /meta-inf/context.xml /meta-inf/manifest.xml /WEB-INF/web.xml /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/samples/servlet/resources/MasterResourceBean.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/RESTBeansResources.class /WEB-INF/classes/com/sun/ws/rest/wadl/resource/application.wadl /WEB-INF/classes/com/sun/ws/rest/wadl/resource/WadlResource.class /WEB-INF/lib/activation.jar /WEB-INF/lib/jsr250-api.jar /WEB-INF/lib/localizer.jar /WEB-INF/lib/mail.jar /WEB-INF/lib/restbeans-impl.jar /WEB-INF/lib/wadl2java.jar
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.
<path id="apt.classpath.id">
<pathelement location="${file.reference.tools.jar}"/>
<pathelement location="${file.reference.jaxws-tools.jar}"/>
</path>
<taskdef name="apt" classname="com.sun.tools.ws.ant.Apt">
<classpath refid="apt.classpath.id"/>
</taskdef>
<target name="-pre-compile">
<apt fork="true" destdir="${build.classes.dir}"
sourcedestdir="gen-src" sourcePath="${src.dir}">
<classpath>
<path refid="apt.classpath.id"/>
<path path="${javac.classpath}"/>
<pathelement location="${build.dir}"/>
</classpath>
<option key="restbeansdestdir" value="."/>
<option key="restbeanpkg" value="com.sun.ws.rest.samples.jaxws.resources"/>
<option key="noservlet"/>
<source dir="${src.dir}">
<include name="**/*.java"/>
</source>
</apt>
</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.ws.rest.api.container.jaxws.ProviderFactory;
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 = ProviderFactory.create("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 = ProviderFactory.create("com.sun.ws.rest.samples.jaxws.resources");
The ProviderFactory.create method takes one parameter,
which is a String, specifying the package that contains the RESTBeansResource
class. An alternative create is also available that can be passed a Set<Class>
of your Java classes, 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 RESTBeansResources, 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 RESTBeansResources) (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: