Web Services

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.

CXF Web Service Example - Building Soap Service
Building Soap Service

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.

CXF Web Service Example - Running Soap Server
Running Soap Server

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.

CXF Web Service Example - Building soap tests
Building soap tests

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.

CXF Web Service Example - Execute SoapService Tests
Execute SoapService Tests

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.

Building Rest Service

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.

Run the Server

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.

Build Rest Service tests

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.

Execute the Tests

3. Download the Source Code

Download
You can download the full source code of this example here: Apache CXF Web Service Example

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 20 years’ experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript. He has reviewed the Manning book titled : "Machine Learning with TensorFlow”. He is also the author of Packt Publishing book - "Hands-On Data Structures and Algorithms with Go".He is member of IFX forum,Oracle JCP and participant in Java Community Process. He is member of the MIT Technology Review Global Panel.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button