使用CXF开发RESTFul服务

相信大家在阅读CXF官方文档(http://cxf.apache.org/docs/index.html)时,总是一知半解。这里向大家推荐一本PacktPub.Apache.CXF.Web.Service.Development。目前,这本书是没有中文版的,为此笔者简单的写了一些经验总结。

CXF官方文档(http://cxf.apache.org/docs/index.html)时,总是一知半解。这里向大家推荐一本PacktPub.Apache.CXF.Web.Service.Development。目前,这本书是没有中文版的,为此笔者简单的写了一些经验总结。

这本书内容上安排的比较浅显,语言上也没有什么深奥的,值得一读。另外值得一提的是,这本书比官方文档高明之处是每个概念就介绍得很清楚,像第二章的Code-first和Contract-first,第5章的Feature和Interceptor概念的介绍,还有第6章的REST概念简介,内容上都比官方文档详细很多。缺点就是并没有将CXF所有的特性都写下来,这就需要将samples中的内容好好消化一下了。

代码使用Maven组织

http://dl.iteye.com/topics/download/fbbf344b-2357-33fe-86d7-a44116e6de85

使用CXF开发RESTFul服务

在各个系统交互领域,Web services逐渐成为主流。有两种主要方式来开发Web Services:Simple Object Access Protocol (SOAP)和Representational State Transfer (REST)

开发基于SOAP的Web Services需要很多的约束, 以便客户端和服务端交互信息,例如,使用Web Service Description Language (WSDL)来描述信息。还有很多WS的标准如WS-Security。

使用REST构架的服务被称为RESTful服务。这种架构利用简单的XML在HTTP协议上,就像网页一样发送请求,简化了基于SOAP架构开发。RESTful Web Services尤其适用于只需要提交和接受简单的XML信息。

接下来会介绍使用CXF框架来开发RESTful风格的Web Services。

简介

Java API for RESTful services

CXF JAX-RS实现

开发RESTful服务

简介

REST也就是Representational State Transfer。REST并不特指一种技术,也不是一个标准,它仅仅是一个构架风格。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则通过网络暴露资源给用户。事实上,WWW就是经典的REST架构风格。在服务器端,应用程序状态和功能可以分为各种资源。它向客户端公开。每个资源都使用 URI (Universal Resource Identifier) 得到一个惟一的地址。所有资源都共享统一的界面,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。客户端通过交换各种资源的表现体来查询或更新资源。资源的表现体包括HTML,XML,JSON等。客户端需要解析响应服务器的表现体。客户端和服务器之间的交互在请求之间是无状态的,无状态请求可以由任何可用服务器应答。

下面是一些RESTfu例子,提供雇员和部门的信息,并介绍客户端怎样访问这些服务

URI for the RESTful service—http://<host>/department/deptname/employee:

•     GET—获得deptname部门的所有员工信息

•     POST—为deptname部门创建员工信息。

•     DELETE—删除 deptname部门一名员工信息

URI for the RESTful service—http://<host>/department/deptname/employee/naveen:

•     GET—获得deptname部门名叫naveen的员工信息

•     PUT—为deptname部门创建名叫naveen的员工信息

•     DELETE—删除deptname部门名叫naveen的员工信息

下面是POST请求的例子http://<host>/department/deptname/employee

POST /department/deptname/employee HTTP/1.1

Content-Type: */*

Accept: application/xml

User-Agent: Apache CXF 2.2.2

Cache-Control: no-cache

Pragma: no-cache

Host: 127.0.0.1:9001

<employee><firstname>rajeev</firstname><lastname>hathi</lastname>

<dob>10/26/78</dob></employee>

Java API for RESTful services

上一节,我们了解雇员POST请求。如果需要提供一种实现去识别雇员POST请求,我们应该做如下工作:

识别这是否一个HTTP POST请求。

将HTTP POST请求中的XML的内容转换为实现端所需要的格式,例如JAVA对象。

执行指定的操作,例如插入雇员信息到数据库。

以HTTP形式响应客户端,例如设置标志响应成功的HTTP状态200 ,并将响应转换到指定格式(XML或JSON),最后将其设置到HTTP Body。

依据需求,可能你要实现所有的HTTP方法,如GET,PUT,DELETE等。这不就是标准的RESTful JAVA开发模式吗?接着,Java API for RESTful Web services (JAX-RS)规范制定了开发RESTful的标准。

JAX-RS规范定义了创建RESTful服务的语法。JAX-RS使用annotations注解在实现RESTful的服务,使用annotations注解POJO将它暴露为RESTful资源。RESTful服务类中,通过URI(/category)和HTTP(GET, POST)方法注解方法。

@GET

@Path("/category/{id}")

public Category getCategory(@PathParam("id") String id)

实现JAX-RS的框架在运行的时候,通过映射HTTP请求到RESTful方法,负责调用正确的JAVA实现方法。JAX-RS规范提供这种映射的方法的算法。基础算法包括,判断JAVA资源类,判断HTTP URI请求,判断内容格式(例如application/xml),还有HTTP方法(例如GET)

JAX-RS规范提出如下要点:

POJO依赖性

为了暴露为资源,使用annotations注解POJO,

HTTP依赖性

RESTful资源暴露在HTTP中,规范中将HTTP 协议和JAX-RS API相对应映射。

格式独立性

API中提供嵌入式的方法,标准化添加HTTP内容的类型。例如,application/xml就是HTTP内容的类型中的一种。

容器独立性

可以在任何一种容器中部署实现JAX-RS规范的应用程序。

CXF JAX-RS实现

CXF实现了JAX-RS1.0规范,并提供了很多特性帮助开发者搭建企业级的RESTful服务。

下面是CXF框架提供创建RESTful服务的各种特性。

集成Spring

Spring框架已经变成事实上的构建企业级JAVA应用程序集成框架。CXF提供与Spring整合,简化了配置和部署RESTful应用程序。Spring提供依赖注入促进松散耦合,提供各种服务,像声明式事务管理。所有这些Spring提供的特性都可以被开发RESTful服务的CXF框架使用。

插入式数据绑定

数据库绑定就是映射HTTP请求,例如JSON或XML,到对应的JAVA对象。同样的,在发送HTTP响应之前,服务端的JAVA实现需要映射为客户端所需要的格式。通过提供数据库绑定组件,CXF在后台透明的处理映射。CXF支持各种数据绑定机制,如JAXB,JSON,XMLBean和Aegis。CXF允许指定特定的绑定机制。

客户端API

JAX-RS规范并没有提供客户端调用REST服务的API。CXF提供了这种API直接调用RESTful服务,也可以使用Spring框架配置到应用程序中。

安全

CXF可以使用Spring框架集成的声明式安全组件,按照应用程序的需要限制资源类和方法,而不必使用代码处理安全性问题。

过滤器

过滤器用来预处理或后处理信息。CXF可以创建和配置过滤器来审核信息,记录信息的日志,还有基于应用要求修改请求或响应。

CXF也运行开发者使用JAX-WS Provider和Dispatch API来创建RESTful服务。

开发RESTful服务

本节将介绍使用JAX-RS实现的方法来开发RESTful服务,并执行CRUD操作。首先看一下Book Shop应用。

Book Shop应用是一款网络应用,提供技术书籍(JAVA或.NET)的分类。Book Shop让管理员为新的书籍创建分类,修改分类等。一旦这个分类存在,应用可以为这个分类添加新的书籍。

这个应用将提供如下方法:

创建分类

更新分类

删除分类

获取分类列表

获取特定分类

为特定分类添加书籍

获取特定分类中所有书籍

为了开发RESTful服务,需要做如下工作:

创建POJO类

为POJO类提供数据绑定

创建实现RESTful功能的服务类

创建调用RESTful服务的客户端

创建POJO类

  1. package demo.restful;
  2. import javax.xml.bind.annotation.XmlRootElement;
  3. @XmlRootElement(name = "Book")
  4. public class Book {
  5. private String bookId;
  6. private String bookISBNnumber;
  7. private String bookName;
  8. //Let assume one author only
  9. private String author;
  10. public String getBookId() {
  11. return bookId;
  12. }
  13. public void setBookId(String bookId) {
  14. this.bookId = bookId;
  15. }
  16. public String getBookISBNnumber() {
  17. return bookISBNnumber;
  18. }
  19. public void setBookISBNnumber(String bookISBNnumber) {
  20. this.bookISBNnumber = bookISBNnumber;
  21. }
  22. public String getBookName() {
  23. return bookName;
  24. }
  25. public void setBookName(String bookName) {
  26. this.bookName = bookName;
  27. }
  28. public String getAuthor() {
  29. return author;
  30. }
  31. public void setAuthor(String author) {
  32. this.author = author;
  33. }
  34. }
package demo.restful;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "Book")
public class Book { private String bookId; private String bookISBNnumber; private String bookName; //Let assume one author only
private String author; public String getBookId() {
return bookId;
} public void setBookId(String bookId) {
this.bookId = bookId;
} public String getBookISBNnumber() {
return bookISBNnumber;
} public void setBookISBNnumber(String bookISBNnumber) {
this.bookISBNnumber = bookISBNnumber;
} public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public String getAuthor() {
return author;
} public void setAuthor(String author) {
this.author = author;
} }
  1. package demo.restful;
  2. import java.util.Collection;
  3. import javax.xml.bind.annotation.XmlRootElement;
  4. @XmlRootElement(name = "Category")
  5. public class Category {
  6. private String categoryId;
  7. private String categoryName;
  8. private Collection<Book> books;
  9. public String getCategoryId() {
  10. return categoryId;
  11. }
  12. public void setCategoryId(String categoryId) {
  13. this.categoryId = categoryId;
  14. }
  15. public String getCategoryName() {
  16. return categoryName;
  17. }
  18. public void setCategoryName(String categoryName) {
  19. this.categoryName = categoryName;
  20. }
  21. public Collection<Book> getBooks() {
  22. return books;
  23. }
  24. public void setBooks(Collection<Book> books) {
  25. this.books = books;
  26. }
  27. }
package demo.restful;

import java.util.Collection;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "Category")
public class Category { private String categoryId; private String categoryName; private Collection<Book> books; public String getCategoryId() {
return categoryId;
} public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
} public String getCategoryName() {
return categoryName;
} public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
} public Collection<Book> getBooks() {
return books;
} public void setBooks(Collection<Book> books) {
this.books = books;
} }

为POJO类提供数据绑定

为了提供RESTful服务端和客户端通信,POJO需要转换为特定的格式,例如XML或JSON。为了实现这部分功能,需要一个数据绑定的组件来映射JAVA对象和XML(或者指定的格式)。

CXF使用JAXB作为默认的数据绑定组件。JAXB使用注解来定义JAVA对象和XML之间映射的关系。

在POJO类Category中,注解@XmlRootElement指定Category为XML的根元素。Category类的属性默认指定映射为@XmlElement。@XmlElement用来定义XML中的子元素。@XmlRootElement和@XmlElement允许自定义命名空间和XML中元素的名称。如果没有定义的话,JAXB在运行的时候默认的使用同样的属性名和类名来定义XML元素。

下面的XML请求表示了Category数据对象。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<Category>

<books>

<author>Naveen Balani</author>

<bookISBNnumber>ISBNB001</bookISBNnumber>

<bookId>NB001</bookId>

<bookName>Fiction Book1</bookName>

</books>

<categoryId>005</categoryId>

<categoryName>Fiction Series</categoryName>

</Category>

创建实现RESTful功能的服务类

类注解

CategoryService的类声明上定义了@Path 和@Produces注解。@Path定义了URI路径,客户端可以通过这个路径访问到CategoryService对象。如@Path("/categoryservice"),那么URI请求,就可以是http://localhost:9000/categoryservice/。@Produces定义了服务类和方法生产内容的类型。如@Produces("application/xml"),那么CategoryService只会生产application/xml。

方法注解

每个方法都和@Produces相关,它决定了这个方法生产什么样的响应。每个方法都有和HTTP方法映射的的注解,例如@GET 和@POST。方法中的@Path注解指定了该方法的URI。例如getCategory()方法上的@Path("/category/{id}"),可以使用http://localhost:9000/categoryservice/category/001,访问到category id 为001的分类。{id}就好比参数。接着,让我们看看@PathParam注解。@PathParam注解是用来给URI 路径一个模板变量,方法的输入参数。@PathParam("id") 就是URI中的 @Path ("/category/{id}")。

@GET

@Path("/category/{id}")

public Category getCategory(@PathParam("id") String id)

异常处理

让我们看一种情况,客户端发送一个请求删除或者更新一个分类,但是这个分类并不存在,那么服务端需要返回正确的错误信息给客户端。

为了处理异常,JAX-RS提供了WebApplicationException,它继承自RuntimeException类。WebApplicationException可以使用HTTP状态代码或者javax.ws.rs.core.Response对象作为构造器。Response对象除了提供HTTP状态代码外,还可以提供用户容易识别的错误信息。

RESTful服务的异常处理可以分为如下几类:

实现类抛出带有HTTP错误代码的WebApplicationException异常。一般的,4XX定义了客户端错误,如错误请求;5XX定义了服务端错误,如服务器没有完成请求。

直接发回javax.ws.rs.core.Response对象,Response对象中包含了HTTP错误代码。

为了测试异常处理代码,启动CategoryServerStart类。在IE浏览器中输入

http://localhost:9000/categoryservice/category/011

由于IE不会显示自定义的错误信息,我们会看到HTTP 400 BAD Request。

如果你在Firefox或者Chrome,就会返回

<error>Category Not Found</error>

添加JSON支持

在@Produces 和 @Consumes注解中添加application/json,指定CategoryService除了application/xml外,还接收和产生application/json。CXF运行时会处理HTTP JSON请求到JAVA对象的转换,还有JAVA对象到HTTP JSON响应的映射。

  1. package demo.restful;
  2. //JAX-RS Imports
  3. import javax.ws.rs.Consumes;
  4. import javax.ws.rs.DELETE;
  5. import javax.ws.rs.GET;
  6. import javax.ws.rs.POST;
  7. import javax.ws.rs.PUT;
  8. import javax.ws.rs.Path;
  9. import javax.ws.rs.PathParam;
  10. import javax.ws.rs.Produces;
  11. import javax.ws.rs.WebApplicationException;
  12. import javax.ws.rs.core.Response;
  13. import javax.ws.rs.core.Response.ResponseBuilder;
  14. import javax.ws.rs.core.Response.Status;
  15. /*
  16. * CategoryService class - Add/Removes category for books
  17. */
  18. @Path("/categoryservice")
  19. @Produces({"application/json","application/xml"})
  20. public class CategoryService {
  21. private CategoryDAO categoryDAO = new CategoryDAO();
  22. public CategoryDAO getCategoryDAO() {
  23. return categoryDAO;
  24. }
  25. public void setCategoryDAO(CategoryDAO categoryDAO) {
  26. this.categoryDAO = categoryDAO;
  27. }
  28. @GET
  29. @Path("/category/{id}")
  30. @Produces({"application/json","application/xml"})
  31. public Category getCategory(@PathParam("id") String id) {
  32. System.out.println("getCategory called with category id: " + id);
  33. Category cat = (Category) getCategoryDAO().getCategory(id);
  34. if (cat == null) {
  35. ResponseBuilder builder = Response.status(Status.BAD_REQUEST);
  36. builder.type("application/xml");
  37. builder.entity("<error>Category Not Found</error>");
  38. throw new WebApplicationException(builder.build());
  39. } else {
  40. return cat;
  41. }
  42. }
  43. @POST
  44. @Path("/category")
  45. @Consumes({"application/json","application/xml"})
  46. public Response addCategory(Category category) {
  47. System.out.println("addCategory called");
  48. Category cat = (Category) getCategoryDAO().getCategory(
  49. category.getCategoryId());
  50. if (cat != null) {
  51. return Response.status(Status.BAD_REQUEST).build();
  52. } else {
  53. getCategoryDAO().addCategory(category);
  54. return Response.ok(category).build();
  55. }
  56. }
  57. @DELETE
  58. @Path("/category/{id}")
  59. @Consumes({"application/json","application/xml"})
  60. public Response deleteCategory(@PathParam("id") String id) {
  61. System.out.println("deleteCategory with category id : " + id);
  62. Category cat = (Category) getCategoryDAO().getCategory(id);
  63. if (cat == null) {
  64. return Response.status(Status.BAD_REQUEST).build();
  65. } else {
  66. getCategoryDAO().deleteCategory(id);
  67. return Response.ok().build();
  68. }
  69. }
  70. @PUT
  71. @Path("/category")
  72. @Consumes({"application/json","application/xml"})
  73. public Response updateCategory(Category category) {
  74. System.out.println("updateCategory with category id : "
  75. + category.getCategoryId());
  76. Category cat = (Category) getCategoryDAO().getCategory(
  77. category.getCategoryId());
  78. if (cat == null) {
  79. return Response.status(Status.BAD_REQUEST).build();
  80. } else {
  81. getCategoryDAO().updateCategory(category);
  82. return Response.ok(category).build();
  83. }
  84. }
  85. @POST
  86. @Path("/category/book")
  87. @Consumes({"application/json","application/xml"})
  88. public Response addBooks(Category category) {
  89. System.out.println("addBooks with category id : "
  90. + category.getCategoryId());
  91. Category cat = (Category) getCategoryDAO().getCategory(
  92. category.getCategoryId());
  93. if (cat == null) {
  94. return Response.status(Status.NOT_FOUND).build();
  95. } else {
  96. getCategoryDAO().addBook(category);
  97. return Response.ok(category).build();
  98. }
  99. }
  100. @GET
  101. @Path("/category/{id}/books")
  102. @Consumes("application/xml,application/json")
  103. public Response getBooks(@PathParam("id") String id) {
  104. System.out.println("getBooks called with category id : " + id);
  105. Category cat = (Category) getCategoryDAO().getCategory(id);
  106. if (cat == null) {
  107. return Response.status(Status.NOT_FOUND).build();
  108. } else {
  109. cat.setBooks(getCategoryDAO().getBooks(id));
  110. return Response.ok(cat).build();
  111. }
  112. }
  113. }
package demo.restful;

//JAX-RS Imports
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status; /*
* CategoryService class - Add/Removes category for books
*/ @Path("/categoryservice")
@Produces({"application/json","application/xml"})
public class CategoryService { private CategoryDAO categoryDAO = new CategoryDAO(); public CategoryDAO getCategoryDAO() {
return categoryDAO;
} public void setCategoryDAO(CategoryDAO categoryDAO) {
this.categoryDAO = categoryDAO;
} @GET
@Path("/category/{id}")
@Produces({"application/json","application/xml"})
public Category getCategory(@PathParam("id") String id) { System.out.println("getCategory called with category id: " + id); Category cat = (Category) getCategoryDAO().getCategory(id);
if (cat == null) {
ResponseBuilder builder = Response.status(Status.BAD_REQUEST);
builder.type("application/xml");
builder.entity("<error>Category Not Found</error>");
throw new WebApplicationException(builder.build());
} else {
return cat;
}
} @POST
@Path("/category")
@Consumes({"application/json","application/xml"})
public Response addCategory(Category category) { System.out.println("addCategory called"); Category cat = (Category) getCategoryDAO().getCategory(
category.getCategoryId()); if (cat != null) {
return Response.status(Status.BAD_REQUEST).build();
} else {
getCategoryDAO().addCategory(category);
return Response.ok(category).build();
} } @DELETE
@Path("/category/{id}")
@Consumes({"application/json","application/xml"})
public Response deleteCategory(@PathParam("id") String id) { System.out.println("deleteCategory with category id : " + id); Category cat = (Category) getCategoryDAO().getCategory(id);
if (cat == null) {
return Response.status(Status.BAD_REQUEST).build();
} else {
getCategoryDAO().deleteCategory(id);
return Response.ok().build();
}
} @PUT
@Path("/category")
@Consumes({"application/json","application/xml"})
public Response updateCategory(Category category) { System.out.println("updateCategory with category id : "
+ category.getCategoryId()); Category cat = (Category) getCategoryDAO().getCategory(
category.getCategoryId());
if (cat == null) {
return Response.status(Status.BAD_REQUEST).build();
} else {
getCategoryDAO().updateCategory(category);
return Response.ok(category).build();
}
} @POST
@Path("/category/book")
@Consumes({"application/json","application/xml"})
public Response addBooks(Category category) { System.out.println("addBooks with category id : "
+ category.getCategoryId()); Category cat = (Category) getCategoryDAO().getCategory(
category.getCategoryId());
if (cat == null) {
return Response.status(Status.NOT_FOUND).build();
} else {
getCategoryDAO().addBook(category);
return Response.ok(category).build();
}
} @GET
@Path("/category/{id}/books")
@Consumes("application/xml,application/json")
public Response getBooks(@PathParam("id") String id) { System.out.println("getBooks called with category id : " + id); Category cat = (Category) getCategoryDAO().getCategory(id); if (cat == null) {
return Response.status(Status.NOT_FOUND).build();
} else {
cat.setBooks(getCategoryDAO().getBooks(id));
return Response.ok(cat).build(); }
} }

DAO

  1. package demo.restful;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. /*
  7. * DataAcess object for performing CRUD operations.
  8. * Dummy implementation.
  9. */
  10. public class CategoryDAO {
  11. private static Map<String, Category> categoryMap = new HashMap<String, Category>();
  12. private static Map<String, Collection<Book>> bookMap = new HashMap<String, Collection<Book>>();
  13. static {
  14. Category category1 = new Category();
  15. category1.setCategoryId("001");
  16. category1.setCategoryName("Java");
  17. categoryMap.put(category1.getCategoryId(), category1);
  18. Book book1 = new Book();
  19. book1.setAuthor("Naveen Balani");
  20. book1.setBookName("Spring Series");
  21. book1.setBookId("001");
  22. book1.setBookISBNnumber("ISB001");
  23. Book book2 = new Book();
  24. book2.setAuthor("Rajeev Hathi");
  25. book2.setBookName("CXF Series");
  26. book2.setBookId("002");
  27. book2.setBookISBNnumber("ISB002");
  28. Collection<Book> booksList = new ArrayList<Book>();
  29. booksList.add(book1);
  30. booksList.add(book2);
  31. bookMap.put(category1.getCategoryId(), booksList);
  32. }
  33. public void addCategory(Category category) {
  34. categoryMap.put(category.getCategoryId(), category);
  35. }
  36. public void addBook(Category category) {
  37. bookMap.put(category.getCategoryId(), category.getBooks());
  38. }
  39. public Collection<Book> getBooks(String categoryId) {
  40. return bookMap.get(categoryId);
  41. }
  42. public Category getCategory(String id) {
  43. Category cat = null;
  44. //Dummy implementation to return a new copy of category to
  45. //avoid getting overridden by service
  46. if(categoryMap.get(id) != null) {
  47. cat = new Category();
  48. cat.setCategoryId(categoryMap.get(id).getCategoryId());
  49. cat.setCategoryName(categoryMap.get(id).getCategoryName());
  50. }
  51. return cat;
  52. }
  53. public void deleteCategory(String id) {
  54. categoryMap.remove(id);
  55. // Remove association of books
  56. bookMap.remove(id);
  57. }
  58. public void updateCategory(Category category) {
  59. categoryMap.put(category.getCategoryId(), category);
  60. }
  61. }
package demo.restful;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map; /*
* DataAcess object for performing CRUD operations.
* Dummy implementation.
*/
public class CategoryDAO { private static Map<String, Category> categoryMap = new HashMap<String, Category>();
private static Map<String, Collection<Book>> bookMap = new HashMap<String, Collection<Book>>(); static { Category category1 = new Category();
category1.setCategoryId("001");
category1.setCategoryName("Java");
categoryMap.put(category1.getCategoryId(), category1); Book book1 = new Book();
book1.setAuthor("Naveen Balani");
book1.setBookName("Spring Series");
book1.setBookId("001");
book1.setBookISBNnumber("ISB001"); Book book2 = new Book();
book2.setAuthor("Rajeev Hathi");
book2.setBookName("CXF Series");
book2.setBookId("002");
book2.setBookISBNnumber("ISB002"); Collection<Book> booksList = new ArrayList<Book>();
booksList.add(book1);
booksList.add(book2); bookMap.put(category1.getCategoryId(), booksList);
} public void addCategory(Category category) {
categoryMap.put(category.getCategoryId(), category); } public void addBook(Category category) {
bookMap.put(category.getCategoryId(), category.getBooks()); } public Collection<Book> getBooks(String categoryId) {
return bookMap.get(categoryId); } public Category getCategory(String id) {
Category cat = null;
//Dummy implementation to return a new copy of category to
//avoid getting overridden by service
if(categoryMap.get(id) != null) {
cat = new Category();
cat.setCategoryId(categoryMap.get(id).getCategoryId());
cat.setCategoryName(categoryMap.get(id).getCategoryName());
}
return cat;
} public void deleteCategory(String id) {
categoryMap.remove(id);
// Remove association of books
bookMap.remove(id);
} public void updateCategory(Category category) {
categoryMap.put(category.getCategoryId(), category); } }

创建调用RESTful服务的客户端

JAX-RS并不提供调用RESTful服务客户端。CXF框架提供了两种方式来创建客户端,这两种都可以使用Spring配置。

代理API

代理API允许你使用RESTful服务的资源类和接口。代理类是客户端直接调用接口方法,使用户不需要手工创建HTTP请求。将RESTful服务类传递给org.apache.cxf.jaxrs.client.JAXRSClientFactory类。一旦代理类创建好了,你可以直接使用RESTful服务接口类的任何方法。

CategoryService store = JAXRSClientFactory.create("http://

localhost:9000", CategoryService.class);

//Makes remote call to Category RESTFul service

store.getBooks("001");

HTTP客户端

使用org.apache.cxf.jaxrs.client.WebClient调用RESTful服务。本例中采用HTTP客户端。

  1. package demo.restful.client;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.Iterator;
  5. import javax.ws.rs.core.Response;
  6. import org.apache.cxf.jaxrs.client.WebClient;
  7. import demo.restful.Book;
  8. import demo.restful.Category;
  9. public class CategoryServiceRESTClient {
  10. //Put some static value
  11. private static final String CATEGORY_URL = "http://localhost:9000/";
  12. private static final String CATEGORY_ID = "005";
  13. private static final String TYPE_XML = "application/xml";
  14. private static final String TYPE_JSON = "application/json";
  15. public static void main(String[] args) {
  16. //System.out.println("Format is " + args[0]);
  17. testAddCategory(TYPE_XML);
  18. testUpdateCategory(TYPE_XML);
  19. testGetCategory(TYPE_XML);
  20. testAddBooksForCategory(TYPE_XML);
  21. testGetBooksForCategory(TYPE_XML);
  22. testDeleteCategory(TYPE_XML);
  23. testAddCategory(TYPE_JSON);
  24. testUpdateCategory(TYPE_JSON);
  25. testGetCategory(TYPE_JSON);
  26. testAddBooksForCategory(TYPE_JSON);
  27. testGetBooksForCategory(TYPE_JSON);
  28. testDeleteCategory(TYPE_JSON);
  29. //      if(args[0] !=null && args[0].equalsIgnoreCase(TYPE_XML)){
  30. //          //Content type- XML
  31. //          testAddCategory(TYPE_XML);
  32. //          testUpdateCategory(TYPE_XML);
  33. //          testGetCategory(TYPE_XML);
  34. //          testAddBooksForCategory(TYPE_XML);
  35. //          testGetBooksForCategory(TYPE_XML);
  36. //          testDeleteCategory(TYPE_XML);
  37. //      }
  38. //
  39. //      if(args[0] !=null && args[0].equalsIgnoreCase(TYPE_JSON)){
  40. //          //ContentType- JSON
  41. //          testAddCategory(TYPE_JSON);
  42. //          testUpdateCategory(TYPE_JSON);
  43. //          testGetCategory(TYPE_JSON);
  44. //          testAddBooksForCategory(TYPE_JSON);
  45. //          testGetBooksForCategory(TYPE_JSON);
  46. //          testDeleteCategory(TYPE_JSON);
  47. //      }
  48. }
  49. private static void testAddCategory(final String format) {
  50. System.out.println("testAddCategory called with format " + format);
  51. WebClient client = WebClient.create(CATEGORY_URL);
  52. client.path("/categoryservice/category").accept(
  53. format).type(format);
  54. Category cat = new Category();
  55. cat.setCategoryId(CATEGORY_ID);
  56. cat.setCategoryName("Fiction");
  57. Category catResponse = client.post(cat, Category.class);
  58. System.out.println("Category Id retreived for format " + format + " is " + catResponse.getCategoryId());
  59. assertEquals(catResponse.getCategoryId(), CATEGORY_ID);
  60. }
  61. private static void testUpdateCategory(final String format) {
  62. System.out.println("testUpdateCategory called with format " + format);
  63. WebClient client = WebClient.create(CATEGORY_URL);
  64. client.path("/categoryservice/category").accept(
  65. format).type(format);
  66. Category cat = new Category();
  67. cat.setCategoryId(CATEGORY_ID);
  68. cat.setCategoryName("Fiction Series");
  69. Response response = client.put(cat);
  70. System.out.println("Status retreived for update category for format " + format + " is " + response.getStatus());
  71. assertEquals("200", String.valueOf(response.getStatus()));
  72. }
  73. private static void testGetCategory(final String format) {
  74. System.out.println("testGetCategory called with format " + format);
  75. WebClient client = WebClient.create(CATEGORY_URL);
  76. Category category = client.path("/categoryservice/category/" + CATEGORY_ID).accept(
  77. format).type(format).get(Category.class);
  78. System.out.println("Category details retreived from service with format " + format);
  79. System.out.println("Category Name " + category.getCategoryName());
  80. System.out.println("Category Id " + category.getCategoryId());
  81. assertEquals(CATEGORY_ID, category.getCategoryId());
  82. }
  83. private static void testAddBooksForCategory(final String format) {
  84. System.out.println("testAddBooksForCategory called with format " + format);
  85. WebClient client = WebClient.create(CATEGORY_URL);
  86. client.path("/categoryservice/category/book").type(format).
  87. accept(format);
  88. Category cat = new Category();
  89. cat.setCategoryId(CATEGORY_ID);
  90. cat.setCategoryName("Fiction Series");
  91. Book book1 = new Book();
  92. book1.setAuthor("Naveen Balani");
  93. book1.setBookId("NB001");
  94. book1.setBookISBNnumber("ISBNB001");
  95. book1.setBookName("Fiction Book1");
  96. Collection<Book> booksList = new ArrayList<Book>();
  97. booksList.add(book1);
  98. cat.setBooks(booksList);
  99. client.post(cat, Category.class);
  100. }
  101. private static void testGetBooksForCategory(final String format) {
  102. System.out.println("testGetBooksForCategory called with format " + format);
  103. WebClient clientBook = WebClient.create(CATEGORY_URL);
  104. Category categoryBooks = clientBook.path(
  105. "/categoryservice/category/" + CATEGORY_ID + "/books").type(format).accept(format).get(Category.class);
  106. System.out.println("Book details retreived from service with format " + format);
  107. assertEquals(String.valueOf(categoryBooks.getBooks().size()), "1");
  108. Iterator<Book> iterator = categoryBooks.getBooks().iterator();
  109. while (iterator.hasNext()) {
  110. Book book = iterator.next();
  111. System.out.println("Book Name " + book.getBookName());
  112. System.out.println("Book ISBN " + book.getBookISBNnumber());
  113. System.out.println("Book ID " + book.getBookId());
  114. System.out.println("Book Author " + book.getAuthor());
  115. }
  116. }
  117. private static void testDeleteCategory(final String format) {
  118. System.out.println("testDeleteCategory called with format " + format);
  119. WebClient client = WebClient.create(CATEGORY_URL);
  120. client.path("/categoryservice/category/" + CATEGORY_ID).type(format).
  121. accept(format);
  122. Response response = client.delete();
  123. System.out.println("Status retreived for delete category for format " + format + " is " + response.getStatus());
  124. assertEquals("200", String.valueOf(response.getStatus()));
  125. }
  126. private static void assertEquals(String expected, String result) {
  127. if (!expected.equalsIgnoreCase(result)) {
  128. throw new RuntimeException("Expecte value " + expected + ", Got value" + result);
  129. }
  130. }
  131. }

http://reymont.iteye.com/blog/1523822

上一篇:基于CXF开发crm服务


下一篇:CXF 开发 REST 服务