JAXB XML Binding With CXF (Without Spring) in JAX-RS

Let’s revisit the quick overview post on JAX-RS and instead of working with RESTEasy and raw XML, we will utilize the power of XML binding with JAXB and CXF APIs to complete the same set of operations. We will send XML request from client and it will be unmarshalled to java object when it reaches web service operation method. Similarly, we will return java object from web service method and it will be marshalled to  XML before reaching client.

Example Scenario:

Let’s say that we have to build a shopping cart in the form of REST webservice. Just like in any usual shopping operation, in this case as well, we can add an item to the cart or update it or delete it or just retrieve  it to see its price. We would be doing these operations by sending request to 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. CXF 3.0.0
e. Java 1.8

2. Eclipse project folder structure:

JAXRS-CXF-JAXBNoSpring

3. Jars Used

In case you are not using maven, here is the list of jars needed to deploy in Tomcat.

cxf-core-3.0.0.jar
cxf-rt-frontend-jaxrs-3.0.0.jar
cxf-rt-transports-http-3.0.0.jar
javax.annotation-api-1.2.jar
javax.ws.rs-api-2.0.jar
stax2-api-3.1.4.jar
woodstox-core-asl-4.3.0.jar
xmlschema-core-2.1.0.jar

4.Web Project

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

Create a Maven Web Project

5. Maven Dependencies

Paste the below content to pom.xml in the newly created project. It has dependencies for JAX-RS and CXF.

<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>JAX-RSJAXBCXFNoSpring</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>Restful Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxrs</artifactId>
			<version>3.0.0</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>JAX-RSJAXBCXFNoSpring</finalName>
	</build>
</project>

6. POJO

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

package com.javanbeyond.data;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "item")
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;
  }

}

7. Application class

file: MyApplication.java. The Class that registers the resource class InputReceiver. 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;

public class MyApplication extends Application {
   private Set<Object> singletons = new HashSet<Object>();
   private Set<Class<?>> empty = new HashSet<Class<?>>();

   public MyApplication() {
      singletons.add(new OrderReceiver());
   }

   @Override
   public Set<Class<?>> getClasses() {
      return empty;
   }

   @Override
   public Set<Object> getSingletons() {
      return singletons;
   }
}

8. Resource class

file: OrderReceiver.java . Resource class that contains the actual business logic and Http method related JAX-RS annotations. Although we are going to send and receive  XML request and response, we are receiving object of ShoppingItem in the method updateItem and addItem as a request and sending object of  same type in the method getItem and deleteItem as a response. This conversion is done automagically by JAXB based on the the @XmlRootElement annotation in the POJO class. The @Consumes and @Produces annotations on the methods indicates JAX-RS runtime the content type of the request and response respectively.

package com.javanbeyond.service;

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 com.javanbeyond.data.ShoppingItem;

