开发者关于 JAX-RPC 的介绍,第 2 部分: 研究 JAX-RPC 的规范提高 Web 服务互操作性

来源:百度文库 编辑:神马文学网 时间:2024/07/05 13:40:29
熟悉这个新兴标准的细节

文档选项

将此页作为电子邮件发送
级别: 初级
Joshy Joseph (joshy@us.ibm.com), 软件工程师, IBM OGSA Development Team
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,那里有一个服务端点实现(具体的)类和一个服务端点接口的定义。

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包含一个服务端点接口的示例。
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 对象是通过引用返回的。
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中给出。
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 元素。










在清单 5中,您可以看到 JAX-RPC 规范怎样创建它的服务端点来处理特定于服务的 JAVA 异常。
Public interface BookSearch implements java.rmi.Remote{
Public Books[] getBooksByAuthor(String authorName) throws java.rmi.RemoteException,
com.acme.AuthorNotFoundException;
}
清单 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告诉您怎样扩展您的服务实现类来支持生命周期管理以及怎样使用服务上下文。
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 消息处理程序,但是它可以很灵活的定义其他处理程序,而且不需要任何消息处理模型。

JAX-RPC 处理程序 API 定义了三个基本的方法,还有两个生命周期方法,表示在清单 8 中。
public class Handler{
handleRequest(MessageContext context)
handleResponse(MessageContext context)
handleFaults(MessageContext context)
init(HandlerInfo info);
destroy();
...........
}
一个处理程序应该被作为无状态的实例实现。通过提供初始化的接口( Handler.init (HandlerInfo info) ),运行时系统可以把所要求的上下文信息传递给处理程序。这将帮助处理程序获得特定于容器的增值功能的访问权,包括认证机制,事务处理,日志记录的框架等。

在 为 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 消息头的样本实现。
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 信息和显式的服务上下文表示。
public interface BookSerachService implements java.rmi.Remote{
public Books[] searchForBooks(String authorName, StringHolder context)
throws RemoteException;
}
在清单 10中,您能看到上下文被加到了方法参数上。
JAX-RPC 规范不强制处理服务上下文的服务器端模型。定义处理程序来进行服务上下文是由容器提供商(针对隐式或显式上下文管理)和程序员(针对显式上下文管理)决定的。您能看到,这使得上下文路由和设置报头消息处理器变得灵活。




回页首
JAX-RPC 规范 API 在远程过程调用和/或返回值时支持 MIME 编码的内容的使用。这是以带附件的 SOAP 标准为基础的(请参阅参考资料)。 带附件的 SOAP 消息是通过使用 MIME multipart/related 类型建立的。根部件是初始的 SOAP 消息,MIME 内容被作为消息的另外部件添加。这些 SOAP 部件可能包含对 MIME 部件的引用。也要注意每一个 MIME 部件包含内容的 ID 或内容定位信息来唯一地标识 MIME 部件。请参阅图 3,它显示了一条样本 MIME 消息。

JAVA 服务端点接口中的远程方法可能使用几个 JAVA 类型中的一个来表示 MIME 编码的内容。
JAX-RPC 规范提供了一个 JAVA 类的集合,它是直接到 MIME 类型的映射。示例包括: Image/gif 映射到 java.awt.image
text/plain 映射到 java.lang.string
text/xml 映射到 javax.xml.transform.Source
请参阅 JAX-RPC 规范的 7.5 节,那里有完整的映射清单(您可以在参考资料里找到一个到这个规范的链接。)
javax.activation.DataHandler 可以映射任何 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 和其他标准上达成一致,那么您就可以认为规范的下一个版本将会要求这一点。




回页首

