使用 WESB 的 Web 服务绑定与基于 WSCF 的 Web 服务客户端通信
 

2009-07-30 作者:孙 英佶,高 忠涛 来源:IBM

 
本文内容包括:
随着 WESB(WebSphere Enterprise Service Bus,WebSphere 企业服务总线)功能的日益强大,越来越多的企业使用 WESB 在其应用系统之间进行集成。Web 服务(Web Service)技术以其标准性和灵活性,逐步成为系统集成中使用的主要技术,因而 WESB 的 Web Service Binding(Web 服务绑定)也就越来越多的被使用。

通常在使用 WESB 进行集成的工程中,Web Service Export Binding 所发布的服务描述文件是以 WSDL(Web Service Definition Language,WEB 服务描述语言)嵌套文件对的形式出现的。其中一个用于描述 WESB 模块发布的服务,在这个文件之中,通过 <import> 的方式引用了客户原有的 WSDL 文件。而原有文件中包含了客户已经定义好的甚至是正在使用中的数据结构和端口类型等信息。然而,部分客户已有的 Web 服务应用程序在构建时使用了 WSCF(Web Services Contract-First,Web 服务契约先行)作为 WSDL 文件的解析器,而某些早期版本的 WSCF 并不能处理这种嵌套的 WSDL 文件结构。因而就需要一种方法,作为 WESB 与这种应用之间的桥梁,从而达到使用 WESB 对企业该类系统集成的目的。

一个典型场景

笔者选取了一个典型的 WESB 应用场景:

图 1 客户使用 WESB 在不同的 Web 服务应用之间进行集成

图 1 所示,是客户使用 WESB 在各个已有应用之间进行集成的典型场景。客户的各个应用或本身就是 Web 服务,或被封装成 Web 服务与 WESB 相连。WESB 则负责在不同的服务消费者和提供者之间进行路由,同时提供一些消息格式转换之类的中介功能,从而达到将企业应用整合的目的。

客户的架构是合理的,但是在实施过程中还是遇到了一些问题,如图中“1”的位置所示。客户正在使用一个 VC 开发的 Web 服务应用程序,假设该 Web 服务使用的描述文件叫 originalService.wsdl。通常使用 WESB 进行集成的作法是,在中介模块(Mediation Module)的输入端,使用一个 Web Service Import Binding,指向 originalService.wsdl;然后根据需求在中介模块中使用其中定义的接口或用户定义的新接口生成一个 Web Service Export Binding,从而发布出一个新的 Web 服务,假设对应新服务的描述文件为 ExportService.wsdl。原有客户端只要指向这个新文件中的服务地址就可以使用新的 Web 服务了。用这种方法就可以把 WESB 的模块安插到企业原有的应用之间,从而发挥其在消息中介方面的优势。

但是问题也出现在这里。WESB 发布的 Web 服务描述文件是由两个 WSDL 文件嵌套组成,ExportService.wsdl 中描述了新的 Web 服务的地址和端口信息,其中使用了 <wsdl:import=” originalService.wsdl” namespace=”……”> 来引入 originalService.wsdl,从而复用了客户原有服务的数据结构。而客户原先的客户端是使用 VC 进行开发的,其中生成调用 Web 服务的代码的时候,利用了 WSCF 所提供的功能。如前所述,早期的 WSCF 并不提供对嵌套的 WSDL 文件结构的支持。换言之,也就是 VC 的 Web 客户端应用无法调用由 Web Service Export Binding 所发布出的 Web 服务。

示例 1 本场景中 VC 客户端调用 Web 服务时发生的错误

指定的 XXXX 告警接收服务调用失败,请确认服务是否存在并已经启动。
System.ArgumentException: 
Cannot find definition for http://xxxx. 
Service Description with namespace http://yyyy is missing.
Parameter name: name
 at System.Web.Services.Description.ServiceDescriptionCollection.GetServiceDescription(
                             XmlQualifiedName name)
 at System.Web.Services.Description.ServiceDescriptionCollection.GetPortType(
                           XmlQualifiedName name)
 at System.Web.Services.Description.ProtocolImporter.GenerateCode(
      CodeNamespace codeNamespace, ImportContext importContext, Hashtable exportContext)
 at System.Web.Services.Description.ServiceDescriptionImporter.Import(
          CodeNamespace codeNamespace, ImportContext importContext, 
                Hashtable exportContext, StringCollection warnings)
 at System.Web.Services.Description.ServiceDescriptionImporter.Import(
                CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit)
 at BOCO.APP.Common.DynWSCall.DynWebService.GeneratorCodeUnit(
                     String wsdlUrl, String keyFile, ClassInterfaceType intfType)
 at BOCO.APP.Common.DynWSCall.DynWebService.CompilerCSharp(
  String wsdlUrl, String keyFile, ClassInterfaceType intfType, Boolean generateInMemory)
 at BOCO.APP.Common.DynWSCall.DynWebService.Binding(
        String url, String keyFile, ClassInterfaceType intfType)
 at BOCO.APP.NMS_EOMS_INTF.AutoForwardAlarmWorkSheet.FormConfig.TestAlarmService()

