Apache CXF Web服务实例的详细教程

801 阅读2分钟

在这篇文章中,我们将实现一个Apache CXF的Web服务实例。网络服务可以使用SOAP和REST来开发。SOAP指的是简单对象访问协议。REST指的是Representational State Transfer。

1.概述

Roy Fielding是第一个提出REST架构建议的人。 在REST中,资源是一个统一的资源标识符或URI。一个资源在任何给定时间点的状态由一个文档表示,被称为资源的表示。

Apache CXF框架起源于两个名为Celtix和XFire的项目。CXF是由Celtix和XFire创造的。 Celtix是一个开源的企业服务总线。这个开源项目是由IONA赞助的。XFire是一个开源的SOAP框架。Apache CXF支持Plain Old Apache CXF对象(POJO)、JAX-WS、WSDL、JAX-RS和JMS。在接下来的章节中,我们将看看Apache CXF JAX-WS和JAX-RS web服务的例子。

2.Apache CXF Web 服务示例

Apache CXF是基于JAX-WS和JAX-RS标准的。该框架提供了处理 WSDL 和 java 对象的功能。它有修改 XML 消息、SOAP、JAX-RS Web 服务和 spring 框架集成的 API。

2.1 先决条件

在Linux、windows或mac操作系统上需要Java 8。 构建apache cxf应用程序需要Maven 3.6.1。

2.2 下载

您可以从Oracle网站上下载Java 8。Apache Maven 3.6.1可以从Apache网站下载。

2.3 设置

以下是Java环境所需的设置命令。

Java设置

JAVA_HOME="/desktop/jdk1.8.0_73"
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH

maven的环境变量设置如下。

Maven设置

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服务

让我们来看看雇员SOAP服务。 我们从Employee 接口开始。Employee 接口有getName 方法

雇员

package org.javacodegeeks.cxf.soap;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlJavaTypeAdapter(EmployeeAdapter.class)
public interface Employee {
    public String getName();
}

EmployeeImpl 类实现了 接口。代码显示如下Employee

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 接口,如下图所示。该接口有 , 和 方法。message addEmployee getEmployees

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 类扩展了 接口,如下所示。EmployeeService

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 类如下所示,它测试了三个方法 , , 和 。message addEmployee 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());
    }
}

这个soap服务所需的库在pom.xml中被配置,如下所示。

库的配置

 <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命令被用来构建Employee soap服务。该命令如下。

Maven包

mvn package

执行命令的输出如下所示。

CXF Web Service Example - Building Soap Service

构建Soap服务

Maven命令用于启动服务器。该命令如下。

Maven exec java

mvn exec:java

执行命令的输出结果如下。

CXF Web Service Example - Running Soap Server

运行Soap服务器

Maven命令用于构建测试。该命令如下。

Maven测试

mvn test

执行命令的输出结果如下。

CXF Web Service Example - Building soap tests

构建soap测试

Maven命令用于执行EmployeeServiceLiveTest 。该命令如下。

Maven Execute Test

mvn -Dtest=EmployeeServiceLiveTest

执行命令的输出结果如下。

CXF Web Service Example - Execute SoapService Tests

执行SoapService测试

2.5 REST服务

让我们来看看如何构建一个部门REST服务。我们从Employee 类开始。Employee 类有idname 属性。

雇员

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()));
    }
}

现在让我们来看看Department 类。部门有一个雇员的列表。它的方法有getEmployee,createEmployee,update Employee,deleteEmployee, 和getEmployees 。代码显示如下。

部门

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 类有 , 和 方法。代码显示如下。getDepartment updateDepartment

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 类如下所示,它有启动服务器和创建 的代码。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 类如下所示,其中有对 方法的单元测试。DepartmentService

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);
    }
}

这个休息服务所需的库在pom.xml中配置,如下所示。

库的配置

<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命令被用来构建Department Rest Service。该命令如下。

Maven包

mvn package

执行命令的输出结果如下所示:

构建Rest服务

Maven命令用于启动服务器。该命令如下。

Maven exec java

mvn exec:java

执行命令的输出结果如下:

运行服务器

Maven命令用于构建测试。该命令如下。

Maven测试

mvn test

执行命令的输出结果如下。

构建Rest Service测试

Maven命令用于执行DepartmentServiceLiveTest 。该命令如下。

Maven Execute Test

mvn -Dtest=DepartmentServiceLiveTest

执行命令的输出结果如下。

执行测试