这里是三个不同的从客户机调用服务端点的的模型,如图 4所示。它们独立于任何特定于服务实现的模型。我将依次讨论每一个模型的细节。
基于存根的模型(静态存根)您有两种方法可以创建服务存根:
来自 JAVA 服务端点接口
来自服务的 WSDL 描述
您可以从 WSDL(用 WSDL2JAVA)或从服务端点接口生成一个存根类。一个生成了的存根类是实现 javax.xml.rpc.stub 和服务端点接口所必需的。这个存根接口通过设置诸如端点地址、会话、用户名、密码这样的属性来提供 API 以配置存根。
动态代理。和上面所讨论的静态存根相反的是,运行时客户机使用 javax.xml.rpc.Service 接口来创建动态服务代理存根客户。客户机能够 推算出关于 WSDL 和它要调用的服务的信息。它使用 ServiceFactory 类来创建服务获取代理。
DII(动态调用接口)。 这个软件模式省却了客户提前了解服务的确切名字和参数的需要。DII 客户机可以使用能查找服务信息的服务代理程序在运行时发现这个信息。服务发现中的灵活性使运行时系统能使用服务代理程序,服务代理程序能够采用各种服务发 现机制(如 ebXML、注册表、UDDI等)。
如果您正在为 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 头扩展。
您可以参阅本文在 developerWorks 全球站点上的英文原文.
请参阅这个系列第一个部分,Developer‘s introduction to JAX-RPC, Part 1。( developerWorks,2002 年 11月)
看一下这篇文章的附录,它包含了示例的完整 WSDL 清单。
从根源,也就是 Java Community Process 中获取JAX-RPC 规范。
JAX-RPC 规范的参考实现建立在Apache Axis beta 3和Sun‘s Web Services Developer Pack的基础之上。这篇文章的代码已经用这些技术测试过。
您也能用最新版本的IBM Web Services Toolkit(可在 alphaWorks 上得到)进行实践。
您可能在读本系列时很方便地参阅下面的 W3C 规范:WSDL
SOAP
带附件的 SOAP 消息
XML
您也会发觉 IBM developerWorks 上的Web 服务规范页很有用。
下面的团体努力维护 Web 服务互操作性:WS-I
W3C
SOAPBuilders
OASIS
查找更多关于实现企业 Web 服务的信息,它是 JSR109。IBM 在这个标准化的实施上处于领先地位。



Joshy Joseph 是一个在 IBM OGSA 开发小组中工作的软件工程师。他编程时主要喜欢使用 Web 服务、语义 Web、REST 及网格计算这样的新兴技术,以及基于UML、AOP 和 XP 的编程模型。您可以通过joshy@us.ibm.com和他联系。
开发者关于 JAX-RPC 的介绍,第 2 部分: 研究 JAX-RPC 的规范提高 Web 服务互操作性 开发者关于 JAX-RPC 的介绍,第 1 部分: 了解 JAX-RPC 类型映射系统的各个方面 Web 服务编程技巧与窍门: 在 JAX-RPC 应用程序中构建有状态会话 中文java技术网::Web Service学习笔记之----JAX-RPC Web Service学习笔记之----JAX-RPC - 中文JAVA技术网 Web services programming tips and tricks: SOAP attachments with JAX-RPC 启动被禁用的RPC服务 Web 服务:Web 服务内幕,第 9 部分:研究问题 Web 服务内幕,第 2 部分:W3C Web 服务专题研讨会的概述 Web 服务内幕,第 2 部分:W3C Web 服务专题研讨会的概述 了解 Web 服务规范,第 1 部分: SOAP 了解 Web 服务规范,第 4 部分:WS-Security 了解 Web 服务规范,第 1 部分: SOAP 实现安全的AXIS Web服务,第1部分 SOA 建模: 第 2 部分 服务规范 SOA 建模: 第 2 部分 服务规范 PHP中使用XML-RPC构造Web Service简单入门 PHP中使用XML-RPC构造Web Service简单入门 JDK6的新特性之十一:更简单,更强大的JAX-WS 使用 Axis2 和 JiBX 将 Java 类转换成 Web 服务,第 2 部分: 把 XML 转换成功能全面的 Web 服务 Web 服务:Web 服务内幕:关于 Soap 的决策 Web 服务:Web 服务内幕:关于 Soap 的决策 Web 服务内幕,第 3 部分:Apache 和 Microsoft -- 良好的合作 基于服务的企业集成模式轻松入门,第 3 部分: Web services 和注册中心