|
开发人员:Web服务
创建Web服务,第二部分
作者:Mike Lehmann
同JAX-RPC处理器管理SOAP消息
本系列文章的第一篇讲述了用于包封一个Java类的API,以便可以用JAX-RPC发送和接收SOAP消息--用一个Java类EmpImpl.java包含一个对employee表的JDBC数据库查询,然后发布和使用。现在我们重点介绍一下人们常说的带外(out-of-band)消息处理,它使用一个JAX-RPC的被称之为"处理类"的强大特性,使开发人员能够预处理和后处理(postprocess)SOAP消息,以便可以独立于SOAP消息处理而采取特定的行动。
图 1是处理器的基本概况。客户端发送一个SOAP消息,其中有被添加的信息或者由处理器A和B进行分析的信息。在服务器端,在SOAP消息被该实现(implementation)处理之前,处理器C和D可以根据SOAP消息的内容采取行动。在服务器终端(endpoint)处理完该SOAP消息后,发出响应SOAP消息。跟着,服务器端的处理器E和F被调用,并且,一旦该SOAP消息到达客户端,就执行客户端处理器G和H。
在许多情况下,各处理器执行的顺序是非常重要的。设想在我们所举的例子中,在创建了一个包含员工号码(按该号码查询其工资)的SOAP消息之后,该消息需要被压缩、加密,并且加上数字签名。这就是最经典的一种应用情况,被称之为处理器链,也就是按照特定顺序执行的一系列处理器--一个用于压缩,一个用于加密,一个用于签名。在服务器端,需要按相反的顺序执行这些处理器--一个识别签名,一个处理解密,最后一个进行解压。
让我们对这个员工服务的例子加上一些用户要求,这样你对于JAX-RPC处理器会有更高层次的理解。因为特定的Web服务操作getEmpSalary()是如此的机密,所以需要采用某种形式的登录机制来保证所有对工资的访问审核记录得以保存。同时,鉴于SOAP本身要增加对业务操作的执行时间,因此跟踪用于所有这些额外操作的总时间会是很有用的。最后,每次调用我们的员工服务例子,都需要对用户进行认证。
 |
