Thursday, April 12, 2012

Part 1: RESTful WS with Apache CXF - XML

Overview:
This is Part 1 of the article on RESTful WS with Apache CXF. In this article we will look at how you can do XML/HTTP based RESTful WS with Apache CXF. We're using the CXF support on JAX-RS (JSR-311) for this. The sample code can be found here.

You can also follow the Part 2.
Prerequisite:JDK >= 1.5.x
Maven >= 2.2.x

Project Structure: Following is the project structure I have used (Maven WAR project).

Step 1: Maven Dependencies (pom.xml)
 <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>org.fazlan</groupId>
<artifactId>org.fazlan.employee.cxf.xmlrest.service</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>org.fazlan.employee.cxf.xmlrest.service</name>
<url>http://maven.apache.org</url>
<build>
<finalName>CXFEmployeeXmlRESTService</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Spring framework -->
<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>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-aegis</artifactId>
<!-- 2.4.4 or 2.5.0 -->
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
<properties>
<org.springframework.version>3.1.1.RELEASE</org.springframework.version>
<cxf.version>2.4.6</cxf.version>
</properties>
</project>

Step 2: Creating POJO Classes and Service Implementation

Employee.java
 package org.fazlan.employee.cxf.xmlrest.service.beans;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;
@XmlType(
name = "EmployeeType",
namespace = "org.fazlan.employee.cxf.xmlrest.service.beans",
propOrder = {"id", "firstName", "lastName"})
@XmlRootElement(
name = "Employee",
namespace = "org.fazlan.employee.cxf.xmlrest.service.beans")
public class Employee implements Serializable {
private Long id;
private String firstName;
private String lastName;
public Employee() {
}
public Employee(Long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
return !(firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) && !(id != null ? !id.equals(employee.id) : employee.id != null) && !(lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null);
}
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}

Employees.java - This is used to hold a collection object of Employee.java

 package org.fazlan.employee.cxf.xmlrest.service.beans;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.Collection;
@XmlType(
name = "EmployeesType",
namespace = "org.fazlan.employee.cxf.xmlrest.service.beans")
@XmlRootElement(
name = "Employees",
namespace = "org.fazlan.employee.cxf.xmlrest.service.beans")
public class Employees {
private Collection<Employee> employees;
public Employees() {
}
public Employees(Collection<Employee> employees) {
setEmployees(employees);
}
@XmlElement(name = "employee",required = true)
@XmlElementWrapper(name = "employees")
public Collection<Employee> getEmployees() {
return employees;
}
public void setEmployees(Collection<Employee> employees) {
this.employees = employees;
}
}
IEmployeeService.java - Service Interface

package org.fazlan.employee.cxf.xmlrest.service.services;
import org.fazlan.employee.cxf.xmlrest.service.beans.Employee;
import org.fazlan.employee.cxf.xmlrest.service.beans.Employees;
import javax.ws.rs.core.Response;
public interface IEmployeeService {
Employee get(Long id);
Employees getAll();
Response add(Employee employee);
Response update(Employee employee);
Response delete(Long id);
}
EmployeeService.java - Implementation of the service.

NOTE: This service class has been annotated to produce and consume certain methods in XML(application/xml) as the mime-type. Read more about Apache CXF - JAX-RS basics.

