XFire是当前J2EE领域非常流行的Web Service框架,以其卓越的性能和简单易用的特性博得了广大开发者的青睐。目前XFire已经演变为Apache的CXF项目,但仍有大量用户在使用XFire。
下面是XFire客户端调用的流程分析图,本文后续本分将围绕该图展开。
XFire客户端的调用非常灵活,可以有很多种方式,如通过配置调用、通过API编程调用或者与Spring等IoC框架集成使用。虽然调用方式灵活多样,但万变不离其中,其内部流程是一致的。
创建服务模型
服务模型是XFire中非常重要的概念之一,包含了服务的接口信息、操作信息、Binding信息等诸多服务调用过程中需要的信息。因此在进行服务调用之前首先要创建服务模型。创建服务模型的工作是由服务工厂ServiceFactory完成的,用户需要为服务工程提供服务接口、名称、命名空间等一些信息,其中服务接口是必须的,其他为可选信息。
创建Client实例
Client是XFire客户端的核心组成部分,间接的代表了一个服务。当为具体某个服务配置拦截器(Handler,有很多种译法如拦截器、处理器、过滤器等,本文统一用拦截器)时,其实是将拦截器信息应用到Client实例上。Client可以手工创建也可以由XFireProxyFactory创建,无论通过哪种方式,Client在初始化过程中最重要的一步都是在out拦截器堆栈中增加一个OutMessageSender拦截器。该拦截器负责最终将服务调用通过HTTP发送到服务提供者并返回处理结果。本文后续部分还会对OutMessageSender做更加详细的讲解。
创建服务代理对象
XFireProxy,XFire SOAP客户端代理实现,用户调用服务时(如Hello.echo(“tony”))就是通过该对象的invoke方法来执行。实际上,XFireProxy只是将调用代理到Client实例,最终执行服务的还是Client实例。
构造调用链信息
Client实例的invoke方法在执行时,生成了一个Invocation对象,该对象构造了一次完整的调用信息,包括OutMessage、MessageContext等。同时Invocation还负责构造一个拦截器管道(HandlerPipeline),该管道包含了本次调用需要执行的所有拦截器,当然也包括OutMessageSender。这些拦截器会分不同的阶段来执行,这也是XFire一个特性。XFire默认定义了很多阶段(Phase),每个阶段都会有若干拦截器被调用。
循环调用拦截器
拦截器(Handler)是XFire中最为重要的概念,一次服务调用就是由若干拦截器组合完成的。XFire默认提供了很多预定义的拦截器,用户也可以定义自己的拦截器。基本上,通过拦截器可以影响XFire执行过程中的任何步骤,你可以为所欲为:)
拦截器有两个重要的概念,一个是阶段(Phase),一个是顺序(Order)。这两个因素共同决定了拦截器的执行顺序。可以在三个不同的地方配置拦截器:
n XFire实例:全局拦截器,对所有通道上的所有服务起作用
n Transport:通道特定的拦截器,只对该通道(如HTTP、JMS)起作用
n 具体服务:服务特定的拦截器,只对该服务起作用
其实,具体服务上的拦截器最终是配置到Client上。对于同一个阶段上的拦截器,执行顺序为“具体服务—>Transport—>XFire实例”。千万不要忽视这些顺序,这对你正确的使用拦截器非常有帮助。
发送远程服务请求
这是整个调用链中最后的一环,也是最关键的一步。OutMessageHandler,前文已经有所提及,是一个特殊的拦截器,在Client初始化时创建并加入调用链中。该拦截器处于拦截器调用链的Phase.SEND阶段,基本上也是最后的阶段。OutMessageHandler从当前调用的消息上下文(MessageContext)中获取请求的服务地址URI以及SOAP消息,然后通过HTTP将SOAP请求发送到远程服务器(针对HTTP通道,如果是JMS通道则发送到指定的目的地)。最终将远程服务器的响应逐级返回给调用者。
案例分析
前文很多地方都提到Handler非常重要,那么具体有那些应用场景呢?本部分通过两个案例逐步演示Handler的应用。
一、 简单安全验证
这是一个非常典型的应用场景,假设A公司对外提供了一个旅程信息查询服务,该服务通过XFire对外发布。但是A公司只希望其合作伙伴才能使用该服务,那么A公司可以为该服务配置一个Handler,该Handler从SOAP的消息头中获取认证字符串,只有通过验证的请求才被执行。下面是简单的示例代码,真实情况要比这复杂得多。
publicvoid invoke(MessageContext context) throws Exception {
Element header = context.getInMessage().getHeader();
String authCode = header.getChild("authCode",null).getValue();
if(!"tony".equals(authCode)){
thrownew XFireFault("Authentication Fail!", XFireFault.SENDER);
}
}
|
对于A公司的合作伙伴,要想调用该服务,必须在其SOAP的消息头中包含上面代码中的验证字符串,否则服务将被拒绝。下面是简单的示例代码:
publicvoid invoke(MessageContext context) throws Exception {
Element header = context.getInMessage().getHeader();
Element authCode = new Element("authCode");
authCode.addContent("tony");
header.addContent(authCode);
}
|
二、 查找真实服务
这是一个比较特殊的应用场景:假设A公司已经初步实现SOA,拥有一个服务注册中心,所有的XFire服务都在该中心注册。客户端在调用服务时需要动态的从该服务注册中心获取当前的服务地址及版本。通过其他方式肯定也可以实现该需求,但是通过Handler来实现会非常的幽雅,而且对应用不需要做任何变动。我们先来看一下Handler的代码:
publicvoid invoke(MessageContext context) throws Exception {
// 1.寻址
lookupRealServiceUri(context);
}
privatevoid lookupRealServiceUri(MessageContext context) {
String uri = context.getOutMessage().getUri();
try {
uri = serviceLocator.lookup(requestEnvironment, uri);
} catch (Exception e) {
// Ignoral this exception
}
context.getOutMessage().setUri(uri);
}
|
正如代码所示,只需要从context中获取当前请求的服务URI地址,然后用当前请求环境信息及服务URI地址到服务注册中心查找真实的服务,并重新设置服务的地址。
结束语
本文粗略的介绍了XFire客户端的调用流程,并着重讲解了Handler的扩展机制及其应用场景,力求读者能够通过本文对XFire能有更加深入的了解和掌握。文中难免存在不足之处,欢迎任何形式的交流。