Create Rest Apis With Jax-Rs
Create Rest Apis With Jax-Rs
In REST API Design Tutorial, we learned to put the REST principles onto the design process of a network
application. In this post, we will learn to create REST APIs for the same application using JAX-RS 2.0 (Java API for
RESTful Services).
Table of Contents
1. JAX-RS Specification
JAX-RS provides portable APIs for developing, exposing, and accessing Web
applications designed and implemented in compliance with principles of REST
architectural style.
The Java EE 6 release took the first step towards standardizing RESTful web service APIs by introducing a Java
API for RESTful web services (JAX-RS) [JSR 311].
JAX-RS ensures the portability of REST API code across all Java EE-compliant application servers. The latest
version is JAX-RS 2.0 [JSR 339], which was released as part of the Java EE 7 platform.
JAX-RS focuses on applying Java annotations to plain Java objects. JAX-RS has annotations to bind specific URI
patterns and HTTP operations to specific methods of your Java class. It also has annotations that can help you
handle input/output parameters.
As we already said that JAX-RS is a specification; it means we need to have its implementation to run REST API
code. Some of the popular JAX-RS implementations available today are:
Jersey
RESTEasy
Apache CXF
Restlet
2. JAX-RS Annotations
Let’s go through some essential annotations provided by JAX-RS 2.0.
2.1. @Path(‘resourcePath’)
The @Path annotation is used to match the URI path, which is relative to the base URI. It can be specified on
resource class or method.
@Path("/configurations")
public class ConfigurationResource
{
@Path("/{id}")
@GET
public Response getConfigurationById(@PathParam("id") Integer id) {
//…
}
}
The above example sets the path to base URL + /resourcePath. The base URL is based on the application name,
the servlet, and the URL pattern from the web.xml configuration file.
2.2. @POST
The @POST annotated method will handle the HTTP POST requests on the matching resource path.
@POST
@Consumes("application/xml")
public Response createConfiguration(Configuration config) {
//...
}
2.3. @PUT
The @PUT annotated method will handle the HTTP PUT requests on the matching resource path.
@PUT
@Consumes("application/xml")
public Response updateConfiguration(@PathParam("id") Integer id, Configuration config){
//...
}
2.4. @GET
The @GET annotated method will handle the HTTP GET requests on the matching resource path.
@GET
@Path("/{id}")
public Response getConfigurationById(@PathParam("id") Integer id){
//...
}
2.5. @DELETE
The @DELETE annotated method will handle the HTTP DELETE requests on the matching resource path.
@DELETE
@Path("/{id}")
public Response deleteConfiguration(@PathParam("id") Integer id){
//...
}
@DELETE
@Path("/{id}")
public Response deleteConfiguration(@PathParam("id") Integer id){
//...
}
In the above example, the value of id from /{id} will match to @PathParam("id") Integer id.
For example, URI HTTP DELETE /configurations/22312 will be mapped to the above method and id will be
populated with the value 22312.
2.7. @Produces
The @Produces annotation defines which MIME type is delivered by annotated resource methods. It can be
defined at class level as well as method level.
If defined at the class level, all methods inside the resource class will be returning the same MIME type, if not
overridden in any method.
@Path("/configurations")
@Produces("application/xml")
public class ConfigurationResource {
//...
}
2.8. @Consumes
The @Consumes annotation defines which MIME type is consumed by annotated resource method.
@POST
@Consumes("application/xml")
public Response createConfiguration(Configuration config) {
//...
}
2.9. @Context
To build HATEOAS links, JAX-RS 2.0 provides UriInfo class which can be obtained using the @Context annotation.
@Context
UriInfo uriInfo;
By default the JAX-RS runtime will automatically support the methods HEAD and
OPTIONS, if not explicitly implemented.
For HEAD the runtime will invoke the implemented GET method (if present) and ignore
the response entity (if set).
The OPTIONS method can return a response with a set of supported resource methods in
the ‘Allow’ header.
Open new project wizard from File > New > Maven Project
Select maven-archtype-webapp
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-servlet-initializer</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
In this example, we are exposing two representations. Let’s create java classes for them.
import java.util.List;
import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement(name = "configurations")
@XmlAccessorType(XmlAccessType.FIELD)
public class Configurations
{
@XmlAttribute
private Integer size;
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
@XmlElement
private Link link;
@XmlElement
private List<Configuration> configurations;
import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import net.restfulapi.app.rest.domain.common.Status;
@XmlRootElement(name="configuration")
@XmlAccessorType(XmlAccessType.FIELD)
public class Configuration
{
@XmlAttribute
private Integer id;
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
@XmlElement
private Link link;
@XmlElement
private String content;
@XmlElement
private Status status;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "message")
public class Message {
public Message() {
super();
}
Additionally, we have simulated the DB functionality using ConfigurationDB class. It exposes static utility methods
for CRUD operations in configuration resource collection and individual configuration resources.
package net.restfulapi.app.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.restfulapi.app.rest.domain.Configuration;
import net.restfulapi.app.rest.domain.common.Status;
I have added self-explanatory code comments above each method to explain it.
package net.restfulapi.app.rest.service;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import net.restfulapi.app.dao.ConfigurationDB;
import net.restfulapi.app.rest.domain.Configuration;
import net.restfulapi.app.rest.domain.Configurations;
import net.restfulapi.app.rest.domain.common.Message;
import net.restfulapi.app.rest.domain.common.Status;
/**
* This REST resource has common path "/configurations" and
* represents configurations collection resources
* as well as individual collection resources.
*
* Default MIME type for this resource is "application/XML"
* */
@Path("/configurations")
@Produces("application/xml")
public class ConfigurationResource
{
/**
* Initialize the application with these two default configurations
* */
static {
ConfigurationDB.createConfiguration("Some Content", Status.ACTIVE);
ConfigurationDB.createConfiguration("Some More Content", Status.INACTIVE);
}
/**
* Use uriInfo to get current context path and to build HATEOAS links
* */
@Context
UriInfo uriInfo;
/**
* Get configurations collection resource mapped at path "HTTP GET /configurations"
* */
@GET
public Configurations getConfigurations() {
/**
* Get individual configuration resource mapped at path "HTTP GET /configurations/{id}"
* */
@GET
@Path("/{id}")
public Response getConfigurationById(@PathParam("id") Integer id){
Configuration config = ConfigurationDB.getConfiguration(id);
if(config == null) {
return Response.status(javax.ws.rs.core.Response.Status.NOT_FOUND)
.build();
}
if(config != null){
UriBuilder builder = UriBuilder.fromResource(ConfigurationResource.class)
.path(ConfigurationResource.class, "getConfigurationById");
Link link = Link.fromUri(builder.build(id))
.rel("self")
.build();
config.setLink(link);
}
return Response.status(javax.ws.rs.core.Response.Status.OK)
.entity(config)
.build();
}
/**
* Create NEW configuration resource in configurations collection resource
* */
@POST
@Consumes("application/xml")
public Response createConfiguration(Configuration config){
if(config.getContent() == null) {
return Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST)
.entity(new Message("Config content not found"))
.build();
}
/**
* Modify EXISTING configuration resource by it’s "id" at path "/configurations/{id}"
* */
@PUT
@Path("/{id}")
@Consumes("application/xml")
public Response updateConfiguration(@PathParam("id") Integer id, Configuration config){
if(config.getContent() == null) {
return Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST)
.entity(new Message("Config content not found"))
.build();
}
ConfigurationDB.updateConfiguration(id, config);
return Response.status(javax.ws.rs.core.Response.Status.OK)
.entity(new Message("Config Updated Successfully"))
.build();
}
/**
* Delete configuration resource by it’s "id" at path "/configurations/{id}"
* */
@DELETE
@Path("/{id}")
public Response deleteConfiguration(@PathParam("id") Integer id){
ConfigurationDB.removeConfiguration(id);
return Response.status(javax.ws.rs.core.Response.Status.OK).build();
}
}
package net.restfulapi.app.rest;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import net.restfulapi.app.rest.service.ConfigurationResource;
@ApplicationPath("/network-management")
public class NetworkApplication extends Application {
public NetworkApplication() {
singletons.add(new ConfigurationResource());
}
@Override
public Set<Class<?>> getClasses() {
return empty;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
Here @ApplicationPath annotation identifies this class as REST application to automatic scanning process in
servlet 3.0 containers. It helps in making web.xml file almost empty – with no REST specific configuration at all.
8. Demo
Build and deploy this project to any web server and start the server. Now test the REST APIs by invoking the
above URIs on any browser client.
Click on the given download link to download the source code for this application.
Download Sourcecode
Yes
No
ADVER TISEMENTS