@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
  // The incoming request is in XML but it is converted to Shopping cart java
  // object by JAXB and this method gets the same object. The @Consumes
  // annotation
  // signifies that the incoming request type is in XML format.
  @POST
  @Consumes(MediaType.APPLICATION_XML)
  public Response addItem(ShoppingItem item) {
    item.setId(idCounter.incrementAndGet());
    shoppingCart.put(item.getId(), item);
    System.out.println("Created cart item " + item.getId());
    return Response.status(200)
        .entity("item with id " + item.getId() " created").build();
  }

  // This maps to http get request. Its used to get an item
  // The returning object is marshaled to XML by JAXB.
  @GET
  @Path("{id}")
  @Produces(MediaType.APPLICATION_XML)
  public ShoppingItem getItem(@PathParam("id"int id) {
    ShoppingItem item = shoppingCart.get(id);
    if (item == null) {
      throw new WebApplicationException(Response.Status.NOT_FOUND);
    }
    System.out.println("Getting cart item " + item.getId());
    return item;
  }

  // This maps to http put request. Its used to update an item
  // XML is deserialized to ShoppingItem object
  @PUT
  @Consumes(MediaType.APPLICATION_XML)
  public Response updateItem(ShoppingItem itemToUpdate) {
    ShoppingItem currentItem = shoppingCart.get(itemToUpdate.getId());
    if (currentItem == null)
      throw new WebApplicationException(Response.Status.NOT_FOUND);
    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
  // The returning object is serialized to XML
  @DELETE
  @Path("{id}")
  @Produces(MediaType.APPLICATION_XML)
  public ShoppingItem deleteItem(@PathParam("id"int id) {
    if (!shoppingCart.containsKey(id))
      throw new WebApplicationException(Response.Status.NOT_FOUND);
    ShoppingItem deletedItem = shoppingCart.remove(id);
    System.out.println("Removed cart item " + deletedItem.getId());
    return deletedItem;
  }
}

9. Deployment descriptor

file: web.xml . 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>
	<servlet>
		<servlet-name>rest</servlet-name>
		<servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
		<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>index.html</welcome-file>
	</welcome-file-list>
</web-app>

 


 

Example Execution:

  1. Start the application and type “http://localhost:8080/JAX-RSJAXBCXFNoSpring/”.
  2. You should get an index.html like below. This is the client to interact with our web service. Each button corresponds to an Http method.

JAXBCXFInitialClient

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

JAXRSJAXBCXFPostRequest

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

JAXRSJAXBCXFGetRequest

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

JAXRSJAXBCXFPutRequest

6. Now, delete the item from cart by clicking on DELETE 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.

JAXRSJAXBCXFDeleteRequest

 

Working with Matrix URIs (@MatrixParam,PathSegment) in JAX-RS

This tutorial shows how to extract Matrix Parameters from http request in JAX-RS.

Example Scenario :

Our rest service is like a car inventory  which has cars of different models and years. For the sake of simplicity, let’s say it has only Honda cars. We want to get one or more cars from the inventory based on different matrix parameter values.


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.9.Final
e. Java 1.8

2. Eclipse project folder structure:

JAXRSRESTMatrixParameters

3. Web Project

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

Create a Maven Web Project

4. Jars used

In case you are not using maven, here are external jars needed for deploying this application in Tomcat.

activation-1.1.jar
commons-codec-1.6.jar
commons-io-2.1.jar
commons-logging-1.1.1.jar
httpclient-4.2.6.jar
httpcore-4.2.5.jar
javax.ws.rs-api-2.0.jar
jaxrs-api-3.0.9.Final.jar
jboss-annotations-api_1.1_spec-1.0.1.Final.jar
jcip-annotations-1.0.jar
resteasy-jaxrs-3.0.9.Final.jar

5. Maven Dependencies

Paste the below content to pom.xml in the newly created project. It has dependencies for JAX-RS and RESTEasy jars.

<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>WorkingWithPathSegMatrixParam</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>Restful Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<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.9.Final</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>WorkingWithPathSegMatrixParam</finalName>
	</build>
</project>

6. POJO

file: Car.java. A POJO which the data is converted into and stored in inventory.

package com.javanbeyond.data;

public class Car {
  private int id;
  private String make;
  private String model;
  private String exteriorColor;
  private String interiorColor;
  private int year;

  public int getId() {
    return id;
  }

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

  public String getMake() {
    return make;
  }

  public void setMake(String make) {
    this.make = make;
  }

  public String getModel() {
    return model;
  }

  public void setModel(String model) {
    this.model = model;
  }

  public String getExteriorColor() {
    return exteriorColor;
  }

  public void setExteriorColor(String exteriorColor) {
    this.exteriorColor = exteriorColor;
  }

  public String getInteriorColor() {
    return interiorColor;
  }

  public void setInteriorColor(String interiorColor) {
    this.interiorColor = interiorColor;
  }

  public int getYear() {
    return year;
  }

  public void setYear(int year) {
    this.year = year;
  }

}

7. Application class

The class that registers the resource class for JAX-RS runtime to forward requests to methods of this class.

file: MyApplication.java.

package com.javanbeyond.service;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApplication extends Application {
  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> empty = new HashSet<Class<?>>();

  // Registering the resource class object
  public MyApplication() {
    singletons.add(new OrderReceiver());
  }

  @Override
  public Set<Class<?>> getClasses() {
    return empty;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

8. Resource class

file: OrderReceiver.java . In JAX-RS, we use @MatrixParam and PathSegment to capture information provided in matrix parameters of a URI and pass it to methods as parameters. The “/cars/{make}” @Path value  before the class brings the URI “http://localhost:8080/rest/cars/honda” to this class. The part after this URI decides which method will be called at runtime.

Case 1: Getting Matrix Parameters with PathSegment

The method getCarByPathSeg receives the URI  “/cars/honda/civic;color=gray/1991″ and “honda” is mapped to {make} from class level @Path annotation .{year} gets the value “1991” and “civic;color=gray” is stored in {model}. {model} is passed as PathSegment  to method parameter which encapsulates matrix parameter as well. We want to get a car with interior color as gray.

// Request match : /cars/honda/civic;color=gray/1991
  // To test it, please comment getCarByMatrix method
  // This method can be used as an alternative to getCarByMatrix
  @GET
  @Path("{model}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCarByPathSeg(@PathParam("make"String make,
      @PathParam("model"PathSegment carSeg,
      @PathParam("year"int year) {
    System.out.println("in getCarByPathSeg");
    String color = carSeg.getMatrixParameters().getFirst("color");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(color)) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 2: Getting Matrix Parameters with @MatrixParam

The method getCarByMatrix receives the URI  “/cars/honda/civic;color=gray/1991″. The matrix parameter “color” is captured by @MatrixParam annotation and the same is injected to the color parameter of the method. This case is similar to the case above. Only this time we are capturing the value of color with MatrixParam. We want to get a car with interior color as gray.

// Request match : /cars/honda/civic;color=gray/1991
  // This is same as above. But we don't have to extract the color ourselves.
  // MatrixParam does it for us
  // This method can be used as an alternative to getCarByMatrix
  @GET
  @Path("{model}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCarByMatrix(@PathParam("make"String make,
      @PathParam("model"String model,
      @MatrixParam("color"String color, @PathParam("year"int year) {
    System.out.println("in getCarByMatrix");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(color)) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 3: Capturing multiple Matrix Parameters

The method getCar receives the URI  “/cars/honda/crv;color=white/accord;color=black/1991″. This is the case of multiple matrix parameters. The parameter color is repeated two times and thus needs to be captured by a list of PathSegment. The getCar “carSegs” parameter does it beautifully. Here we are looking for cars with colors white or black.

// Request match : /cars/honda/crv;color=white/accord;color=black/1991
  // This method uses regex to match multiple matrix parameters
  @GET
  @Path("{model:.+/.+}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCar(@PathParam("make"String make,
      @PathParam("model"List<PathSegment> carSegs,
      @PathParam("year"int year) {
    System.out.println("in getCar");
    String interiorColor1 = carSegs.get(0).getMatrixParameters()
        .getFirst("color");
    String interiorColor2 = carSegs.get(1).getMatrixParameters()
        .getFirst("color");
    final Map<Integer, Car> subInventory = new HashMap<Integer, Car>();
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(interiorColor1|| car
          .getInteriorColor().equalsIgnoreCase(interiorColor2)) {
        subInventory.put(carEntry.getKey(), car);

      }
    }
    if (subInventory.isEmpty()) {
      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
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          outputRequestedItem(outputStream, subInventory);
        }
      };

  }

9. Deployment descriptor

file: web.xml . Application class is registered by setting as init param to the servlet. The URI must contain “/rest” for this REST servlet to be invoked.

<web-app>
	<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>
		<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>index.html</welcome-file>
	</welcome-file-list>
</web-app>

 

Example Execution:

1. Start the application.

2. Open your favorite browser and type the url “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/civic;color=gray/1991” and press Enter. You will get an XML response of a car of Civic model and interior color as gray.

JAXRSPathSegmentExample

3. Comment the method getCarByPathSeg and redeploy the application. Type the previous URI “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/civic;color=gray/1991” in your browser address bar and press Enter. The response should be similar to the one before. But this time we used MatrixParam annotation to extract color parameter.

MatrixParamExample

4. Now type “http://localhost:8080/WorkingWithPathSegMatrixParam/rest/cars/honda/crv;color=white/accord;color=black/1991” and press Enter. You get the cars with interior color as white or black.

JAXRSMultipleMatrixParameters

 

JAX-RS Handling QueryString (@QueryParam,@DefaultValue,UriInfo)

This tutorial shows us how to capture Query parameters using @QueryParam and UriInfo and the role of @DefaultValue annotation.

Example Scenario :

We have a rest service which is like a car inventory and has cars of different models and years. For the sake of simplicity, let’s say it has only Honda cars. We want to retrieve one or more cars from the inventory based on different query parameters passed in the request URI.


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 –

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:

JAX-RS QueryParamDirectory Struc

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>WorkingWithQueryParam</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>Restful Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <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>WorkingWithQueryParam</finalName>
    </build>
</project>

 5. POJO

file: Car.java. A POJO which is stored as car in Car Inventory web service and data of which is sent to client based on request URI.

package com.javanbeyond.data;

// A simple POJO
public class Car {
  private int id;
  private String make;
  private String model;
  private String exteriorColor;
  private String interiorColor;
  private int year;

  public int getId() {
    return id;
  }

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

  public String getMake() {
    return make;
  }

  public void setMake(String make) {
    this.make = make;
  }

  public String getModel() {
    return model;
  }

  public void setModel(String model) {
    this.model = model;
  }

  public String getExteriorColor() {
    return exteriorColor;
  }

  public void setExteriorColor(String exteriorColor) {
    this.exteriorColor = exteriorColor;
  }

  public String getInteriorColor() {
    return interiorColor;
  }

  public void setInteriorColor(String interiorColor) {
    this.interiorColor = interiorColor;
  }

  public int getYear() {
    return year;
  }

  public void setYear(int year) {
    this.year = year;
  }

}

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;

// regular application class
public class MyApplication extends Application {
  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> empty = new HashSet<Class<?>>();

  public MyApplication() {
    singletons.add(new OrderReceiver());
  }

  @Override
  public Set<Class<?>> getClasses() {
    return empty;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

7. Resource class

file: OrderReceiver.java . In JAX-RS, we use @QueryParam annotation to capture information provided in the query string of URI and pass it to methods as parameters. The “/cars/{make}” @Path value  before the class, brings the URI “http://localhost:8080/rest/cars/honda” to this class. {make} is mapped to “honda”. The part after this decides which method will be called at runtime.

If you need more information about query string, please refer Http Query String.

Case 1: Getting Query Strings with @QueryParam

The method getCarByQueryParam receives the URI  “/cars/honda/getCarByQueryParam?model=civic&year=1991″ with the values of queries, model and year mapped  to the same name parameters of the method by @QueryParam. The method returns a car with model as Civic and year as 1991.

// Request match : /cars/honda/getCarByQueryParam?model=civic&year=1991
  // Needed a car with year as 1991 and model as civic. Simple query parameter
  // matching
  @GET
  @Path("getCarByQueryParam")
  @Produces(MediaType.APPLICATION_XML)
  public StreamingOutput getCarByQueryParam(@QueryParam("model"String model,
      @QueryParam("year"int year) {
    System.out.println("in getCarByQueryParam");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getModel().equalsIgnoreCase(model)
          && car.getYear() == year) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 2 : Query String from UriInfo

In the method getCarByQueryInfo , query strings are extracted using UriInfo class which is injected by @Context annotation. This method tackles URI “/cars/honda/getCarByQueryInfo?model=civic&year=1991″  and returns a car with model as Civic and year as 1991.

// Request match : /cars/honda/getCarByQueryInfo?model=civic&year=1991
  // Using UriInfo

  @GET
  @Path("getCarByQueryInfo")
  @Produces(MediaType.APPLICATION_XML)
  public StreamingOutput getCarByQueryInfo(@Context UriInfo uriInfo) {
    System.out.println("in getCarByQueryInfo");
    int year = Integer
        .valueOf(uriInfo.getQueryParameters().getFirst("year"));
    String model = uriInfo.getQueryParameters().getFirst("model");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getModel().equalsIgnoreCase(model)
          && car.getYear() == year) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 3: Missing query string and @DefaultValue annotation

When the URI “/cars/honda/getCarByDefaultValue?color=white” hits the service, the missing query string ‘year’ is given a default value 2000 provided by @DefaultValue annotation. This annotation helps resolving optional query parameters. Please note that default value provided will be used only if the value is missing.In the below case, though default value is provided for both the parameters, only missing ‘year’ query parameter will be given a default value.

// Request match : /cars/honda/getCarByDefaultValue?color=white
  // Using @Default annotation

  @GET
  @Path("getCarByDefaultValue")
  @Produces(MediaType.APPLICATION_XML)
  public StreamingOutput getCarByDefaultValue(
      @DefaultValue("2000"@QueryParam("year"int year,
      @DefaultValue("white"@QueryParam("color"String color) {
    System.out.println("in getCarByDefaultValue");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(color)
          && car.getYear() == year) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

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>
        <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.

2. Open your favorite browser and type the url – “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/getCarByQueryParam?model=civic&year=1991” and press Enter. You will get an XML response of a car of Civic model and year 1991.

JAX-RS Query Parameter Response

3. Type the url  “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/getCarByQueryInfo?model=civic&year=1991” and press enter. You will get the below response:

JAX-RS UriInfo REST

4. Type the url “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/getCarByDefaultValue?color=white” and press Enter.

JAX-RS DefaultValue Example

 

Working with JAX-RS Path Expressions(@Path,@PathParam)

This tutorial shows various forms of path expressions (@Path and @PathParam) supported by JAX-RS and how to map them to REST methods and method parameters in Resource class.

Example Scenario :

We have a rest service which is like a shopping cart and holds some items, say 10 in number.
We want to retrieve one or more items from the cart based on different path expressions in our URI.


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 –

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:

REST Web Project - Maven 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>WorkingWithATPath</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>Restful Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <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>WorkingWithATPath</finalName>
    </build>
</project>

 

5. POJO

file: ShoppingItem.java.A POJO which is stored as cart item in our shopping cart web service and data of which is sent to client based on request URI.

 

package com.javanbeyond.data;
//  Simple 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;

// Regular Application class
public class MyApplication extends Application {
  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> empty = new HashSet<Class<?>>();

  public MyApplication() {
    singletons.add(new OrderReceiver());
  }

  @Override
  public Set<Class<?>> getClasses() {
    return empty;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

7. Resource class

file: OrderReceiver.java . In JAX-RS we use @Path and @PathParam annotations to capture information provided in the URI and pass it to annotated methods as parameters.

The “/orders” @Path value  before the class brings the URI “http://localhost:8080/rest/orders” to this class. The part after it decides which method will be called at runtime. This class has five methods which handle different variations of path elements in the URI.

Case 1: No Path annotation

The getAllItems method handles all URIs which don’t have a path after “/orders” as this is only method which would match this kind of pattern. It returns all the items from the cart.

// No Path Element. This is default when no id is provided
  // We will assume he wants all the created items from cart
  @GET
  // Shows the response will be XML
  @Produces("application/xml")
  public StreamingOutput getAllItems() {
    System.out.println("in getAllItems");
    return new StreamingOutput() {
      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, shoppingCart);
      }
    };
  }

Case 2 : Static Path values

getFirstItem method is called when the URI pattern is like “http://localhost:8080/rest/orders/1”. After orders, “/1” in the URI matches the method’s pattern in Path annotation. It returns only the first item from the shopping cart with id value 1.

// This is called only when 1 is mentioned at the end of the path. The below
  // method getItemById
  // also matches this pattern, but this method has more precedence.
  @GET
  @Path("/1")
  // Shows the response will be XML
  @Produces("application/xml")
  public StreamingOutput getFirstItem() {
    System.out.println("in getFirstItem");
    final ShoppingItem item = shoppingCart.get(1);
    System.out.println("Getting cart item " + item.getId());
    return new StreamingOutput() {
      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, item);
      }
    };
  }

Case 3: Simple URI with parameters

getItemById method is called when the URI is like “http://localhost:8080/rest/orders/{id}” where id ranges from 1 to 10. When the REST runtime sees this pattern, it gives the variable ‘id’ the value passed after “orders/” and passes it as method parameter which is annotated with  @PathParam. The placeholder value of @Path and @PathParam must match. e.g. “http://localhost:8080/rest/orders/2” URI gives id a value of 2 and the same value is given to the parameter “id”. This method returns the item that corresponds to the id passed in the URI.

// This is called when any particular Item is requested. id can be any value
  // from 1 to 10
  @GET
  @Path("{id}")
  // Shows the response will be XML
  @Produces("application/xml")
  public StreamingOutput getItemById(@PathParam("id"int id) {
    System.out.println("in getItemById");
    final ShoppingItem item = shoppingCart.get(id);
    if (item == 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>");
        }
      };
    }
    System.out.println("Getting cart item " + item.getId());
    return new StreamingOutput() {
      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, item);
      }

    };
  }

Case 4: Path values with regular expressions

@Path can also take regular expressions as values and the method getItemByBetweenIds is called  if the regex matches the URI pattern. e.g. in the pattern “{ids : \\d-\\d}” , ids is the placeholder which would be mapped to the incoming URI value if it matches with the regex after colon and the same is passed to the PathParam annotated parameters of the method. In regex, \d stands for digits. So values like 3-4, 6-9 etc are valid values. It gets all the items with ids between left of hyphen to right of hyphen.

Sample URIs hitting this method can be : “http://localhost:8080/rest/orders/4-5”, “http://localhost:8080/rest/orders/7-9” etc.

// Regular expression id between min - max. Shows how regular expression can
  // be used with Path expression for easy retrieval
  @GET
  @Path("{ids : \\d-\\d}")
  // Shows the response will be XML
  @Produces("application/xml")
  public StreamingOutput getItemByBetweenIds(@PathParam("ids"String ids) {
    final Map<Integer, ShoppingItem> subCart = new HashMap<Integer, ShoppingItem>();
    String[] idParts = ids.split("-");
    int max = Integer.valueOf(idParts[1]);
    int min = Integer.valueOf(idParts[0]);
    for (int i = min; i <= max; i++) {
      subCart.put(i, shoppingCart.get(i));
    }
    if (subCart.isEmpty()) {
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          PrintStream writer = new PrintStream(outputStream);
          writer.println(
              "<item>" + Response.Status.NOT_FOUND + "</item>");
        }
      };
    }
    return new StreamingOutput() {

      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, subCart);
      }

    };
  }

Case 5: Multiple path parameters.

Method parameters can be mapped to two or more different values of path in the URI. In the below case, we have two different values id1 and id2. {id1} and {id2} are mapped to the respective same name parameters of the methods. Valid values can be:

“http://localhost:8080/rest/orders/4and5”, “http://localhost:8080/rest/orders/7and9” etc. It fetches items which lies between left and right side of ‘and’ in the path URI.

// More than one path parameter. Show multiple placeholders can be mapped to
  // multiple method arguments
  @GET
  @Path("{id1}and{id2}")
  // Shows the response will be XML
  @Produces("application/xml")
  public StreamingOutput getItemByBetweenIds(@PathParam("id1"int id1,
      @PathParam("id2"int id2) {
    final Map<Integer, ShoppingItem> subCart = new HashMap<Integer, ShoppingItem>();
    subCart.put(1, shoppingCart.get(id1));
    subCart.put(2, shoppingCart.get(id2));
    if (subCart.isEmpty()) {
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          PrintStream writer = new PrintStream(outputStream);
          writer.println(
              "<item>" + Response.Status.NOT_FOUND + "</item>");
        }
      };
    }
    return new StreamingOutput() {

      public void write(OutputStream outputStream)
          throws IOException, WebApplicationException {
        outputRequestedItem(outputStream, subCart);
      }

    };
  }

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>
        <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/WorkingWithATPath/ in browser.
  2. You should get a client.html like below. This is the client to interact with our web service. Pressing “GET” button with different URI patterns fetches different types of data from different methods.

RESTPath-Client

3. Get all the items by not giving any values after /orders.

REST-NoPathScenario

4. Get only the first item by providing “/1” at the end of URI.

RESTPath-Static Path values

5. Get a specific item by providing the id in the URI.

Path based on parameters

6. Get items based on regular expressions in the URI.

Path with regex expression

7. More than one path parameters mapping.

Multiple Path placeholders

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