|
图1:JAX-RPC 处理器
|
配置JAX-RPC终端
为处理这些要求,我们建立了有关员工服务的处理器。为处理器终端设置JAX-RPC处理器通常是在标准JAX-RPC配置文件内加一段声明。首先,为了实现登录,我们使用Java类EmpServiceLoggerHandler.java;其次,为了性能分析,使用Java类EmpServiceTimerHandler.java;最后,为了认证,使用Java类EmpServiceAuthenticateHandler.java。
开发人员必须编写这些类,但是在讨论他们的实现之前,让我们先来重点介绍一下如何配置该服务,以便当收到一个SOAP消息的时候,这些处理器能被调用。正如我在上篇文章中所讲到的那样,每个JAX-RPC
Web服务的一个关键配置文件是webservices.xml。它定义JAX-RPC映射文件、终端接口类和WSDL文件位置等特性。
代码清单
1显示了webservices.xml也用于通过<handler>元素来配置处理器,该元素在<handler-name>元素中包含了该处理器的一个逻辑名称,以及在<handler-class>元素中包含了一个Java类处理器的实际引用。
编写完处理器之后,开发人员就可以手工地将处理器配置加入webservices.xml文件,或者利用一些工具--诸如Oracle
应用服务器10g的Web服务汇编工具,在给定处理器名的条件下,该工具能够自动生成正确配置的webservices.xml文件。最后一个步骤就是把该Web服务、处理器以及JAX-RPC配置文件部署到JAX-RPC运行时环境中。
实施处理器
登录器的(Logger)处理器。每个处理器类提供一个基于基础类GenericHandler的基本框架,在该框架中开发人员可以自由地实现特定的业务逻辑。代码清单
2提供了EmpServiceLoggerHandler的一个片断,该段代码用于查明入站的SOAP请求消息中是否有<getEmpSalaryElement>元素。如果有,那么EmpServiceLoggerHandler便调用logGetSalary()方法,后者则简单报告工资请求已被接收(代码清单
4 是该处理器处理的SOAP消息的一个例子)
两个需要关注的关键方法是handleRequest() 和 handleResponse()。这些方法给开发人员提供了一个处理入站和出站SOAP消息(handlerRequest()和handler
Response())的手段。为保证这些处理器只在它前面的处理器成功执行之后再被执行,处理器给出一个返回值true或者false,false将导致整个处理器链失败。
对于这两种方法,在MessageContext变量ctx的范围内整个SOAP消息都是有效的。在这个例子中,EmpServiceLoggerHandler处理进入到SOAP消息体中询问其内容。允许开发人员操纵导航SOAP消息的Java对象模型,是由SAAJ(the
SOAP API for Attachments for Java--用于Java附件的SOAP API)来定义的,并且该模型在J2EE1.4中也被标准化了。
定时器(Timer)处理器。代码清单
3显示了性能分析处理器EmpServiceTimerHandler,具有与登陆器处理器同样的布局,但是在handleRequest()方法中,它不是查询入站(inbound)SOAP消息,而是修改具有名称值对的MessageContext变量ctx
--该名称值对被标记为"timeIn",它包含了设置为当前时间的java.util.Data变量timeIn。
通过其余的处理器链,JAX-RPC处理器基础设施自动携带"timeIn"名称值对,并且通过出站SOAP消息将其通过MessageContext变量返回给handleResponse()方法。该timeIn对然后被抽取出来,并于当前时间进行比较,以得出特定的SOAP交互所花费的大致时间。对于确定在一个长的处理器链中哪个处理器的执行时间最长来说,这可能是一个很简单实用的技术。
验证处理器。在前面两个处理器中,并没有必要实际修改SOAP消息。然而,对于验证处理器来说,SOAP报头被用来携带用户证书。被发送的目标SOAP消息如代码清单
4所示,在头元素<authheader1>中有<id>和<password>元素。
报头是SOAP消息的一部分,通常携带着诸如安全证书、数字签名、交易标识、相关标识以及其他被放在SOAP体数据之外的信息。SOAP报头是许多高级规范(诸如WS-Security,WS-Composite应用程序框架和WS-Reliability)的基础。
为构建该报头,需要编写带有handleRequest()方法的客户端处理器EmpClientAuthenticateHandler
.java,如代码清单
5所示。请注意:这里有一个非常重要的差异,就是SOAP消息在被发送到服务器之前,要在客户端经过处理。
这样做的目的就是为了导航出站SOAP消息,查找SOAP信封,然后手工添加带两个子的元素<id>
和 <password>的authheader1。在本例中,硬编码代码要比从某些客户端证书存储处搜集来的代码好。
为了在服务器端处理证书,处理器EmpServiceAuthenticateHandler
的handleRequest()方法要进行相反的操作。在入站SOAP消息报头中查找<authheader1>元素,然后根据后台的验证服务来验证<id>
和<password>元素。
更新JAX-RPC Stub
最后一步就是看看员工服务的客户端调用如何变化。在上期的文章中该调用没有变化,但是生成客户端实现类EmpService_
Impl.java的构造器发生了变化,如代码清单
6所示。
特别是,可以看见EmpClientAuthenticateHandler类已用handlerInfo变量进行了注册,然后,被设置为用于JAX-RPC客户端的处理器链。该操作的结果就是,对于每一个发送到该员工服务的SOAP消息而言,EmpClientAuthenticateHandler都被调用一遍,将验证证书添加到出站消息中。
请注意:与服务器端的处理器不同--它们仅仅通过在webservices.xml文件以声明的方式来进行注册,而客户端的处理器可以以程序的方式进行注册,如上面所示,或者可以通过一个单独的应用程序客户端Web服务配置文件以声明的方式进行注册。
与服务器端上的webservices.xml文件一样,只要给定合适的客户端处理器的信息,Oracle应用服务器10g
Web服务汇编程序就可以自动生成相应的客户端stub代码。
总结
我们只使用了三种不同的JAX-RPC处理器来向JAX-RPC Web服务中添加功能,而无需修改底层Web服务的实现。此外,处理器本身几乎是通用的,以致可以独立成模块,只需经过细微的改动,就可以被插入其他的JAX-RPC
Web服务中。
可以基于JAX-RPC处理器,编写更广泛的实用Web服务管理框架,用于注册、验证以及其他任务。那是不是正确的方法呢?敬请关注:Web服务管理是即将到来的一个专栏的主题。
Mike Lehmann
(mike.lehmann@oracle.com)是OracleAS
Containers for J2EE(OC4J)的主要产品经理。 |