客户的代码已经无法修改,而 WESB 的 Web Service Export Binding 的生成机制已经成型已久,无法改变。并且 WESB 的中介模块已经在客户的集成项目中发挥了良好作用,因而也不能废弃或者进行大规模的修改。

问题的解决思路

既然问题是出在客户端使用的 WSCF 不能解析嵌套的 WSDL 文件,那么是否可以考虑使 Web Service Export Binding 发布出单一的 WSDL 文件,使得 WSCF 客户端能够成功解析。笔者曾经尝试手动合并 Web Service Export Binding 所使用的 WSDL 文件,然而中介模块所使用的 Web 模块无法成功发布合并后的 WSDL 文件。因此笔者转而考虑是否有新的机制来为中介模块生成和发布 Web 服务。我们比较中介模块在使用 Web 服务绑定前后的结构,发现使用了 Web 服务绑定(无论 Import Binding 还是 Export Binding)之后,中介模块之中会加入一个新的 Web 模块。因而笔者决定使用一个独立的 Web 模块代替中介模块原有的 Web Service Export Binding,实现发布 Web 服务的功能。新的 Web 模块与后端的中介模块通信,并且发布在同一个 EAR 包之中。

笔者在实际工程之中使用了一个新的中介模块,该中介模块与客户原有的模块之间使用 SCA Binding 进行通信。在这个中介模块里加入独立的 Web 模块,使用客户原有的服务描述文件 originalService.wsdl 来发布 Web 服务。在该 Web 模块内编写 Web 服务的实现代码与中介模块的实现通信。

具体的实现步骤

本工程一共由 3 个模块组成:

ServiceProvider:后端 Web 服务模块,模拟客户生产环境中的 Web 服务提供者;

CustomerMediation:这个模块是模仿客户正在使用的中介模块,其作用是在服务提供者和服务消费者之间进行路由和消息转换;

CSDLAdapter:这个模块的作用在于连接 CustomerMediation 与独立的 Web 模块。它与 CustomerMediation 之间以 SCA 绑定的方式相连接。同时在其内部增加独立的 Web 模块,直接将 originalService.wsdl 发布成 Web 服务,共 VC 客户端调用。

图 2 CSDLAdapter 在典型场景中的位置

由于本文主要介绍 CSDLAdapter 的搭建步骤,因此只是简单提及 ServiceProvider 和 CustomerMediation 的构建方法,读者如果有问题的话,可以查看参考资料中的 Info Center 中的相关介绍。

笔者使用了 Dynamic Web Module 来搭建 ServiceProvider,所发布的服务描述文件名叫作 originalService.wsdl。

CustomerMediation 是基于 WESB 的中介模块。这个模块使用客户自定义的接口,名叫 CustomerIntf。为了简化起见,接口里面只定义了一个名叫 operation1 操作,输入和输出参数都是 String 类型。

