Representational State Transfer (REST) is an architectural style created based on how the Web works. Applied to web services, it tries to put the Web back into web services. To design a RESTful web service, you need to know Hypertext Transfer Protocol (HTTP) and Uniform Resource Identifiers (URIs), and to observe a few design principles. You need to think in terms of resources so as not to betray the principles of REST.
In a REST based architecture everything is a resource. A resource is accessed via a common interface based on the HTTP standard methods. In an REST architecture you typically have a REST server which provides access to the resources and a REST client which accesses and modify the REST resources. Every resource should support the HTTP common operations. Resources are identified by global ID’s (which are typically URIs).
REST allows that resources have different representations, e.g. text, xml, json etc. The rest client can ask for specific representation via the HTTP protocol (Content Negotiation).
This tutorial explains how to develop a RESTful Web Services in J2EE 6 with the JAX-RS reference implementation Jersey.
The REST architectural style was developed in parallel with HTTP/1.1, based on the existing design of HTTP/1.0. The main types of requests standardized in HTTP are GET, POST, PUT, DELETE. These are also called verbs, or methods. HTTP defines four other methods that are less frequently used: HEAD, TRACE, OPTIONS, CONNECT, but we won’t discuss these types of requests.
What we will see is a very simple Web Application for Sum calculation.
Ingredients
- Java SE 6
- Maven 2
- Jersey 1.13
1. Types definition
While SOAP-based services rely on WSDL to describe the format of possible requests for a given web service, REST doen’t need of it. Sometimes can be useful define the types of the exchanced messages in a .xsd
file.
Then, let’s create the file SumService.xsd
under /src/resources/xsd.
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="/SumService" xmlns:tns="/SumService" elementFormDefault="qualified"> <xs:element name="SumRequest"> <xs:complexType> <xs:sequence> <xs:element name="element" type="xs:int" minOccurs="1" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="SumResponse"> <xs:complexType> <xs:sequence> <xs:element name="sum" type="xs:int" minOccurs="1" maxOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
2. The POM file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.madbit.rest</groupId> <artifactId>rest-ex</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>war</packaging> <name>REST example</name> <description>REST example in J2EE 6</description> <dependencies> <!-- REST Web Services --> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> <version>2.0-m05</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.13-b01</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> <version>1.13-b01</version> </dependency> <!-- XML Binding(JAXB) for Web Services --> <dependency> <groupId>javax.xml</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaDirectory>${basedir}/src/main/resources/xsd</schemaDirectory> <generateDirectory>${basedir}/target/generated/</generateDirectory> <generatePackage>org.madbit.rest.ws</generatePackage> <extension>true</extension> </configuration> </plugin> </plugins> </build> </project>
3. JAXB2
Using maven-jaxb2-plugin
it’s possible to delegate to Maven the generation of the classes that will be used by Maven to marshall and unmarshall the XML body of the REST requests and responses.
So, after the execution of the command mvn generate-sources
the classes SumRequest.java
and SumResponse.java
will be generated under target/generated/ directory.
Everyone of these classes will be generated with the JAXB2 annotations. The main one is @XmlRootElement. This annotations are used by JAXB2 to understand how to map and convert an XML data format in a class and vice versa.
Below are showed the generated classes.
SumRequest.java
package org.madbit.rest.ws; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "element" }) @XmlRootElement(name = "SumRequest") public class SumRequest { @XmlElement(type = Integer.class) protected List<Integer> element; public List<Integer> getElement() { if (element == null) { element = new ArrayList<Integer>(); } return this.element; } }
SumResponse.java
package org.madbit.rest.ws; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "sum" }) @XmlRootElement(name = "SumResponse") public class SumResponse { protected int sum; public int getSum() { return sum; } public void setSum(int value) { this.sum = value; } }
4. The REST Endpoint class
At this point we need to develop the endpoint class of the Web Service. To do this you don’t have to write plumbing code to digest HTTP requests, nor create HTTP responses by hand.
All can be done just using the JAX-RS API provided by J2EE 6.
JAX-RS is a very elegant API allowing you to describe a resource with only a few annotations. Resources are POJOs that have at least one method annotated with @javax.ws.rs.Path.
Below the code of the POJO class SumEndpoint.java
which implement the endpoint of the SumService:
package org.madbit.rest; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.madbit.rest.ws.SumRequest; import org.madbit.rest.ws.SumResponse; @Path("/services") public class SumEndpoint { @POST @Path("sum") @Produces(MediaType.APPLICATION_XML) @Consumes(MediaType.APPLICATION_XML) public SumResponse getSum(SumRequest request) { SumResponse response = new SumResponse(); List<Integer> elements = request.getElement(); int sum = 0; for (Integer element: elements) sum += element; response.setSum(sum); return response; } }
As you can see that the REST service doesn’t implement any interface nor extend any class; the only mandatory annotation to turn a POJO into a REST service is @Path
. JAX-RS relies on configuration by exception, so it has a set of annotations to configure the default behavior. Following are the requirements to write a REST service:
- The class must be annotated with
@javax.ws.rs.Path
- To add EJB capabilities to a REST service, the class has to be annotated with @javax.ejb.Stateless
- The class must be defined as public, and it must not be final or abstract
- Root resource classes (classes with a
@Path
annotation) must have a default public constructor. Non-root resource classes do not require such a constructor - The class must not define the
finalize()
method
The @Path
annotation’s value is a relative URI path. When used on classes, these are referred to as root resources, providing the roots of the resource tree and access to subresources.
4.1 HTTP Methods
The class can implements several web services using all the available HTTP methods, like GET, POST, PUT, DELETE, just adding the right JAX-RS Annotation (@GET, @POST, @PUT, @DELETE) before the method. For example:
@Path("/services") public class SumEndpoint { @POST @Path("sum") @Produces(MediaType.APPLICATION_XML) @Consumes(MediaType.APPLICATION_XML) public SumResponse getSum(SumRequest request) { SumResponse response = new SumResponse(); List<Integer> elements = request.getElement(); int sum = 0; for (Integer element: elements) sum += element; response.setSum(sum); return response; } @POST @Path("sub") @Produces(MediaType.APPLICATION_XML) @Consumes(MediaType.APPLICATION_XML) public SubResponse getSub(SubRequest request) { SubResponse response = new SubResponse(); List<Integer> elements = request.getElement(); int sum = 0; for (Integer element: elements) sum -= element; response.setSum(sum); return response; } @GET @Path("random") @Consumes(MediaType.APPLICATION_XML) public int getRandom() { Random randomGenerator = new Random(); return randomGenerator.nextInt(100); } }
More in general, the meaning of the methods difined in the HTTP specification is the following:
To create, update, and delete a book resource, why not use the common HTTP verbs? For example:
• Use POST on data (in XML, JSON, or text format) to create a resource
• Use GET to read a resource
• Use PUT on data to update a resource
• Use DELETE to delete a resource
By using HTTP verbs, we have access to all possible Create, Read, Update, Delete (CRUD) actions on a resource.
4.2 Consuming and Producing Content Types
With REST, a resource can have several representations; a message can be represented as a web page, XML data, or an image showing the album cover. JAX-RS specifies a number of Java types that can represent a resource such as String, InputStream and JAXB beans. The @javax.ws.rs.Consumes and @javax.ws.rs.Produces annotations may be applied to a resource where several representations are
possible. It defines the media types of the representation exchanged between the client and the server.
JAX-RS has a MediaType class that acts like an abstraction for a MIME type. It has several methods and defines the constants listed below:
Constant name | MIME type |
APPLICATION_ATOM_XML | “application/atom+xml” |
APPLICATION_FORM_URLENCODED | “application/x-www-form-urlencoded” |
APPLICATION_JSON | “application/json” |
APPLICATION_OCTET_STREAM | “application/octet-stream” |
APPLICATION_SVG_XML | “application/svg+xml” |
APPLICATION_XHTML_XML | “application/xhtml+xml” |
APPLICATION_XML | “application/xml” |
MULTIPART_FORM_DATA | “multipart/form-data” |
TEXT_HTML | “text/html” |
TEXT_PLAIN | “text/plain” |
TEXT_XML | “text/xml” |
WILDCARD | “*/*” |
5. JAX-RS Reference Implementation – Jersey
Jersey the JAX-RS Reference Implementation for building RESTful Web services. It is an open source project under dual CDDL and GPL licenses. Jersey is extensible through its API and also provides a client API.
Other implementations of JAX-RS are also available such as CXF (Apache), RESTEasy (JBoss), and Restlet (a senior project that existed even before JAX-RS was finalized).
6. Web deployment descriptor
The last step is create a servlet in the web.xml
for the com.sun.jersey.spi.container.servlet.ServletContainer
class.
You should indicate as param-value the package containing the REST endpoint classes (in our case the package is org.madbit.rest
).
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>AppStore Gateway</display-name> <servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>org.madbit.rest.ws</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app>
7. Call the REST APIs
The servlet is mapped on the URL “/rest/*”, this means that all the REST endpoints defined will be accessible under “localhost:8080/rest/” where path is one of the values specified using @Path annotation.
The implemented REST API getSum()
can be called by a client at the URI http://localhost:8080/rest/services/sum
.
An example of XML request is the following:
<?xml version="1.0" encoding="ISO-8859-1"?> <SumRequest xmlns="/SumService"> <element>1</element> <element>4</element> </SumRequest>
A valid response of the request above is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <SumResponse xmlns="/SumService"> <sum>5</sum> </SumResponse>