Apache CXF Restful Web Service Tutorial



This example explains how to implement a Rest Web Service using Apache CXF which can return response in both JSON and XML.

Following are the configuration and implementation details used in this example.

Step 1: Create Maven project

   Following pom.xml defines the dependencies for this example.
		
<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.lal.pro</groupId> <artifactId>apache-cxf-rest-ws</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>apache-cxf-rest-ws Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <cxf.version>2.5.0</cxf.version> <org.springframework.version>4.1.1.RELEASE</org.springframework.version> <com.fasterxml.jackson.version>2.3.3</com.fasterxml.jackson.version> <ch.qos.logback.version>1.1.3</ch.qos.logback.version> <org.sl4j.version>1.7.12</org.sl4j.version> <spring.ws.version>2.2.4.RELEASE</spring.ws.version> </properties> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxrs</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${com.fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${com.fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${com.fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> <version>${com.fasterxml.jackson.version}</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> </dependency> </dependencies> <build> <finalName>apache_cxf_rest_ws</finalName> </build> </project>


Step 2: Create web.xml

   Following web.xml must be placed at "/apache-cxf-rest-ws/src/main/webapp/WEB-INF/".
             	
<!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>CXF</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:ApplicationContext-cxf.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app>


Step 3: Create ApplicationContext

In the following xml we can define the beans, also group the web services and specify In and out interceptors. Place this xml file at /apache-cxf-rest-ws/src/main/resources/. Make sure it is added in your web.xml.

			
			
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:cxf="http://cxf.apache.org/core" xmlns:task="http://www.springframework.org/schema/task"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	 http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
	 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd">


	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

	<task:annotation-driven />

	<context:component-scan base-package="com.lal.pro">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Service" />
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Component" />
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Repository" />
	</context:component-scan>

	<context:annotation-config />
	<context:component-scan base-package="com.lal.pro" />

	<bean id="addressWebService" class="com.lal.pro.cxf.ws.impl.AddressWebServiceImpl" />

	<jaxrs:server id="systemService" address="/system">
		<jaxrs:serviceBeans>
			<ref bean="addressWebService" />
		</jaxrs:serviceBeans>
		<jaxrs:providers>
			<ref bean="jsonProvider" />
		</jaxrs:providers>
	</jaxrs:server>
	<bean
		class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
	<bean id="jacksonMapper"
		class="com.lal.pro.cxf.ws.impl.mapper.CustomJacksonObjectMapper" />

	<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider">
		<property name="mapper" ref="jacksonMapper" />
	</bean>
</beans>   


Step 4: Create a data transfer object

Following DTO can be used in request and response.
			

package com.lal.pro.dto;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Address {
	
	private String streetAddress;
	private String addressOptional;
	private String city;
	private String state;
	private String country;
	private String zip;
	
	public String getStreetAddress() {
		return streetAddress;
	}
	public void setStreetAddress(String streetAddress) {
		this.streetAddress = streetAddress;
	}
	public String getAddressOptional() {
		return addressOptional;
	}
	public void setAddressOptional(String addressOptional) {
		this.addressOptional = addressOptional;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public String getZip() {
		return zip;
	}
	public void setZip(String zip) {
		this.zip = zip;
	}

}



Step 5: Create Web Service

Following code snippet shows the implementaion of Web Service class. It just creates a java object and returns it. When a client consumes this REST webservice it can decide whether it wants the data in xml or json format by setting the header Accept = "application/xml" or Accept = "application/json"

			
			

package com.lal.pro.cxf.ws.impl;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import com.lal.pro.dto.Address;

public class AddressWebServiceImpl {

	@GET
	@Path("/v1/address")
	@Produces({ "application/xml", "application/json" })
	public Response getAddress() {

		Address addressResponse = createAddress();

		return Response.ok(addressResponse).build();
	}

	private Address createAddress() {
		Address address = new Address();
		address.setStreetAddress("4800 abc Rd");
		address.setCity("abcd");
		address.setState("NJ");
		address.setCountry("US");
		address.setZip("10001");
		address.setAddressOptional("addressOptional");

		return address;
	}

}




Step 6: Customize ObjectMapper

Configure the ObjectMapper not to FAIL_ON_UNKNOWN_PROPERTIES


			

	package com.lal.pro.cxf.ws.impl.mapper;

	import java.text.SimpleDateFormat;

	import com.fasterxml.jackson.annotation.JsonInclude.Include;
	import com.fasterxml.jackson.databind.DeserializationFeature;
	import com.fasterxml.jackson.databind.ObjectMapper;

	public class CustomJacksonObjectMapper extends ObjectMapper {
		private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
		
		public CustomJacksonObjectMapper() {
			 this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			 this.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
			 this.setSerializationInclusion(Include.NON_EMPTY);
			 this.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
		}
	}





JSON Response

   When client Header, Accept="application/json"


			
{
  "streetAddress": "4800 abc Rd",
  "addressOptional": "addressOptional",
  "city": "abcd",
  "state": "NJ",
  "country": "US",
  "zip": "10001"
}


XML Response

When client Header, Accept="application/xml"


			
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<address>
    <addressOptional>addressOptional</addressOptional>
    <city>abcd</city>
    <country>US</country>
    <state>NJ</state>
    <streetAddress>4800 abc Rd</streetAddress>
    <zip>10001</zip>
</address>