转自:http://www.ibm.com/developerworks/cn/webservices/1211_zhusy_rpc/
简介:在新技术、新概念甚至新思维层见叠出的 IT 行业,XML-RPC 绝对不是最新的热门技术,但它自从诞生时起,一直在 IT 行业占有一席之地。XML-RPC 具有简单、高效且易于实现等优点,它一直是中小型应用实现分布式计算的最佳选择之一。本文将会首先介绍一下 XML-RPC 协议规范,然后介绍如何实现 Apache XML-RPC 开发客户端及服务端 Java 代码。最后对 Apache XML-RPC 的高级特性进行了介绍。
在新技术、新概念甚至新思维层见叠出的 IT 行业,XML-RPC 绝对不是最新的热门技术,但它自诞生之日起,一直在 IT 行业占有一席之地。XML-RPC 具有简单且易于实现,可以高效跨越不同的软硬件平台上应用系统等优点。相对于庞大、复杂的分布式计算机制,它一直是中小型应用的最佳选择之一。比如,流行于软件研发团队的任务计划管理系统 JIRA、开源的测试用例管理工具 TestLink 等都提供了开放的 XML-RPC 接口,供用户定制开发时进行调用。
XML-RPC(XML-based Remote Procedure Call,基于 XML 的远程过程调用)是一种工作在互联网上的远程过程调用协议。一个 XML-RPC 请求消息就是一个 HTTP-POST 请求消息,其请求消息主体基于 XML 格式。客户端发送 XML-RPC 请求消息到服务端,调用服务端的远程方法并在服务端上运行远程方法。远程方法执行完毕后,返回响应消息给客户端,其响应消息主体同样基于 XML 格式。远程方法的参数支持数字,字符串、日期等;也支持列表数组和其他复杂结构类型。
主流的开发语言都提供了 XML-RPC 协议的客户端程序和服务端程序的实现,这些语言包括 Java、Ruby、Python、C/C++ 、Perl 和 PHP 等。本文选择以 XML-RPC 的 Java 实现 Apache XML-RPC 为例,来研究 XML-RPC 协议规范。Apache XML-RPC 完全兼容 XML-RPC 协议规范,同时也提供了一些扩展特性,使其还具有下述能力:
- 支持 Java 的全部基本数据类型,如 long、byte、short,以及 double
- 支持 Calendar 对象
- 支持支持 DOM 节点、JAXB 对象实例能通过 XML-RPC 进行传输。事实上,任何实现 java.io.Serializable 接口的对象都可以被传输
- 客户端和服务端都可以通过配置在流模式(stream mode)下进行操作,这样比基于大字节数组的默认模式节省资源
下面就以实例来学习 XML-RPC 的请求和响应消息的格式。本文以当前工作使用的 Window 机器为客户端,IP 为 192.168.1.105。服务端为 Ubuntu 机器,IP 为 192.168.1.126。
步骤 1. 下载本文附带的源代码文件,内含三个 Eclipse Java 项目,请把他们导入到 Eclipse 工作区间:
- XML-RPC-Client XML-RPC 客户端实现的例子
- XML-RPC-EmbeddedWebServer 嵌入式服务端端实现的例子,可以直接运行
- XML-RPC-Server Servlet 服务端端实现的例子,需要部署在 Servlet 容器,比如 Tomcat、Jetty 等
步骤 2. 右键选择 XML-RPC-Server 项目,导出为 WAR 文件,命名为 xmlrpc.war。把它部署在服务端的 Servlet 容器上,本文以部署在 Ubuntu 12.04 上的 Tomcat 7.0 为例,复制 xmlrpc.war 到 Tomcat 安装根目录下的 webapps 文件夹下。启动 Tomcat 服务器,使用浏览器访问http://192.168.1.126:8080/xmlrpc/xmlrpc,如能正常显示,则表明在服务端上部署成功。
步骤 3,下载安装 SmartSniff(下载地址见参考资源部分)。SmartSniff 用于捕获客户端与服务端的 XML-RPC 消息交互数据包。运行该软件,在菜单 Options-Capture Filter 与 Display Filter 里设置捕获过滤、展示过滤:include:remote:tcpudp: 192.168.1.126:8080,这样只显示我们感兴趣的协议数据。点击 Start Capture 按钮,启动捕获监听。注意:SmartSniff 只能运行在 Window 机器上,并且只能捕获不同的机器之间的 TCP/IP 网络数据交互。如果 XML-RPC 客户端与服务端运行在相同的机器上,无法使用该工具捕获协议交互数据。
步骤 4,运行客户端的单元测试类,研究 XML-RPC 消息格式。在 Eclipse 项目 XML-RPC-Client 中,打开单元测试类 junit.TestClientServletWebServer.java。选择testXMLRPC()方法
,以 JUnit Test 方式运行。可以发现 JUnit 单元测试可以运行成功,SmartSniff 捕获的协议见图 1。使用相同的方法,可以运行更多的测试类,研究更多的 XML-RPC 消息的格式。
图 1. 捕获 XML-RPC 协议消息
清单 1 是捕获的请求消息,可以看出和普通的 HTTP 请求一样,包含如下消息头:POST、Content-Type、User-Agent、Authorization、Host 与 Content-Length 等。XML 格式的消息体包含一个 methodCall 元素来指定远程方法的信息。MethodName 指定远程方法名称,params/param/value 等标签元素指定远程方法需要的参数列表。xmlns:ex 名称空间是 Apache XML-RPC 对 XML-RPC 协议的扩展,后续会详细介绍。
清单 1. 请求消息例子
POST /xmlrpc/xmlrpc HTTP/1.1 Content-Type: text/xml User-Agent: Apache XML RPC 3.1.3 (Jakarta Commons httpclient Transport) Authorization: Basic Zm9vOmJhcg== Host: 192.168.1.126:8080 Content-Length: 260 <?xml version="1.0" encoding="UTF-8"?> <methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions"> <methodName>Calculator.add</methodName> <params> <param> <value> <i4>2</i4> </value> </param> <param> <value> <i4>3</i4> </value> </param> </params> </methodCall> |
清单 2 是捕获的响应消息。消息体同样是 XML 格式的,它包含一个methodResponse 元素来指定远程方法的响应内容。methodResponse 元素下面可以包含一个 params 元素,用于包装返回的数据内容。也可以包含 <fault> 元素,用于返回遇到的异常消息。XML-RPC 响应消息必须有返回值,因此 <fault> 和 <params> 必须出现一个且只能出现一个。
清单 2. 响应消息例子
在本节,我们研究了 XML-RPC 协议规范及其消息内容格式。下面继续介绍的是,如何使用 Apache XML-RPC 开发客户端和服务端 Java 代码。
在 Eclipse 项目 XML-RPC-Client 中,打开单元测试类 junit.TestClientServletWebServer.java。我们研究一下客户端的代码实现,见清单 3:
清单 3. 客户端代码实现
上面的代码首先实例化一个客户端配置对象 XmlRpcClientConfigImpl config,为该实例指定服务端 URL 属性。接着创建 XmlRpcClient 对象,为其指定配置对象,然后为其设置一个 Sun HTTP 传输工厂对象,这个属于默认的传输工厂对象,可以忽略该行语句。最后创建参数数组,并调用服务端的方法 Calculator.add(后文会介绍如何查询服务端可以提供的方法列表)。
我们可以选择不同的传输工厂对象,比如可以不使用默认的 Sun HTTP 传输工厂对象,而是使用基于 Jakarta Commons HttpClient 的传输工厂。使用不同的传输工厂, XML-RPC 请求消息头 User-Agent 部分可以看出差异。使用 SmartSniff 捕获到的请求消息头,如下图所示。
图 2. 使用不同传输工厂的 XML-RPC 请求消息
XmlRpcClient 是一个无状态的线程安全的对象,实例化 client 对象时,需要设置下面的对象:
表 1. 配置 XmlRpcClient
名称 | 描述 |
---|---|
ClientConfig | 该对象是 XmlRpcClientConfig 的实例,包括如下原子属性,比如服务端 URL,认证信息,编码字符集等 |
TransportFactory | 该工厂类用于创建负责使用客户端配置与服务端通讯的对象。下文会介绍常用的传输工厂类。 |
XmlWriterFactory | XmlWriter 用于创建 XML |
传输工厂类 TransportFactory 决定客户端与服务端通讯的方式,Apache XML-RPC 支持如下几种工程类:
表 2. 传输工厂类
工厂类名 | XmlRpcSunHttpTransportFactory | 默认的工程类,使用 java.net.HttpURLConnection 与服务端连接 | ||
---|---|---|---|---|
XmlRpcCommonsTransportFactory | 该工厂类使用 Jakarta Commons HttpClient 与 HTTP 服务端通讯 . 该工厂允许直接访问结果文档,可以降低内存资源的使用。 | |||
XmlRpcLiteHttpTransportFactory | 该工厂类基于内置的 HTTP 客户端,速度很快,但不支持 HTTP/1.1,不能使用 keepalive 连接。 |
|||
XmlRpcLocalTransportFactory | 该工厂类拥有内置的 XML-RPC 服务器,可以直接使用 Java 调用,主要用于调试与开发环境。 |
根据传输工厂的不同,客户端配置分为如下两种类型:
- org.apache.xmlrpc.client.XmlRpcHttpClientConfig
用于 HTTP 传输工厂,即上文提到的 XmlRpcSunHttpTransportFactory、XmlRpcCommonsTransportFactory 和 XmlRpcLiteHttpTransportFactory。HTTP 传输工厂对应的客户端配置实例支持以下属性设置 basicUserName、basicPassword、basicEncoding、contentLengthOptional、enabledForExceptions、enabledForExtensions、Encoding、gzipCompressing 和 gzipRequesting。关于这些属性的详细描述,请参考 Apache XMl-RPC 官方站点。
- org.apache.xmlrpc.client.XmlRpcLocalClientConfig
用于本地传输工厂,即上文提到的 XmlRpcLocalTransportFactory。本地传输工厂对应的客户端配置实例支持配置属性 xmlRpcServer。作为内置的 XML-RPC 客户端,模拟来自客户端的请求,用于调试与开发。
org.apache.xmlrpc.client.XmlRpcClientConfigImpl 实现了上述两个接口,为方便计,不管使用什么传输工厂类,客户端配置都可以使用实现类 XmlRpcClientConfigImpl。
相对于 XmlRpcClient,服务端的 XmlRpcServer 对象用于接收并执行来自客户端的 XML-RPC 调用,该对象可以嵌入 Servlet 容器(Tomcat,Jetty 等),或其他的 HTTP 服务器。在使用本地传输工厂类的情况下,XML-RPC 被集成到客户端应用内。和 XmlRpcClient 类似,XmlRpcServer 也需要制定一个服务端配置对象 XmlRpcServerConfigImpl。该配置对象支持的属性如下:
表 3. 服务端配置属性
enabledForExceptions | 在启用该属性时,如果服务端捕获异常,服务端会把异常转换成字节数组, 并返回客户端。 |
---|---|
enabledForExtensions | 指定是否启用 Apache 对 XML-RPC 的扩展。 |
下面,我们就来研究一下如果使用 XML-RPC Servlet 来开发服务端的代码。
首先,创建一个业务逻辑类,供客户端调用。见清单 4。需要注意:能被客户端调用的方法必须是 public 的,返回值不是 void 的、实例方法。像构造方法、存取方法的 setter 方法和静态方法都不能被远程调用。任何抽象类、接口类,不能被实例化,它们的方法也不能被远程调用。
清单 4. Calculator 业务逻辑类
接着,在源文件目录 org/apache/xmlrpc/webserver/ 下创建属性文件 XmlRpcServlet.properties。XmlRpcServlet 默认在上述包路径下寻找属性文件。在文件中添加如下条目(清单 5)。左侧为一标识符,右侧为业务逻辑类的全限定类名。
清单 5. 句柄属性文件
然后,在 Web 应用的部署描述文件 web.xml 添加如下内容(清单 6):
清单 6. 部署描述符配置 XmlRpcServlet
现在已经完成了服务端的代码开发,可以把它打成 war 包,部署在 servlet 容器内。启动服务端后,就可以运行客户端代码对服务端上提供的远程方法进行调用。关于如何运行,请详细参考本文附带的源代码项目。
另外,为了给服务端上提供的 XML-RPC 接口提供安全保障,可以为服务端启用认证机制。Apache XML-RPC 使用 XmlRpcHandler 处理认证,它以 XmlRpcRequest 作为参数。XmlRpcRequest 的 getConfig() 方法可以返回 XmlRpcRequestConfig 对象。使用 XmlRpcRequestConfig 的方法 getBasicUserName() 和 getBasicPassword() 可以获得客户端的认证信息。获取客户端的认证信息后,就可以采取适当的逻辑去验证。
首先,实现 AuthenticationHandler 接口,编写处理认证信息的逻辑代码,见清单 7。
清单 7. 认证处理器程序
在自己定义的 XmlRpcServlet 类中使用 AuthenticationHandler,见清单 8。并在应用的部署描述文件中,修改 servlet-class,使用定制的 Servlet 类 MyServlet。
清单 8. 自定义的 XmlRpcServlet
重启 Servlet 容器,刚才的单元测试类必须添加如下用户认证信息(清单 9)才能执行成功。
清单 9. XML-RPC 客户端提供认证信息
至此,我们完成了使用 Apache XML-RPC 开发客户端和服务端代码,部署到了 Servlet 容器 Tomcat 上,并掌握了如何运行客户端代码调用服务端的远程方法。下面对 Apache XML-RPC 的高级特性进行介绍。除了部署在 Servlet 容器里,Apache XML-RPC 还提供了嵌入式 WebServer。XML-RPC 动态代理技术带来了良好的编程体验。通过启用扩展,Apache XML-RPC 支持了更多的 Java 数据类型。还对内省机制进行了简单介绍。
嵌入式 WebServer,更加便携,适用于为非 Web 应用提供远程方法。WebServer 实现代码见如下清单 10。
清单 10. 嵌入式 WebServer 代码
Apache XML-RPC 推荐使用 WebServer 的子类 ServletWebServer。ServletWebServer 支持 Servlet API,方便日后迁移到 Servlet 容器上。使用该子类需要把 servlet-api.jar 加入到构建路径中,该 jar 文件可以在 Servlet 容器中得到。本文演示的 ServletServer.java 代码片段如清单 11 所示。
清单 11. 嵌入式 ServletServer 实现
可以在 Eclipse 选中包 dw.webserver 中 Server.java 或 ServletServer.java,右键弹出上下文菜单,选择 Export 导出菜单,在接下来的向导中选择 Runnable Jar file,并将依赖的 Jar 文件导出到单独的文件夹中,导出的可运行的 Jar 命名为 XMLRPC.jar。通过下面的命令,启动嵌入式 WebServer:java -cp XMLRPC_lib -jar XMLRPC.jar。
在客户端调用嵌入式服务端的远程方法和调用部署在 Servlet 容器上的服务端的远程方法一样,只是前者为 config 对象指定 ServerURL 的时候只需要指定 IP 和端口即可,服务的名称不是必须的,如 config.setServerURL(new RL("http://127.0.0.1:7071"))。
在客户端编写代码调用服务端远程方法时,需要为 client.execute() 提供两个参数。第一个参数为字符串 String 类型,即远程方法的名称;第二个参数是数组类型,即远程方法的参数列表。这种编程体验不够友好,很容易因为记忆、编写错误而导致问题。动态代理技术可以提供一种舒服的客户端编程方式。工作方式如下:把服务端的远程方法都抽象提炼到相应的接口文件中,接口文件在客户端、服务端都存在。在客户端以 XmlRpcClient 为参数创建 ClientFactory 的一个实例。ClientFactory 以抽象接口为参数,返回服务端对应的实现类的实例,然后使用该实例调用服务端的远程方法。
首先,编写接口文件 Adder.java,该接口在服务端和客户端都存在。接着,在服务端编写实现类 AdderImpl.java,实现上述接口类。见清单 12。
清单 12. 动态代理接口及其实现类
然后,修改句柄属性配置文件,注意左侧必须为接口类的全限定名称(清单 13)。
清单 13. 属性配置文件
最后,重启服务端,使修改生效。使用动态代理技术编写客户端调用服务端的方法(清单 14)。可以看出使用这种方式,客户端代码可读性增强,编写更加容易。
清单 14. 动态代理客户端测试类
XML-RPC 请求消息体和响应消息体中 Value 元素的下级子元素用于指定数据类型。数据类型元素包含实际的文本数据。XML-RPC 协议规范支持八种数据类型,Apache XML-RPC 都有相应的 Java 数据类型与其对应。如果 XML-RPC 的客户端和服务端都基于 Apache XML-RPC 实现,并且启用了扩展(enabledForExtensions 属性设置为 true),还可以额外支持更多的 Java 数据类型。通过启用扩展,XML-RPC 支持 Java 语言的全部基本数据类型,日历对象,序列化对象等。
下面将要演示 Apache XML-RPC 如何支持扩展的序列化对象。首先,创建一个序列化 Java Bean,其中包含一个可供远程调用的方法 setAndGetBean(),其参数与返回值均为一个可序列化的对象 SerializableBean。见清单 15。
清单 15. Java 序列化类
接着,在句柄属性配置文件添加一行 serializable=dw.xmlrpc.SerializableBean,并重启服务端(Servlet 容器或者 WebServer 进程)。
清单 16. 序列化测试类
运行客户端的时候,使用 SmartSniff 进行监控协议数据。观察捕获的协议数据,可以看到扩展的标签使用了 ex 前缀,其名称空间 URL 为http://ws.apache.org/xmlrpc/namespaces/extensions。请求消息、响应消息数据见清单 17。
清单 17. 请求消息数据与响应消息数据
正如上文所示,客户端通过调用像 Calculator.add 这样的远程方法来实现分布式计算。但是客户端如何知道服务端可以提供什么样的方法?客户端怎么样调用远程方法,方法的签名是什么?如何获取关于远程方法的帮助信息? XML-RPC 内省机制(introspection)可以回答这些问题,客户端只需要调用服务端的"system.listMethods","system.methodSignature" 和 "system.methodHelp"就能得到答案。Apache XMl-RPC 支持这种内省特性,只需要在服务端进行简单的配置即可。对于部署在 Servlet 容器上的应用,需要继承 XmlRpcServlet,获取 PropertyHandlerMapping 对象,并将其添加到系统句柄对象 XmlRpcSystemImpl,详见代码清单 18。
清单 18. 定制 XmlRpcServlet
如果使用的是嵌入式 WebServer,修改方法与之相似。完整代码见附带的 Eclipse 项目 XML-RPC-EmbeddedWebServer\src\ dw.webserver,代码片段见清单 19。为了演示启用内省机制前后服务端提供的远程方法的差异,笔者特意在代码中调用了 PropertyHandlerMapping 的 getListMethods() 方法,并在服务端控制台把远程方法打印出来。可以通过此种方式,对启用内省配置前后服务端提供的方法进行比较。
清单 19. 嵌入式 WebServer 内省配置
下面的单元测试类代码清单 20 演示如何在客户端调用内省方法 system.listMethods,运行该单元测试类可以打印服务端提供的远程方法列表。更多的例子在附带的源代码项目中可查。
清单 20. 客户端测试内省配置
通过上面的介绍,我们了解了 XML-RPC 协议规范。通过使用网络数据包捕获工具 SmartSniff, 捕获 XML-RPC 的请求与响应消息并进行了细致的研究。然后以 XML-RPC 的 Apache 实现为例,演示了如何开发 XML-RPC 客户端及服务端 Java 代码。最后对 Apache XML-RPC 的高级特性进行了介绍。相信读者们,如果想实现简单的分布式计算,现在应该很清楚如何操作。对于使用 JIRA、TestLink 等提供 XML-RPC 接口的开源工具的读者,如果想调用其开放 XML-RPC 接口,也是很容易的事情。
学习
- 访问XML-RPC 官方站点,了解协议规范的详细内容。
- 访问Apache XML-RPC 官方站点,下载 Apache XML-RPC 实现的软件包,阅读使用文档等。
- 查看文章“Java 编程中的 XML-RPC”,了解 XML-RPC 的另一种 Java 实现 Marquee XML-RPC。
- 访问SmartSniff 站点,了解更多关于 TCP/IP 网络数据包捕获工具 SmartSniff 的知识。
- 访问Http Client 站点和Codec 站点,下载 Apache XML-RPC 依赖的第三方软件包。
- Web services 官方网站
- 在developerWorks 的 SOA 专区,获取提高您技能所需的资源。
- IBM developerWorks 中国 WebSphere 专区:为使用 WebSphere 产品的开发人员准备的技术信息和资料。这里提供产品下载、how-to 信息、支持资源以及免费技术库,包含 2000 多份技术文章、教程、最佳实践、IBM Redbook 和在线产品手册。
获得产品和技术
- 最受欢迎的 WebSphere 试用软件下载:下载关键 WebSphere 产品的免费试用版。
- IBM developerWorks 软件下载资源中心:IBM deveperWorks 最新的软件下载。
- IBM developerWorks 工具包:下载关键 WebSphere 最新的产品工具包。
讨论
- 加入developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。
- 加入IBM 软件下载与技术交流群组,参与在线交流。