在这篇文章中,我们将实现一个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
执行命令的输出如下所示。

构建Soap服务
Maven命令用于启动服务器。该命令如下。
Maven exec java
mvn exec:java
执行命令的输出结果如下。

运行Soap服务器
Maven命令用于构建测试。该命令如下。
Maven测试
mvn test
执行命令的输出结果如下。

构建soap测试
Maven命令用于执行EmployeeServiceLiveTest 。该命令如下。
Maven Execute Test
mvn -Dtest=EmployeeServiceLiveTest
执行命令的输出结果如下。

执行SoapService测试
2.5 REST服务
让我们来看看如何构建一个部门REST服务。我们从Employee 类开始。Employee 类有id 和name 属性。
雇员
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
执行命令的输出结果如下。

执行测试