package org.fazlan.employee.cxf.xmlrest.service.services;
import org.fazlan.employee.cxf.xmlrest.service.beans.Employee;
import org.fazlan.employee.cxf.xmlrest.service.beans.Employees;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
@Path("/employeeservice/")
@Produces("application/xml")
public class EmployeeService implements IEmployeeService {
private static final Map<Long, Employee> EMPLOYEE_MAP = new HashMap<Long, Employee>();
private static long index = 3L;
static {
EMPLOYEE_MAP.put(1L, new Employee(1L, "First Name 1", "Last Name 1"));
EMPLOYEE_MAP.put(2L, new Employee(2L, "First Name 2", "Last Name 2"));
}
@GET
@Path("/get/{id}")
public Employee get(@PathParam("id") Long id) {
return EMPLOYEE_MAP.get(id);
}
@GET
@Path("/getall/")
public Employees getAll() {
return new Employees(EMPLOYEE_MAP.values());
}
@POST
@Path("/add/")
@Consumes("application/xml")
public Response add(Employee employee) {
System.out.println("Adding :" + employee);
employee.setId(index++);
update(employee);
return Response.status(Response.Status.OK).build();
}
@PUT
@Path("/update/")
@Consumes("application/xml")
public Response update(Employee employee) {
EMPLOYEE_MAP.put(employee.getId(), employee);
return Response.status(Response.Status.OK).build();
}
@DELETE
@Path("/delete/{id}/")
public Response delete(@PathParam("id") Long id) {
Employee e = EMPLOYEE_MAP.remove(id);
System.out.println("Deleted :" + e);
return Response.status(Response.Status.OK).build();
}
}

Read more about Apache CXF - JAX-RS basics.

Step 3: Exposing the Service Beans as RESTful Web Service
 <?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:cxf="http://cxf.apache.org/core"
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/core
     http://cxf.apache.org/schemas/core.xsd">
<!-- do not use import statements if CXFServlet init parameters link to this beans.xml -->
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<jaxrs:server id="employeeService" address="/">
<jaxrs:serviceBeans>
<ref bean="employeeServiceBean"/>
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
<jaxrs:features>
<cxf:logging/>
</jaxrs:features>
</jaxrs:server>
<bean id="employeeServiceBean" class="org.fazlan.employee.cxf.xmlrest.service.services.EmployeeService"/>
</beans>


Step 4: Updating the web.xml to Register the CXF Servlet.
 <!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 Web Service Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/app-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-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>/*</url-pattern>
</servlet-mapping>
</web-app>
Step 5: Building the Project
mvn clean install

Step 6: Deploying the Service
Now you can deploy this service on to any container. Here we'll deploy this on Tomcat server.

Step 7: Testing the RESTful Web Service using CURL

We're going to test our Web Service using CURL, which I found as a very easy way to send REST Calls. I found this very nice blog about RESTing with Curl.

Installing CURL

sudo apt-get install curl

-I am using Ubuntu, if you're using Win or Mac, please do the necessary to install curl.

Now, after successfully installing CURL, we are ready to test the services.

GET Resources:
- Retrieving all the employee resources.
 curl -v -H "Accept: application/xml" http://localhost:8080/CXFEmployeeXmlRESTService/employeeservice/getall

- Retrieving a employee resource for a given id(id = 1).
 curl -v -H "Accept: application/xml" http://localhost:8080/CXFEmployeeXmlRESTService/employeeservice/get/1

POST a Resource: Adding a new employee resource.
- Adding the new employee record in the add.xml file.
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Employee xmlns:ns2="org.fazlan.employee.cxf.xmlrest.service.beans">
<id>3</id>
<firstName>First Name1</firstName>
<lastName>Last Name1</lastName>
</ns2:Employee>

 curl -v -H "Accept: application/xml" -H "Content-type: application/xml" -X POST -d @add.xml http://127.0.0.1:8080/CXFEmployeeXmlRESTService/employeeservice/add


PUT a Resource: Updating an existing employee resource.
- Updating an existing employee record in the update.xml file.
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Employee xmlns:ns2="org.fazlan.employee.cxf.xmlrest.service.beans">
<id>1</id>
<firstName>Updated First Name1</firstName>
<lastName>Updated Last Name1</lastName>
</ns2:Employee>

 curl -v -H "Accept: application/xml" -H "Content-type: application/xml" -X PUT -d @update.xml http://127.0.0.1:8080/CXFEmployeeXmlRESTService/employeeservice/update/

DELETE a Resource: Deleting an employee for a given id (id=2)
 curl -i -H "Accept: application/xml" -X DELETE http://localhost:8080/CXFEmployeeXmlRESTService/employeeservice/delete/2/

Summary:
This tutorial looked at how we can create XML RESTful Web Service using Apache CXF and test the services using CURL. You can find the sample code here.

No comments:

Post a Comment