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

 

Back to Top