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

 

Quick Overview of SOAP (JAX-WS) webservice with CXF

In this tutorial, we will learn how to build a simple SOAP web service using CXF, a popular implementation of JAX-WS.

Example Scenario:

We need to build a web service based on SOAP, which would take two binary numbers in request and provide their added result in response.


Project artifacts:

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

Jars used –

cxf-rt-frontend-jaxws-3.0.3.jar
xml-resolver-1.2.jar
asm-3.3.1.jar
cxf-core-3.0.3.jar
woodstox-core-asl-4.4.1.jar
stax2-api-3.1.4.jar
xmlschema-core-2.1.0.jar
cxf-rt-bindings-soap-3.0.3.jar
cxf-rt-wsdl-3.0.3.jar
wsdl4j-1.6.3.jar
cxf-rt-databinding-jaxb-3.0.3.jar
jaxb-impl-2.2.10-b140310.1920.jar
jaxb-core-2.2.10-b140310.1920.jar
cxf-rt-bindings-xml-3.0.3.jar
cxf-rt-frontend-simple-3.0.3.jar
cxf-rt-ws-addr-3.0.3.jar
cxf-rt-ws-policy-3.0.3.jar
neethi-3.0.3.jar
slf4j-log4j12-1.5.6.jar
slf4j-api-1.5.6.jar
log4j-1.2.14.jar
cxf-rt-transports-http-3.0.3.jar
commons-logging-1.1.1.jar
spring-web-4.1.7.RELEASE.jar
spring-aop-4.1.7.RELEASE.jar
aopalliance-1.0.jar
spring-beans-4.1.7.RELEASE.jar
spring-context-4.1.7.RELEASE.jar
spring-expression-4.1.7.RELEASE.jar
spring-core-4.1.7.RELEASE.jar

Before we begin:

Following is the folder structure of maven web project with WSDL in the resource folder.

To learn how to create maven web project, please read this tutorial.

To learn how to create a WSDL, please read this tutorial. We are going to use the same WSDL here.

–    Eclipse initial project folder structure:

CXF Web Project

–     pom.xml ( at the beginning )

<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>QuickSOAPOverviewCXF</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>QuickSOAPOverviewCXF Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>apache-snapshots</id>
            <name>Apache SNAPSHOT Repository</name>
            <url>http://repository.apache.org/snapshots/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.7.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>QuickSOAPOverviewCXF</finalName>
        <plugins>
            <!--             Plugin to create equivalent java classes for the wsdl schema -->
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.0.3</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <!-- where to create the java classes -->
                            <sourceRoot>${basedir}/src/main/java</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/resources/BinaryAdditionService.wsdl</wsdl>
                                    <wsdlLocation>classpath:BinaryAdditionService.wsdl</wsdlLocation>
                                    <extraargs>
                                        <!-- We want server side implementation classes -->
                                        <extraarg>-impl</extraarg>
                                        <extraarg>-verbose</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

Please run maven clean and install and you should see the following java classes created from WSDL.

 

CXFGeneratedClasses

– WSDL and class mapping

The following image depicts the mapping based on which CXF tool wsdl2java and JAXB created the classes from WSDL and schema.

CXFWSDLClassMappingBinding

