Apache CXF Web Service Example
In this article, we will implement an Apache CXF Web service example. Webservices can be developed using SOAP and REST. SOAP refers to Simple Object Access Protocol. REST stands for Representational State Transfer.
1. Overview
Roy Fielding was the first person to come up with the REST architecture proposal. In REST, a Resource is a Uniform Resource Identifier or URI. The state of a resource at any given point of time is represented by a document and is called the Representation of resource.
Apache CXF framework originated from two projects named Celtix and XFire. CXF is coined from Celtix and XFire. Celtix was an open source Enterprise Service Bus. This opensource project was sponsored by IONA. XFire was an opensource SOAP Framework. Apache CXF has support for Plain Old Apache CXF Objects (POJO), JAX-WS,WSDL, JAX-RS, and JMS. In the next sections, we look at Apache CXF JAX-WS and JAX-RS web service examples.
2. Apache CXF Web Service Example
Apache CXF is based on JAX-WS and JAX-RS standards. The framework provides features for handling WSDL and java objects. It has APIs to modify XML messages, SOAP, JAX-RS web services, and spring framework integration.
2.1 Prerequisites
Java 8 is required on the Linux, windows or mac operating system. Maven 3.6.1 is required for building the apache cxf application.
2.2 Download
You can download Java 8 from the Oracle web site . Apache Maven 3.6.1 can be downloaded from the Apache site.
2.3 Setup
Below are the setup commands required for the Java Environment.
Java Setup
JAVA_HOME="/desktop/jdk1.8.0_73" export JAVA_HOME PATH=$JAVA_HOME/bin:$PATH export PATH
The environment variables for maven are set as below:
Maven Setup
JAVA_HOME=”/jboss/jdk1.8.0_73″ export M2_HOME=/users/bhagvan.kommadi/Desktop/apache-maven-3.6.1 export M2=$M2_HOME/bin export PATH=$M2:$PATH
2.4 SOAP Service
Let us look at Employee SOAP service. We start with Employee
Interface.Employee
Interface has getName
method
Employee
package org.javacodegeeks.cxf.soap; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlJavaTypeAdapter(EmployeeAdapter.class) public interface Employee { public String getName(); }
EmployeeImpl
Class implements the Employee
Interface. The code is shown below
EmployeeImpl
package org.javacodegeeks.cxf.soap; import javax.xml.bind.annotation.XmlType; @XmlType(name = "Employee") public class EmployeeImpl implements Employee { private String name; EmployeeImpl() { } public EmployeeImpl(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
EmployeeService
interface is shown below. The interface has message
, addEmployee
and getEmployees
methods.
EmployeeService
package org.javacodegeeks.cxf.soap; import java.util.Map; import javax.jws.WebService; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @WebService public interface EmployeeService { public String message(String message); public String addEmployee(Employee employee); @XmlJavaTypeAdapter(EmployeeMapAdapter.class) public Map getEmployees(); }
EmployeeServiceImpl
class which extends EmployeeService
Interface is shown below.
EmployeeServiceImpl
package org.javacodegeeks.cxf.soap; import java.util.LinkedHashMap; import java.util.Map; import javax.jws.WebService; @WebService(endpointInterface = "org.javacodegeeks.cxf.soap.EmployeeService") public class EmployeeServiceImpl implements EmployeeService { private Map employees = new LinkedHashMap(); public String message(String message) { return "Received " + message; } public String addEmployee(Employee employee) { employees.put(employees.size() + 1, employee); return "Add " + employee.getName(); } public Map getEmployees() { return employees; } }
EmployeeServiceLiveTest
class is shown below which tests the three methods message
, addEmployee
, and getEmployees
.
EmployeeServiceLiveTest
package org.javacodegeeks.cxf.soap; import static org.junit.Assert.assertEquals; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; import org.junit.Before; import org.junit.Test; public class EmployeeServiceLiveTest { private static QName SERVICE_NAME = new QName("http://javacodegeeks.com/", "EmployeeService"); private static QName PORT_NAME = new QName("http://javacodegeeks.com/", "EmployeeServicePort"); private Service service; private EmployeeService employeeServiceProxy; private EmployeeServiceImpl employeeServiceImpl; { service = Service.create(SERVICE_NAME); final String endpointAddress = "http://localhost:8080/EmployeeService"; service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress); } @Before public void reinstantiateEmployeeServiceInstances() { employeeServiceImpl = new EmployeeServiceImpl(); employeeServiceProxy = service.getPort(PORT_NAME, EmployeeService.class); } @Test public void whenUsingMessageMethod_thenCorrect() { final String endpointResponse = employeeServiceProxy.message("Greetings"); final String localResponse = employeeServiceImpl.message("Greetings"); assertEquals(localResponse, endpointResponse); } @Test public void whenUsingAddEmployeeMethod_thenCorrect() { final Employee employee = new EmployeeImpl("Thomas Smith"); final String endpointResponse = employeeServiceProxy.addEmployee(employee); final String localResponse = employeeServiceImpl.addEmployee(employee); assertEquals(localResponse, endpointResponse); } @Test public void usingGetEmployeesMethod_thenCorrect() { final Employee employee1 = new EmployeeImpl("Thomas"); employeeServiceProxy.addEmployee(employee1); final Employee employee2 = new EmployeeImpl("Sam"); employeeServiceProxy.addEmployee(employee2); final Map employees = employeeServiceProxy.getEmployees(); assertEquals("Thomas", employees.get(1).getName()); assertEquals("Sam", employees.get(2).getName()); } }
The libraries required for this soap service are configured in the pom.xml as shown below:
Libraries configuration
<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-jetty</artifactId> <version>${cxf.version}</version> </dependency> </dependencies>
Maven command is used to build the employee soap service. The command is as below :
Maven Package
mvn package
The output of the executed command is shown below.
Maven command is used to start the server. The command is as below :
Maven exec java
mvn exec:java
The output of the executed command is shown below.
Maven command is used to build the tests. The command is as below :
Maven Test
mvn test
The output of the executed command is shown below.
Maven command is used to execute the EmployeeServiceLiveTest
. The command is as below :
Maven Execute Test
mvn -Dtest=EmployeeServiceLiveTest
The output of the executed command is shown below.
2.5 REST Service
Let us look at building a Department REST Service. We start with the Employee
class. Employee
class has id
and name
properties.
Employee
package org.javacodegeeks.cxf.rest; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Employee") public class Employee { private int id; private String name; 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; } @Override public int hashCode() { return id + name.hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof Employee) && (id == ((Employee) obj).getId()) && (name.equals(((Employee) obj).getName())); } }
Now let us look at Department
class. Department has a list of employees. It has methods to getEmployee
,createEmployee
, update Employee
, deleteEmployee
, and getEmployees
. The code is shown below.
Department
package org.javacodegeeks.cxf.rest; import javax.ws.rs.*; import javax.ws.rs.core.Response; import javax.xml.bind.annotation.XmlRootElement; import java.util.ArrayList; import java.util.List; @XmlRootElement(name = "Department") public class Department { private int id; private String name; private List employees = new ArrayList(); 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 List getEmployees() { return employees; } public void setEmployees(List employees) { this.employees = employees; } @GET @Path("{employeeId}") public Employee getEmployee(@PathParam("employeeId") int employeeId) { return findById(employeeId); } @POST public Response createEmployee(Employee employee) { for (Employee element : employees) { if (element.getId() == employee.getId()) { return Response.status(Response.Status.CONFLICT).build(); } } employees.add(employee); return Response.ok(employee).build(); } @DELETE @Path("{employeeId}") public Response deleteEmployee(@PathParam("employeeId") int employeeId) { Employee employee = findById(employeeId); if (employee == null) { return Response.status(Response.Status.NOT_FOUND).build(); } employees.remove(employee); return Response.ok().build(); } private Employee findById(int id) { for (Employee employee : employees) { if (employee.getId() == id) { return employee; } } return null; } @Override public int hashCode() { return id + name.hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof Department) && (id == ((Department) obj).getId()) && (name.equals(((Department) obj).getName())); } }
DepartmentRepository
class has getDepartment
, and updateDepartment
methods. The code is shown below.
DepartmentRepository
package org.javacodegeeks.cxf.rest; import javax.ws.rs.*; import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Path("departmentservice") @Produces("text/xml") public class DepartmentRepository { private Map departments = new HashMap(); { Employee employee1 = new Employee(); Employee employee2 = new Employee(); employee1.setId(1); employee1.setName("Employee A"); employee2.setId(2); employee2.setName("Employee B"); List department1Employees = new ArrayList(); department1Employees.add(employee1); department1Employees.add(employee2); Department department1 = new Department(); Department department2 = new Department(); department1.setId(1); department1.setName("Accounting"); department1.setEmployees(department1Employees); department2.setId(2); department2.setName("Finance"); departments.put(1, department1); departments.put(2, department2); } @GET @Path("departments/{departmentId}") public Department getDepartment(@PathParam("departmentId") int departmentId) { return findById(departmentId); } @PUT @Path("departments/{departmentId}") public Response updateDepartment(@PathParam("departmentId") int departmentId, Department department) { Department existingDepartment = findById(departmentId); if (existingDepartment == null) { return Response.status(Response.Status.NOT_FOUND).build(); } if (existingDepartment.equals(department)) { return Response.notModified().build(); } departments.put(departmentId, department); return Response.ok().build(); } @Path("departments/{departmentId}/employees") public Department pathToEmployee(@PathParam("departmentId") int departmentId) { return findById(departmentId); } private Department findById(int id) { for (Map.Entry department : departments.entrySet()) { if (department.getKey() == id) { return department.getValue(); } } return null; } }
RestfulServer
class is shown below which has the code to start the Server and create the DepartmentService
.
RestfulServer
package org.javacodegeeks.cxf.rest; import org.apache.cxf.endpoint.Server; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; public class RestfulServer { public static void main(String args[]) throws Exception { JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean(); factoryBean.setResourceClasses(DepartmentRepository.class); factoryBean.setResourceProvider(new SingletonResourceProvider(new DepartmentRepository())); factoryBean.setAddress("http://localhost:8080/"); Server server = factoryBean.create(); System.out.println("Server Starting"); Thread.sleep(1000 * 1000); System.out.println("Server Shutting Down"); server.destroy(); System.exit(0); } }
DepartmentServiceLiveTest
class is shown below which has the unit tests for DepartmentService
methods.
DepartmentServiceLiveTest
package org.javacodegeeks.cxf.rest; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import javax.xml.bind.JAXB; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class DepartmentServiceLiveTest { private static final String BASE_URL = "http://localhost:8080/departmentservice/departments/"; private static CloseableHttpClient client; @BeforeClass public static void createClient() { client = HttpClients.createDefault(); } @AfterClass public static void closeClient() throws IOException { client.close(); } @Test public void whenUpdateNonExistentDepartment_thenReceiveNotFoundResponse() throws IOException { final HttpPut httpPut = new HttpPut(BASE_URL + "3"); final InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream("non_existent_department.xml"); httpPut.setEntity(new InputStreamEntity(resourceStream)); httpPut.setHeader("Content-Type", "text/xml"); final HttpResponse response = client.execute(httpPut); assertEquals(404, response.getStatusLine().getStatusCode()); } @Test public void whenUpdateUnchangedDepartment_thenReceiveNotModifiedResponse() throws IOException { final HttpPut httpPut = new HttpPut(BASE_URL + "1"); final InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream("unchanged_department.xml"); httpPut.setEntity(new InputStreamEntity(resourceStream)); httpPut.setHeader("Content-Type", "text/xml"); final HttpResponse response = client.execute(httpPut); assertEquals(304, response.getStatusLine().getStatusCode()); } @Test public void whenUpdateValidDepartment_thenReceiveOKResponse() throws IOException { final HttpPut httpPut = new HttpPut(BASE_URL + "2"); final InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream("changed_department.xml"); httpPut.setEntity(new InputStreamEntity(resourceStream)); httpPut.setHeader("Content-Type", "text/xml"); final HttpResponse response = client.execute(httpPut); assertEquals(200, response.getStatusLine().getStatusCode()); final Department department = getDepartment(2); assertEquals(2, department.getId()); assertEquals("Sales", department.getName()); } @Test public void whenCreateConflictEmployee_thenReceiveConflictResponse() throws IOException { final HttpPost httpPost = new HttpPost(BASE_URL + "1/employees"); final InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream("conflict_employee.xml"); httpPost.setEntity(new InputStreamEntity(resourceStream)); httpPost.setHeader("Content-Type", "text/xml"); final HttpResponse response = client.execute(httpPost); assertEquals(409, response.getStatusLine().getStatusCode()); } @Test public void whenCreateValidEmployee_thenReceiveOKResponse() throws IOException { final HttpPost httpPost = new HttpPost(BASE_URL + "2/employees"); final InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream("created_employee.xml"); httpPost.setEntity(new InputStreamEntity(resourceStream)); httpPost.setHeader("Content-Type", "text/xml"); final HttpResponse response = client.execute(httpPost); assertEquals(200, response.getStatusLine().getStatusCode()); final Employee employee = getEmployee(2, 3); assertEquals(3, employee.getId()); assertEquals("Employee C", employee.getName()); } @Test public void whenDeleteInvalidEmployee_thenReceiveNotFoundResponse() throws IOException { final HttpDelete httpDelete = new HttpDelete(BASE_URL + "1/employees/3"); final HttpResponse response = client.execute(httpDelete); assertEquals(404, response.getStatusLine().getStatusCode()); } @Test public void whenDeleteValidEmployee_thenReceiveOKResponse() throws IOException { final HttpDelete httpDelete = new HttpDelete(BASE_URL + "1/employees/1"); final HttpResponse response = client.execute(httpDelete); assertEquals(200, response.getStatusLine().getStatusCode()); final Department department = getDepartment(1); assertEquals(1, department.getEmployees().size()); assertEquals(2, department.getEmployees().get(0).getId()); assertEquals("Employee B", department.getEmployees().get(0).getName()); } private Department getDepartment(int departmentOrder) throws IOException { final URL url = new URL(BASE_URL + departmentOrder); final InputStream input = url.openStream(); return JAXB.unmarshal(new InputStreamReader(input), Department.class); } private Employee getEmployee(int departmentOrder, int employeeOrder) throws IOException { final URL url = new URL(BASE_URL + departmentOrder + "/employees/" + employeeOrder); final InputStream input = url.openStream(); return JAXB.unmarshal(new InputStreamReader(input), Employee.class); } }
The libraries required for this rest service are configured in the pom.xml as shown below:
Libraries configuration
<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-transports-http-jetty</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>${httpclient.version}</version> <exclusions> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency>
Maven command is used to build the Department Rest service. The command is as below :
Maven Package
mvn package
The output of the executed command is shown below.
Maven command is used to start the server. The command is as below :
Maven exec java
mvn exec:java
The output of the executed command is shown below.
Maven command is used to build the tests. The command is as below :
Maven Test
mvn test
The output of the executed command is shown below.
Maven command is used to execute the DepartmentServiceLiveTest
. The command is as below :
Maven Execute Test
mvn -Dtest=DepartmentServiceLiveTest
The output of the executed command is shown below.
3. Download the Source Code
You can download the full source code of this example here: Apache CXF Web Service Example