级别: 初级 Andre Tost, 高级认证 IT 专业人员, IBM Software Group Russell Butek (butek@us.ibm.com), 软件工程师, IBM Software Group
2004 年 5 月 01 日 当开发 Web 服务时,通常您并不想把特定于 Web 服务的代码放在实现中。在许多情况下,您会采用现有的代码,并且简单地将另一个访问层加入其中,也就是添加一种方法来通过 HTTP 之上的 SOAP 来调用它。这意味着服务实现对 SOAP 一无所知,它甚至连 XML 也不知道,调用它的客户端可能处于另一个进程中,可能在另一台机器上,甚至可能在地球的另一边。虽然这就是 Web 服务技术很出名的优点,但是它还是引起了一些难题,在本技巧中,我们就着手解决这些难题。
JAX-RPC 处理程序基础知识
JAX-RPC 处理程序允许您在服务调用的不同时间截获 SOAP 消息。处理程序既可以存在于客户端中,又可以存在于服务器端中。如果您在客户端使用 JAX-RPC,您就可以正好在 SOAP 请求消息进入网络之前让处理程序对其进行处理,并且在它返回到客户端之前,您还可以处理响应消息。类似地,在调用服务实现以及传出响应之前,您可以在服务器上截获传入 SOAP 请求消息。
可以将几个处理程序组合在一起,这称之为“处理程序链”。每个处理程序都处理 SOAP 消息,然后将其传送到链中的下一个处理程序。处理发生的确切序列是可配置的。在本文后面我们将再次讨论这个问题。
要开发一个 JAX-RPC 处理程序,您可以简单地创建一个类来实现
javax.xml.rpc.handler.Handler 接口。它有三种方法,可以分别用于处理 SOAP 请求、响应和故障。
处理程序生命周期
处理程序是在 JAX-RPC 规范中定义的。然而,“企业 Web 服务(Enterprise Web Services)”(JSR109)规范描述了如何在 J2EE 环境中使用它们,并且增加了一些对应用程序服务器管理处理程序的方式的说明(请参见
参考资料以获得关于这个规范更详细的信息)。在本文中,我们将假定您的 Web 服务运行在 J2EE 应用程序服务器上,因而将遵循 JSR109 以及 JAX-RPC 的定义。
处理程序是跨服务调用共享的。换句话说,它们可以存储仅仅对特定的客户端或服务器实例有效的信息。您可以将这与处理 Servlet 的方式相比较。当创建新的处理器实例时,就会调用它的 init() 方法。这使得您能够创建可用于多个调用的处理程序。在删除处理程序之前,会调用 destroy() 方法,这样您就可以清除它。然而,根据经验,您应该避免将任何状态完全存储在处理程序中。
处理程序配置
您可以程序化地配置处理程序,而在运行 J2EE 应用程序服务器的情况下,您也可以通过部署描述符来配置它们。处理程序和处理程序链是基于每个服务定义的。它们可以定义在服务器和客户端部署描述符中。
清单 1显示了我们的样本服务的部署描述符,它说明了处理程序类调用处理程序的方式。PerformanceHandler 是为 HelloWorld 服务注册的:
清单 1. 带有处理程序的 Web 服务部署描述符
<webservices id="WebServices_1066491732483">
<webservice-description id="WebServiceDescription_1066491732483">
<webservice-description-name>HelloWorldService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/HelloWorld.wsdl</wsdl-file>
<jaxrpc-mapping-file>WEB-INF/HelloWorld_mapping.xml</jaxrpc-mapping-file>
<port-component id="PortComponent_1066491732483">
<port-component-name>HelloWorld</port-component-name>
<wsdl-port id="WSDLPort_1066491732483">
<namespaceURI>http://pack</namespaceURI>
<localpart>HelloWorld</localpart>
</wsdl-port>
<service-endpoint-interface>pack.HelloWorld</service-endpoint-interface>
<service-impl-bean id="ServiceImplBean_1066491732483">
<servlet-link>pack_HelloWorld</servlet-link>
</service-impl-bean>
<handler id="Handler_1066493401322">
<handler-name>handler.PerformanceHandler</handler-name>
<handler-class>handler.PerformanceHandler</handler-class>
</handler>
</port-component>
</webservice-description>
</webservices>
|
上面定义了多个处理程序,这些处理程序形成了前面提到的链。
存储状态
如果参与一个服务调用的多个处理程序需要共享信息,它们就可以通过将属性添加到消息上下文(当它在处理程序之间传送时)来这样做。从请求到响应,这个消息上下文都是可用的。换句话说,我们可以使用它来将信息存储在传入请求中,而当响应返回时,我们又可以重用这些信息。在后面的示例中,我们将着手这样做。
示例:服务性能处理程序
现在,让我们看一看,您可以如何创建一个处理程序来测量服务实现的响应时间。我们假定您创建了一个 HelloWorld Web 服务,这个服务简单地返回一个字符串类型的消息。
清单 2显示了服务实现 Bean 的代码(您可以通过所有这些来源查找 EAR 文件,包括
参考资料部分中的一个测试客户端在内):
清单 2. Web 服务实现类
public class HelloWorld {
public String helloWorld(String message) {
return "Hello "+message;
}
}
|
必须为托管该 Web 服务的服务器配置处理程序。它将在请求和响应消息中调用,这样我们就可以测量所用的时间。
初始化处理程序
如前所述,每个处理程序都必须实现
javax.xml.rpc.handler.Handler 接口。或者,您可以简单地继承 javac.xml.rpc.handler.GenericHandler 类来轻松地做到这一点,此类提供了所有方法的缺省实现。为了存储性能结果,我们可以使用称为 Logger 的类,我们把它放在 init() 方法中。您可以在
参考资料部分找到 Logger 类的来源。此外,应用程序服务器还将
javax.xml.rpc.handler.HandlerInfo 对象传入此方法,我们也需要它来进行缓存,请参见
清单 3:
清单 3. 处理程序实现--初始化和销毁
public class PerformanceHandler extends GenericHandler {
protected HandlerInfo info = null;
protected Logger logger = null;
public void init(HandlerInfo arg) {
info = arg;
logger = Logger.getLogger("c://temp//HelloWorldServiceLog");
}
public void destroy() {
try {
logger.close();
} catch (Exception x) {}
}
|
注意,在销毁处理程序实例时,我们关闭了 Logger 对象。
处理请求和响应
每个处理程序都实现 handleRequest 方法,该方法是在请求消息到达时调用的,如
清单 4中所示。
清单 4. 处理程序实现--handleRequest 方法
public boolean handleRequest(MessageContext context) {
try {
Date startTime = new Date();
context.setProperty("startTime", startTime);
} catch (Exception x) {
// insert error handling here
x.printStackTrace();
}
return true;
}
|
这里您可以看到,我们将消息上下文中的当前时间存储为名为“startTime”的特性。应用程序服务器将保证相同的消息上下文对象传送到 handleResponse 方法,这样我们就可以测量其中所用的时间,如
清单 5所示。
清单 5. 处理程序实现--handleResponse 方法
public boolean handleResponse(MessageContext context) {
try {
Date startTime = (Date)context.getProperty("startTime");
Date endTime = new Date();
long elapsedTime = endTime.getTime()-startTime.getTime();
logger.write("Elapsed time is "+elapsedTime+"\\n");
} catch (Exception x) {
// insert error handling here
x.printStackTrace();
}
return true;
}
|
现在,您可以配置您的服务了。确保您配置的处理程序与上面的部署描述符中所示的处理程序一样,并且您可以测量执行服务请求所用的时间。
总结
JAX-RPC 定义了一种机制,在这种机制中,您可以通过截获请求和响应消息来管理服务请求,而不必更改实际的服务消费者或提供者。在 J2EE 中,您可以将处理程序配置在部署描述符中,而无需编写任何代码,这为您提供了一种强大的方法,让您可以在 SOAP 消息通过您的系统时对其加以控制。
参考资料
作者简介  | |  | Andre Tost 是一位解决方案设计师,他在 WebSphere Business Development 组工作,在那里他帮助 IBM 的策略联盟(Strategic Alliance)合作伙伴使用 WebSphere 来集成他们的应用程序。他的主要研究方向是在整个 WebSphere 产品家族中使用 Web 服务技术。在承担现在的工作之前,他在 IBM 软件开发中作为开发人员及架构师已经有 10 年了,最近他主要是研究 WebSphere Business Components 产品。他来自德国,现在生活和工作在美国明尼苏达州罗彻斯特市。在他的空闲时间,他喜欢跟家人在一起玩或者看足球。您可以通过
atost@us.ibm.com与 Andre 联系。
|
 | |  | Russell Butek 是 IBM WebSphere Web 服务引擎的开发人员之一。他也是 JAX-RPC Java Specification Request(JSR) 专家组的 IBM 代表。他从事 Apache 的 AXIS SOAP 引擎的实现方面的研究,推动了 AXIS 1.0 遵循 JAX-RPC 1.0。以前,他是 IBM CORBA ORB 的开发人员和许多 OMG 特别工作组的 IBM 代表:包括可移植拦截器特别工作组(他是这个特别工作组的主席)、核心特别工作组以及互操作性特别工作组。您可以通过
butek@us.ibm.com与 Russell 联系。
|
对本文的评价
|