package-info.java : It contains the mapping between target namespace of the WSDL (http://www.javanbeyond.com/BinaryAdditionService/) and the created package name (com.javanbeyond.binaryadditionservice).It can be modified by passing -p argument to wsdl2java tool of CXF. Each package has one package-info.java.

ObjectFactory.java : It acts as a factory class to create objects for the classes in its package. If there are multiple namespaces in schema, so will be multiple packages and each package has its own ObjectFactory class.

BinaryAddResponse.java : The Response class whose object will be marshalled to response XML.

BinaryAddRequest.java : The Request class which the incoming payload will be unmarshalled to and passed to implementation operation method.

BinaryAdditionService.java : The web service interface created based on port type in WSDL.

BinaryAdditionServiceSOAPImpl.java : The implementation class of BinaryAdditionService interface. All our business logic will be written here or will start here.

BinaryAdditionService_Service.java : The client representation of the Service.

Let’s Proceed…

  1. Final project directory structure:

SOAP CXF Project Structure

2. Implementation class

file : BinaryAdditionServiceSOAPImpl. This is the implementation class of our web service interface. It has a method created from WSDL operation and all our processing logic will be in this method or will start from this method. The XML request payload is unmarshalled to BinaryAdditionRequest object and passed to the operation name method as an argument.

 


/**
 * Please modify this class to meet your needs
 * This class is not complete
 */

package com.javanbeyond.binaryadditionservice;

import java.util.logging.Logger;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;

/**
 * This class was generated by Apache CXF 3.0.3 2016-06-09T21:50:03.647-05:00
 * Generated source version: 3.0.3
 
 */

@javax.jws.WebService(serviceName = "BinaryAdditionService", portName = "BinaryAdditionServiceSOAP", targetNamespace = "http://www.javanbeyond.com/BinaryAdditionService/", wsdlLocation = "classpath:BinaryAdditionService.wsdl", endpointInterface = "com.javanbeyond.binaryadditionservice.BinaryAdditionService")

public class BinaryAdditionServiceSOAPImpl implements BinaryAdditionService {

    private static final Logger LOG = Logger
            .getLogger(BinaryAdditionServiceSOAPImpl.class.getName());

    /*
     * (non-Javadoc)
     
     * @see
     * com.javanbeyond.binaryadditionservice.BinaryAdditionService#binaryAdd(com
     * .javanbeyond.binaryadditionservice.BinaryAddRequest parameters )*
     */
    public com.javanbeyond.binaryadditionservice.BinaryAddResponse binaryAdd(
            BinaryAddRequest parameters) {
        LOG.info("Executing operation binaryAdd");
        System.out.println(parameters);
        try {
            // Create a Object Factory method for this package to create
            // equivalent java objects for this
            // namespace/package
            ObjectFactory objFactory = new ObjectFactory();
            BinaryAddResponse _return = objFactory.createBinaryAddResponse();
            _return.setOut(Integer
                    .toBinaryString(Integer.parseInt(parameters.getA()2)
                            + Integer.parseInt(parameters.getB()2)));
            return _return;
        catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

}

3. Spring configuration.

file : BinaryAdditionConfig.xml. Spring application context file to bind different instances of classes (beans) in spring container including our web service class within an application.

 

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd   http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <!-- The actual implementation class and the corresponding url to access         it -->
    <jaxws:endpoint id="calculateEndpoint" implementor="#binaryCalculate" address="/BinaryCalculatorService"></jaxws:endpoint>
    <bean id="binaryCalculate" class="com.javanbeyond.binaryadditionservice.BinaryAdditionServiceSOAPImpl"></bean>
</beans>

4. Deployment descriptor

file: web.xml. Deployment descriptor file for mapping URIs to CXF servlet. The spring configuration file location is added as a context parameter through web.xml.

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:BinaryAdditionConfig.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/soap/*</url-pattern>
    </servlet-mapping>
</web-app>

5.  Maven Dependencies

file : pom.xml. Maven unit of work to declare project dependencies, compile code and build war file for deployment in Tomcat. Comment the plugin part to prevent it from rebuilding and overriding our changes in Implementation class when we rebuild our code.

<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>QuickSOAPOverviewCXF</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>QuickSOAPOverviewCXF Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>apache-snapshots</id>
            <name>Apache SNAPSHOT Repository</name>
            <url>http://repository.apache.org/snapshots/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.7.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>QuickSOAPOverviewCXF</finalName>
        <plugins>
            <!--             Plugin to create equivalent java classes for the wsdl schema -->
            <!-- <plugin>                 <groupId>org.apache.cxf</groupId>                 <artifactId>cxf-codegen-plugin</artifactId>                 <version>3.0.3</version>                 <executions>                     <execution>                         <id>generate-sources</id>                         <phase>generate-sources</phase>                         <configuration>                             where to create the java classes                             <sourceRoot>${basedir}/src/main/java</sourceRoot>                             <wsdlOptions>                                 <wsdlOption>                                     <wsdl>${basedir}/src/main/resources/BinaryAdditionService.wsdl</wsdl>                                     <wsdlLocation>classpath:BinaryAdditionService.wsdl</wsdlLocation>                                     <extraargs>                                         We want server side implementation classes                                         <extraarg>-impl</extraarg>                                         <extraarg>-verbose</extraarg>                                     </extraargs>                                 </wsdlOption>                             </wsdlOptions>                         </configuration>                         <goals>                             <goal>wsdl2java</goal>                         </goals>                     </execution>                 </executions>             </plugin> --></plugins>
    </build>
</project>

Example Execution:

  1. Please run maven clean and install and deploy the war file created in Tomcat . You can use your own choice of SOAP client. In this tutorial, we will use SOAPUI, a popular web service client.
  2. Open SOAPUI and create a SOAP project with the url “http://localhost:8080/QuickSOAPOverviewCXF/soap/BinaryCalculatorService?wsdl”

SOAPUI Create Project

3. Press ok. A SOAP project should get created.

SOAPUI Request Create

4. Fill any binary input and press the send button. You should get a response of added binary numbers.

 

SOAP UI response

 

Back to Top