A Quick Overview on REST (JAX-RS) webservice

This tutorial uses RESTEasy, a popular JAX-RS implementation to give a brief introduction of a REST aware web service using Maven.

Example Scenario:

Let’s say we have to build a shopping cart as REST web service. In this shopping cart, we should be able to add an item, update an item, delete an item or just retrieve the item. We would be doing these operations by hitting the web service with a client on different http methods and accordingly, get a response.


Project artifacts:

1. Technologies used –

a. Eclipse IDE for Java EE developer 4.5.0.20150621-1200 (Mars)
b. Preinstalled Maven with Eclipse Mars
c. Tomcat 7.0.64
d. RESTEasy 3.0.4.Final
e. Java 1.8

Jars used –

junit-3.8.1.jar
javax.ws.rs-api-2.0.jar
resteasy-jaxrs-3.0.4.Final.jar
jaxrs-api-3.0.4.Final.jar
slf4j-simple-1.5.8.jar
slf4j-api-1.5.8.jar
scannotation-1.0.3.jar
javassist-3.12.1.GA.jar
jboss-annotations-api_1.1_spec-1.0.1.Final.jar
activation-1.1.jar
httpclient-4.2.1.jar
httpcore-4.2.1.jar
commons-logging-1.1.1.jar
commons-codec-1.6.jar
commons-io-2.1.jar
jcip-annotations-1.0.jar

2. Eclipse project folder structure:

Maven Web application directory structure

3. Web Project

Create a standard web project in Eclipse by following the below link.

Create a Maven Web Project

4. Maven Dependencies

file:pom.xml. Maven unit of work to declare project dependencies, compile code and build war file for deployment in Tomcat.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javanbeyond</groupId>
    <artifactId>QuickCrashCourseREST</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>Restful Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.0.4.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>QuickCrashCourseREST</finalName>
    </build>
</project>

5. POJO

file: ShoppingItem.java. A POJO which the incoming request from client to web service will be converted to and stored.

package com.javanbeyond.data;

// A normal POJO
public class ShoppingItem {
  private int id;
  private String name;
  private String price;
  private String quantity;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getPrice() {
    return price;
  }

  public void setPrice(String price) {
    this.price = price;
  }

  public String getQuantity() {
    return quantity;
  }

  public void setQuantity(String quantity) {
    this.quantity = quantity;
  }
}

6. Application class

file: MyApplication.java. The Class that registers the resource class OrderReceiver with JAX-RS runtime. It has two sets.
Our resource class should be added to the singleton set.

package com.javanbeyond.service;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
// Application class to register our resource class Order Receiver
public class MyApplication extends Application {
   private Set<Object> singletons = new HashSet<Object>();
   private Set<Class<?>> empty = new HashSet<Class<?>>();
   public MyApplication() {
     // Registers resource class as singletons. The 
     // annotations present in this class will be resolved at Runtime 
      singletons.add(new OrderReceiver());
   }
   @Override
   public Set<Class<?>> getClasses() {
      return empty;
   }
   @Override
   public Set<Object> getSingletons() {
      return singletons;
   }
}

7. Resource class

file: OrderReceiver.java . Resource class that contains the actual business logic and Http method related JAX-RS annotations. It is called by JAX-RS implementation at runtime based on request URI.

package com.javanbeyond.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

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.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.javanbeyond.data.ShoppingItem;

// Our actual resource class
// if url contains the below pattern, this class will be initialized and its
// methods are called.
@Path("/orders")
public class OrderReceiver {
  private Map<Integer, ShoppingItem> shoppingCart = new ConcurrentHashMap<Integer, ShoppingItem>();
  private AtomicInteger idCounter = new AtomicInteger();

  public OrderReceiver() {
  }

  // This maps to http post request. Its used to add a new item
  @POST
  // Defines the content type it can accept
  @Consumes(MediaType.APPLICATION_XML)
  public Response addItem(InputStream is) {
    final ShoppingItem item = readRequest(is);
    item.setId(idCounter.incrementAndGet());
    shoppingCart.put(item.getId(), item);
    System.out.println("Created cart item " + item.getId());
    // returns a status code of 200 with the below message
    return Response.status(200)
        .entity("item with id " + item.getId() " created").build();
  }