假设我们已经完成上面两个模块,下面着重介绍 CSDLAdapter 的构建方法。

  1. 在 CustomerMediation 模块的装配图中,右键点击 CustomerMediation,Generate Export…SCA Binding,生成的 Binding 名叫 CustomerMediationExport;
    图 3 在 CustomerMediation 中发布 SCA Export Binding

     
  2. 新建一个 Mediation Module,名叫 CSDLAdapter,接口使用 CustomerMediation 的接口 CustomerIntf 即可。
  3. 加入一个 Standalone Reference,名字叫 CustomerIntfPartner,这个我们后面要用到。同时将 CustomerMediation 的 CustomerMediationExport 拖入,生成一个 SCA Import Binding,名叫 Import1;
  4. 将几个组件相连,CSDLAdapter 使用接口 CustomerIntf。分别连接好其中的请求流和响应流。笔者在这里加入了打印日志到控制台的功能。读者朋友也可以加入其他所需的功能;
    图 4 新建 CSDLAdapter 中介模块

     
  5. 接下来的工作就是以 CSDLAdapter 模块为基础,发布独立的 Web 模块。首先切换到 J2EE 视图。新建一个 Dynamic Web Module,名字叫 CSDLAdapterWeb,同时把这个模块加入 CSDLAdapter 的 EAR 文件之中。这一步操作是构建成功的关键,Web 模块的名字一定要是所在模块的名字加上“Web”,请注意大小写。
    图 5 在 CSDLAdapter 中,加入独立的 Web 模块

     
  6. 右键点击 CSDLAdapterWeb,新建一个 Web Service,注意 Web service type 选择“Top down Java bean Web Service”,Service definition 选择 originalService.wsdl 即可,同时注意图中红圈所示位置,选择 Assemble service,点击 Finish;

    图 6 在 CSDLAdapterWeb 中,创建 Web Service

     
  7. 我们查看 CSDLApdaterWeb 模块,会发现在 Java Resources: src 节点下面,生成了几个 java 文件,我们来编辑 EchoSOAPImpl.java 文件。echoOperation 的实现代码如下:

    示例 2 Web 模块的服务实现代码

    import com.ibm.websphere.bo.BOFactory;
    import com.ibm.websphere.sca.Service;
    import com.ibm.websphere.sca.ServiceManager;
    import commonj.sdo.DataObject; 
    
    private static String ref = "CustomerIntfPartner";
    
    public java.lang.String echoOperation(java.lang.String parameter) 
          throws java.rmi.RemoteException {
     	System.out.println("into web module");
     String wsdlOperation = "operation1";
     String rtnStr = "error";
     	try{
     ServiceManager sm = new ServiceManager();
     Service service = (Service) sm.locateService(ref);
     System.out.println("invoking"); 		 
     String	output = (String) service.invoke(wsdlOperation,parameter);
     System.out.println("invoked");
     if (output!=null) {
     System.out.println("value got");
     rtnStr = output;
     }else{
     System.out.println("No value");
     	rtnStr = "NULL";
     }
     	}catch(Exception ex){
     		rtnStr = "error";
     		ex.printStackTrace();
     	}
     return rtnStr;
     }
     
  8. 编译所有的文件,将 CSDLApdater 文件导出成 EAR 文件,与 CustomerMediation 和 ServiceProvider 一起部署在 WESB 服务器之上。
  9. 启动这三个应用程序,然后在浏览器中输入“http://localhost:9080/CSDLAdapterWeb/services/EchoServicePort”,如果看到下图所示界面,就说明 CSDLAdapter 的 Web 服务已经创建和发布成功了。
    图 7 服务成功发布的界面

     
  10. 笔者使用 RAD 自带的 Web Services Explorer 做了验证。输入“sunyj”,可以看到返回值是“hello sunyj”。我们查看 SystemOut 文件的输出,可以看到消息在这个应用之中流过的痕迹。
    1. “forward in CSDLAdapter”表示消息最早是发送到 CSDLAdapter 模块之中;
    2. “Forward/backward in Mediation”表示消息被客户原来的中介模块 CustomerMediation 进行转发;
    3. “into service”表示这个消息最终是在 ServiceProvider 中根据业务需求进行的处理的。
图 8 经过测试所有模块都运行正常的界面

通过这种方法对 CustomerMediation 进行增强,使之能够与 VC Web 服务客户端进行通信。

结束语

本文通过解决在实际项目中遇到的问题,向大家提供了一种使用 WESB 进行集成的新思路,提出了一个将中介模块发布成 Web 服务的新方法,并解决了因对嵌套 WSDL 文件支持不足而在集成过程中引发的问题。推广而言,这个方案可以解决所有无法处理嵌套 WSDL 文件的 Web 服务客户端程序与 WESB Web 服务绑定的互联问题。本文中所提到的 WSCF 只是其中的典型代表而已。据笔者了解,WSCF 已经被集成进了 VisualStudio 系列开发工具之中,因此当使用 VC、VB、C# 等开发的 Web 服务应用无法与 WESB 的服务绑定进行集成时,不妨考虑一下本解决方案。如果本文有幸帮助读者解决了他们的技术难题,那是我们最大的收获。

另外,上文中也提及了笔者曾经尝试过的另一个解决方法,笔者尝试将 Web Service Export Binding 所使用的 WSDL 文件合二为一,从而满足 VC 客户端的要求。但是笔者经过试验,发现 Web Service Export Binding 无法读取合并后的 WSDL 文件。或许除了 WSDL 文件a和 EJB 的描述文件之外还有其他的参数需要设置。如果读者朋友们有兴趣可以研究一下。

参考资料

学习 获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织