2003 年 1 月 01 日
在
这个系列的第一篇文章里, Joshy Joseph 讨论了 JAX-RPC 标准的一个重要方面:它的类型映射系统。既然知道了 JAX-RPC
怎样把 XML 类型映射成 Java
类型,您就可以认真地研究这个规范,包括它的异常处理机制和潜在的运行时服务了。读完本文后,您就可以开始构建基于 JAVA 的互操作 Web
服务了。
基于
XML 的远程过程调用的 JAVA API(Java APIs for XML-based Remote Procedure
Call(JAX-RPC))在 Java Community Process 已经作为 JSR 101 进入了最后建议阶段。XML Web
服务提供商已经开始将这个包作为核心 API 在 JAVA 平台上构建互操作 Web
服务。在本系列中,我会引导让您一步一步地了解这个标准提供的主要功能,并使用样本代码作为全程指导。
在
本系列的第一部分中,我描述了 WSDL/XML 类型和 JAVA 类型之间的互相映射。我解释了 JAX-RPC 标准怎样定义该功能,以及关于如何设计互操作类型系统的一些要点。在某种程度上,这个类型映射标准有助于使 Web 服务运行时系统实现消息级别的互操作性。
在这本系列的第二部分中,您将学到如何用 JAX-RPC 标准的客户机和服务器端接口定义和消息处理模型实现下一级别的 Web 服务互操作性。
您可以回忆一下,JAX-RPC 规范包括下列主题:
- 类型映射系统
- 服务端点
- 异常处理
- 服务端点上下文
- 消息处理程序
- 服务客户机和服务上下文
- 带附件的 SOAP
- 运行时服务
- JAX-RPC 客户机调用模型
正
如我在本系列的第 1 部分谈到的那样,我讨论了 JAX-RPC
的类型映射系统的重要方面。现在我将探究一下规范中剩余部分的重点。正如我在第一部分所做的那样,我将用一段代码(来自为 ACME 售书商所做的样本
Web 服务)来描述这些要点。要想了解关于本示例的更多信息,请参阅第 1 部分,要了解关于本示例完整的 WSDL 清单,请参阅 附录。(请参阅
参考资料以了解更多信息)
服务端点
JAX-RPC
服务端点指的是真正的服务实现所依赖的 Web 服务端点。请参阅
图 1,那里有一个服务端点实现(具体的)类和一个服务端点接口的定义。
图 1. 服务实现接口层次结构
服务端点接口
Web 服务端点类是通过使用 WSDL2JAVA(一个由 Apache Axis 提供的 WSDL 到 Java
的映射工具)由服务端点界面派生出来的,而服务端点界面是根据 WSDL 定义创建的。正如 JAX-RPC 规范的 5.2
节中预先定义好的一样,这些服务端点必须和服务端点接口定义一致。这个定义的一些要求如下:
- 所有服务端点必须继承
java.rmi.Remote 接口。
- 所有由这个接口实现的方法必须抛出一个
java.rmi.RemoteException 。可能有特定于应用程序的异常作为这个标准异常的补充。
- 这个方法参数必须是 JAX-RPC 支持的 Java 类型。用这种方法,这个规范为 Java 平台和它对应的 XML 类型保证了一个适当的序列化/反序列化机制。
- 通常,Java接口可以通过使用
public static final 声明维护常量数据。然而,JAX-RPC 规范阻碍您这么做,这个规范表明 WSDL 1.1 在
wsdl.porttype 中对常量的表示不标准。
- 您可以使用
Holder 和
SOAPElement 类作为接口方法的自变量。(请参阅这个系列的第 1 部分,您可得到关于
Holder 和
SOAPElement 类的更多信息。)
清单 1包含一个服务端点接口的示例。
清单 1. 服务端点接口
Public interface AuthorSearchService implements java.rmi.Remote{ Public Books[] seachForAuthor(String authorName) throws java.rmi.RemoteException, com.acme.InvalidAuthorName; }
|
注意
清单 1 中的样本端点接口处理运行时
RemoteException 和应用程序级的
InvalidAuthorName 。在我描述 JAX-RPC 异常处理时,我将更深入地看一下语义上的意思以及应用程序异常之间的关系(在 WSDL 的上下文环境里)。
也要注意,基于 SOAP 的分布式系统和基于其他协议(如 RMI)的分布式系统的主要的实现上的区别是,前者不支持通过引用传递和返回对象。因此您应该避免去创建一个使用远程引用作为参数或返回值的服务端点。另外 JAVA 值的类型和数组不应该存有远程引用。
一些服务提供商(如 WASP)有支持远程对象引用的实现。可是为这些实现写的代码可能在运行时系统之间缺少可移植性。例如,可能不是所有的 JAX-RPC 系统都支持
清单 2 中的代码,因为
BookSearchBroker 对象是通过引用返回的。
清单 2. 潜在的无可移植性的代码
Public interface BookSearchBroker extends java.rmi.Remote{ Public Books[] searchForAuthor(String tickerSymbol) throws java.rmi.RemoteException; } public interface StockQuote extends java.rmi.Remote{ public BookSearchBroker getBooksSearchBroker() throws java.rmi.RemoteException; }
|
服务端点实现类型
服务端点实现类型是由服务端点接口派生出来的。作为对这个接口的补充,这个服务端点实现类可能实现
ServiceLifecycle 接口来管理服务的生命周期(请参阅
图 1)。这个接口的声明在
清单 3中给出。
清单 3. 服务生命周期声明
Public interface ServiceLifecycle{ void init (Object context) throws ServiceException; void destroy(); }
|
这个接口使得 JAX-RPC 运行时系统能管理 Web 服务示例的生命周期。(如果这个服务实现是一个 servlet,那么这个运行时系统就是一个 servlet 容器。)这个运行时系统负责装载服务端点类以及服务端点类的实例化。实例化之后,它将调用
ServiceLifecycle.init() 方法来初始化服务示例。
ServiceLifecycle.init() 认为运行时上下文
ServiceContext 是一个参数。我将在后面的题名为
服务上下文的部分里更加的在细节上讨论运行时上下文这个问题。
您开发一个服务端点类时,您必须确保这个端点不维护任何属于客户机的状态。这个运行时系统可以把多个对该服务端点接口的客户机调用分派到对于这个单一实例。并且注意这些服务端点示例可能被运行时程序合用以提高性能。
在运行时系统把端点从客户服务里除去时,这个运行时系统必须调用
ServiceLifecycle.destroy() 方法。这有助于服务类实例放弃它正在使用的资源。
异常处理
您可以看到 JAX-RPC 规范试图在应用程序级和运行系统级处理 Web 服务运行时的异常,这是基于服务端点接口的标准设计方法以及它对
wsdl.fault 元素的映射。
这个特定于服务的异常是在
wsdl.fault 元素中声明的,这些异常类型是由
java.lang.Exception 类派生的。
清单 4 中的
wsdl.operation 声明包括特定的
wsdl.fault 元素。
清单 4. wsdl.operation 声明
<message name="AuthorNotFoundException"> <part name="Author" type="xsd:string" /> </message> <portType name ="BookSearch"> <operation name="getBooksByAuthor" > <input message="tns:getAuthorName"> <output message="tns:getBookList"> <fault name=" AuthorNotFoundException" message=" tns: AuthorNotFoundException"> </operation> </portType>
|
在
清单 5中,您可以看到 JAX-RPC 规范怎样创建它的服务端点来处理特定于服务的 JAVA 异常。
清单 5. 处理特定于服务的 JAVA 异常
Public interface BookSearch implements java.rmi.Remote{ Public Books[] getBooksByAuthor(String authorName) throws java.rmi.RemoteException, com.acme.AuthorNotFoundException; }
|
清单 6包括作为结果创建的 JAVA 异常类
清单 6. Java 异常类
public class AuthorNotFoundException extends java.lang.Exception{ ........... public AuthorNotFoundException(String Author ){ ..... } public getAuthor(){...} }
|
服务端点上下文
JAX-PPC 允许运行时系统灵活地管理上下文信息(注意,
ServiceLifecycle.init() 方法希望得到
Object 类型的上下文)。每一个运行时环境可以维护它自己独特的上下文信息。例如,基于 servlet 的运行时系统维护一个
ServletEndpointContext 对象。EJB 2.1 规范定义了 EJB
SessionContext 。
作
为一个例子,我们来探究一个基于 servlet 的运行时系统,然后看一下它是怎样管理上下文信息的。这个 servlet
端点上下文包括的信息有用户主体、消息上下文、基于 http 的用户会话信息以及 servlet
上下文。这个规范要求服务运行时在所有对服务端点实例的远程方法调用之间维护全部这些信息。您可以从下面的清单 7
中的服务上下文接口看到,这是一个有价值的信息,一个服务可以通过多种途径利用它:
- HTTP 会话信息帮助客户机维护和服务器的 HTTP 会话。这是可以任选的功能部件。
- 用户主体(如果运行时系统已经验证了这个用户)帮助服务开发者验证用户来得到特定的运行时操作。
- 另外一个很好的由这个接口提供的功能是它对 SOAP 信息上下文传播的支持。这帮助服务实现者从请求处理程序链中获得 SOAP 信息上下文,然后处理该上下文并将其与响应处理程序链关联。
简单的说,这个接口提供了关于调用程序、消息、当前环境的细节的动态运行时信息。
清单 7告诉您怎样扩展您的服务实现类来支持生命周期管理以及怎样使用服务上下文。
清单 7. 服务上下文接口
Public interface BookSearchServiceImpl implements java.rmi.Remote, javax.xml.rpc.server. ServiceLifecycle { public void init(Object context) throws ServiceException{ ServletEndpointConext sC = (ServletEndpointConext)context; Java.security.Principal userPrinciple = sC. getUserPrincipal(); HttpSession session = null; Try{ session = sC.getHttpSession(); }catch(JAXRPCException e){ // Not an HTTP based //endpoint } MessageContext ctx = sC.getMessageContext(); } public void destroy(){ } public Books[] searchForBooks(String authorName) throws java.rmi.RemoteException, com.acme.InvalidAuthor{ return null; } }
|
消息处理程序
现在我们来考虑一下 JAX-RPC 规范的最强大的功能,
消息处理程序。
消息处理程序向 Web
服务端点(客户机和服务器)提供了附加的消息处理功能,作为对基本服务实现逻辑的扩展。处理程序可以处理加密和解密、日志记录和审计等。当前的
JAX-RPC 运行时系统仅仅定义了 SOAP 消息处理程序,但是它可以很灵活的定义其他处理程序,而且不需要任何消息处理模型。
图 2. 服务和处理程序调用模型
JAX-RPC 处理程序 API 定义了三个基本的方法,还有两个生命周期方法,表示在清单 8 中。
清单 8. 处理程序方法
public class Handler{ handleRequest(MessageContext context) handleResponse(MessageContext context) handleFaults(MessageContext context) init(HandlerInfo info); destroy(); ........... }
|
一个处理程序应该被作为无状态的实例实现。通过提供初始化的接口(
Handler.init (HandlerInfo info) ),运行时系统可以把所要求的上下文信息传递给处理程序。这将帮助处理程序获得特定于容器的增值功能的访问权,包括认证机制,事务处理,日志记录的框架等。
 |
处理程序实现和 JSR 109
在
为 J2EE 容器开发 JAX-RPC 处理程序之前,您必须向 J2EE 容器供应商咨询。根据实现企业 Web 服务(Implementing
Enterprise Web
Services)规范(JSR109),处理程序在应用程序执行上下文中运行,因此只能支持有限的功能。为了更好地理解这个因容器产生的限制,请考虑一
下有关安全性的情况:您可能不会写 JAX-RPC 的处理程序来支持 WS-Security
在应用程序层进行认证和授权。这个决定应该在应用程序执行之前作出。请参阅您的 J2EE 容器供应商文档中关于 Web
服务的内容,以获得更多信息。要了解更多关于 JSR109 的信息,请参阅下面的 参考资料。
|
|
您可以随意地从 API 提供的缺省处理程序派生出新的处理程序,也可以从 SOAP 消息处理程序(
SOAPMessageContext
作为一个参数)或者普通处理程序来派生出新的处理程序。处理程序可以修改传递到自身的消息。因为出于安全性的原因,这些处理程序是很灵活的,所以大多数现
在可用的框架将在运行时系统的控制下管理它们。例如,在一个 J2EE 的容器里,处理程序可能是 J2EE
容器的一部分。因此,您应该去查找您的应用程序服务器(运行时)供应商文档来找到更多关于和您正在使用的产品一起被提供的内置处理程序信息。这些内置处理
程序可能是为 WS-Security、WS-Transaction 或日志记录等原因设计的。尽管您能编写自己的 JAX-RPC
处理程序,应用程序服务器提供商也能够根据配置和安全策略决定是否允许一个新的处理程序存在。
处理程序链
处理程序链表示一个有序的处理程序清单。这个分组有助于您定义和该处理程序调用模型相关联的策略。这些策略的示例包括调用顺序、调用风格(例如一个单向的调用只调用
handleRequest() ;它不会调用
handleResponse() )等。另外一个您可以在处理程序链上设置的的策略:处理程序链可以根据 SOAP 头最外层的元素的
qname 调用处理程序。您可以通过
Handler.init() 方法传递一个
HandlerInfo 对象来将这个关联配置到处理程序。只有当前正在处理的处理程序返回
true 时,处理程序链才继续处理这个处理程序。
您可以通过指定动作者(
角色;请参阅 SOAP 1.1 规范以了解更多细节;您可以在下面的
参考资料部分找到一个相关的链接)的 URI 来将处理程序链和 SOAP 动作者相关联。缺省情况下,处理程序链总是一直与特定的 SOAP 动作者
next 相关联。正如 WSDL 端口限定名所表明的,处理程序链是在每服务端点的基础上注册的。
清单 9显示了一个能访问 SOAP 消息头的样本实现。
清单 9. 样本处理程序
Public class AcmeSOAPHeaderHandler extends GenericHandler{ Public Boolean handleRequest(MessageContext ctx){ try{ SOAPMessageContext mc = (SOAPMessageContext)ctx; SOAPMessage msg = mc.getMessage(); SOAPPart sp = msg.getSOAPPart(); SOAPEnvelop se = sp.getEnvelop(); SOAPHeader header= se.getHeader(); // Now we can process the header if (everything fine ) return true; // chain handlers //continue processing else{ //Return false results in chaining to stop return false; } }catch(Exception ex){ } } }
|
服务客户机和服务上下文
客户机 JAX-RPC 的亮点之一就在于它能把上下文信息和端点的远程方法调用关联起来。注意,JAX-RPC
规范并不强求上下文信息的语义。用户可以根据 WSDL 绑定中 SOAP 头定义的显式定义它;也可以根据 WS-Security
之类的标准定义它;或者通过使用特定于绑定的细节(比如 HTTP 请求头)来定义它。
这个运行时上下文信息可以由容器或者客户机来设定。容器管理的上下文管理被称作
隐式上下文管理,而客户机管理的管理被称作
显式上下文管理。
在这里使用
隐式一词是因为在隐式上下文管理中,客户机或服务器上都不需要进行编程以支持上下文传播,这种支持是由运行时引擎提供的。这种上下文信息的示例包括安全和事务信息。
显式服务上下文的表现形式是作为追加于服务方法调用的附加参数。当从 JAVA 参数映射到 WSDL 时这可能会引发问题,因为这些增加的元素将映射到 WSDL 头。
清单 10 通过端点 JAVA 接口表示了一个 WSDL 定义及它的
soap:header 信息和显式的服务上下文表示。
清单 10. WSDL 定义的端点 JAVA 接口
public interface BookSerachService implements java.rmi.Remote{ public Books[] searchForBooks(String authorName, StringHolder context) throws RemoteException; }
|
在
清单 10中,您能看到上下文被加到了方法参数上。
JAX-RPC 规范不强制处理服务上下文的服务器端模型。定义处理程序来进行服务上下文是由容器提供商(针对隐式或显式上下文管理)和程序员(针对显式上下文管理)决定的。您能看到,这使得上下文路由和设置报头消息处理器变得灵活。
带附件的 SOAP
JAX-RPC 规范 API 在远程过程调用和/或返回值时支持 MIME 编码的内容的使用。这是以带附件的 SOAP 标准为基础的(请参阅
参考资料)。
带附件的 SOAP 消息是通过使用 MIME multipart/related 类型建立的。根部件是初始的 SOAP 消息,MIME
内容被作为消息的另外部件添加。这些 SOAP 部件可能包含对 MIME 部件的引用。也要注意每一个 MIME 部件包含内容的 ID
或内容定位信息来唯一地标识 MIME 部件。请参阅 图 3,它显示了一条样本 MIME 消息。
图3. SOAP 消息包
JAVA 服务端点接口中的远程方法可能使用几个 JAVA 类型中的一个来表示 MIME 编码的内容。
JAX-RPC 运行时系统通过以下几点决定 MIME 部件的 MIME 类型。
- 使用 WSDL 中
mime.content 元素定义的 MIME 类型
- SOAP 消息中 MIME 部件的
Content-Type
运行时服务
这里有几个必须由 JAX-RPC 运行时基础架构支持的确定的要求。我将在这里讨论最重要的两个。
安全性
JAX-RPC 运行时系统必须支持最基本的 HTTP 认证。客户机发送用户名和密码到 HTTP 服务器,它们在那里被验证(HTTP
服务器被认为是 JAX-RPC 运行时系统的一部分)。所有其他的安全机制(数字证书、SSL、WS-Security
等)都是由客户机和服务器运行时系统所提供的增值功能。这些可以用 JAX-RPC
处理程序和服务传播来处理。想了解更多细节,您可以参阅系统文档来了解关于您的特定运行时更多细节。
会话管理
这个要求使客户机能参与和服务端点的会话。JAX-RPC 的运行时系统至少应该支持下面中的一个:
- 基于 Cookie 的会话管理
- URL 重写
- SSL 会话
服务器初始化会话管理进程。您可以通过设置
session.maintain 属性为
true 来表明服务器就绪以支持会话。要了解更多具体如何做的细节,请参阅样本。
JAX-RPC 会话管理中的一个最明显的遗漏是对 SOAP 基于头的会话相关性的要求,目前大多数工具包都支持此会话相关性。一旦您已经定义了会话管理的标准方式,并且各方已经在 WS-I 和其他标准上达成一致,那么您就可以认为规范的下一个版本将会要求这一点。
JAX-RPC 客户机调用模型
图 4. 客户机端 JAX-RPC
这里是三个不同的从客户机调用服务端点的的模型,如
图 4所示。它们独立于任何特定于服务实现的模型。我将依次讨论每一个模型的细节。
如果您正在为 J2EE 开发,您应该记住下面的特定于 J2EE 的 JAX-RPC 要求:
- 服务应该实现
javax.naming.referenceable 和/或
java.io.serlializable 接口来支持注册和查找。
- 组件供应商必须在部署描述符中声明所有的服务引用。
结束语
到现在为止,我已经讨论了 JAX-RPC 的大部分功能,您可以用这些功能来生成互操作性的 Web
服务。关于这个标准的这个介绍性系列文章将使您能够开发出具有最大程度互操作性的 Web 服务。正如我早些时候提到过的,有很多不同的
JAX-RPC 运行时引擎的实现可供使用,还有不同质量级别的服务功能(来支持
J2EE)可供使用;因此,您总是应该咨询您的应用程序容器提供商来了解更多关于对 JAX-RPC 支持程度的信息。现在,没有哪个单独的
JAX-RPC 运行时系统可以提供本系列中讨论的所有功能。我确信将有这个规范的修正版来支持最近的 SOAP 和 WSDL
规范(两个规范现在的版本都达到了 1.2);JAX-RPC 也可以采用 JAXB(Java XML Binding,JAVA XML
绑定,JSR31)作为互操作性类型映射。请密切注视这个规范,因为 Web 服务发展能支持越来越多的服务概要和 SOAP 头扩展。
参考资料
关于作者
 |

|
 |
Joshy Joseph 是一个在 IBM OGSA 开发小组中工作的软件工程师。他编程时主要喜欢使用 Web 服务、语义 Web、REST 及网格计算这样的新兴技术,以及基于UML、AOP 和 XP 的编程模型。您可以通过
joshy@us.ibm.com和他联系。
|
|