  // This maps to http get request. Its used to get an item
  @GET
  // it recognizes a pattern like orders/2 where the value 2 is given to
  // id and passed as an argument to the method
  @Path("{id}")
  // Defines the content type it returns
  @Produces(MediaType.APPLICATION_XML)
  public StreamingOutput getItem(@PathParam("id"int id) {
    final ShoppingItem item = shoppingCart.get(id);
    if (item == null) {
      // Writing response in case of incorrect input
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          PrintStream writer = new PrintStream(outputStream);
          writer.println(
              "<item>" + Response.Status.NOT_FOUND + "</item>");
        }
      };
    }
    System.out.println("Getting cart item " + item.getId());
    // Writing response
    return new StreamingOutput() {
      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, item);
      }
    };
  }

  // This maps to http put request. Its used to update an item
  @PUT
  @Path("{id}")
  @Consumes(MediaType.APPLICATION_XML)
  public Response updateItem(@PathParam("id"int id, InputStream is) {
    ShoppingItem itemToUpdate = readRequest(is);
    final ShoppingItem currentItem = shoppingCart.get(id);
    if (currentItem == null)
      return Response.status(200)
          .entity("<item>" + Response.Status.NOT_FOUND + "</item>")
          .build();
    System.out.println("Updating cart item " + currentItem.getId());
    currentItem.setName(itemToUpdate.getName());
    currentItem.setPrice(itemToUpdate.getPrice());
    currentItem.setQuantity(itemToUpdate.getQuantity());
    return Response.status(200)
        .entity("item with id " + currentItem.getId() " updated")
        .build();

  }

  // This maps to http delete request. It is used to delete an item
  @DELETE
  @Path("{id}")
  @Consumes(MediaType.APPLICATION_XML)
  public StreamingOutput deleteItem(@PathParam("id"int id, InputStream is) {
    final ShoppingItem currentItem = shoppingCart.get(id);
    if (currentItem == null)
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          PrintStream writer = new PrintStream(outputStream);
          writer.println(
              "<item>" + Response.Status.NOT_FOUND + "</item>");
        }
      };
    else {
      shoppingCart.remove(currentItem.getId());
      System.out.println("Removed cart item " + currentItem.getId());
    }
    return new StreamingOutput() {
      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, currentItem);
      }
    };
  }

  protected void outputRequestedItem(OutputStream os, ShoppingItem item)
      throws IOException {
    PrintStream writer = new PrintStream(os);
    writer.println("<item id=\"" + item.getId() "\">");
    writer.println("   <name>" + item.getName() "</name>");
    writer.println("   <price>" + item.getPrice() "</price>");
    writer.println("   <quantity>" + item.getQuantity() "</quantity>");
    writer.println("</item>");
  }

  // utility method to convert input stream to java object
  protected ShoppingItem readRequest(InputStream is) {
    try {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance()
          .newDocumentBuilder();
      Document doc = builder.parse(is);
      Element root = doc.getDocumentElement();
      ShoppingItem item = new ShoppingItem();
      if (root.getAttribute("id"!= null
          && !root.getAttribute("id").trim().equals(""))
        item.setId(Integer.valueOf(root.getAttribute("id")));
      NodeList nodes = root.getChildNodes();
      ;
      for (int i = 0; i < nodes.getLength(); i++) {
        Node element = (Nodenodes.item(i);
        if (element.getNodeName().equals("name")) {
          item.setName(element.getTextContent());
        else if (element.getNodeName().equals("price")) {
          item.setPrice(element.getTextContent());
        else if (element.getNodeName().equals("quantity")) {
          item.setQuantity(element.getTextContent());
        }
      }
      return item;
    catch (Exception e) {
      e.printStackTrace(System.out);
      throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
    }
  }
}

8. Deployment descriptor

file: web.xml . Deployment descriptor file for mapping URIs to servlet. Application class is registered by setting as init param to the servlet.

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
    <display-name>Archetype Created Web Application</display-name>
    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/rest</param-value>
    </context-param>
    <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
        <!-- Registers the Application class to JAX-RS runtime -->
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.javanbeyond.service.MyApplication</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>client.html</welcome-file>
    </welcome-file-list>
</web-app>

 


Example Execution:

  1. Deploy the application in Tomcat and type http://localhost:8080/QuickCrashCourseREST/ in browser.
  2. You should get a client.html like below. This is the client to interact with our web service. Each button corresponds to an Http method.

RESTful webservice client

3. Click on POST button to add an item to cart.

RESTful webservice client POST request

4. Get the created item by clicking GET and providing the id in the url.

RESTful webservice client GET request

5. Update the item by making quantity as 5 and clicking PUT button.

RESTful webservice client PUT request

6. Now, delete the item from cart by clicking on DELETE button and providing the id in the url. The deleted item will be the response. We can see the response as updated value from PUT request.

RESful webservice client DELETE request

Back to Top