使用 RPG 代理调用 Web 服务

设置并生成 Web 服务 RPG 代理

本文将演示如何使用 ILE 的集成式 Web 服务客户端的新特性,即生成 RPG 代理(或存根)的能力,从 RPG 应用程序调用 Web 服务。

Nadir Amra, IBM 软件工程师, IBM

作者 Nadir Amra 的照片Nadir K. Amra 有超过 15 年在 IBM i 上使用 Internet 技术的经验,目前是 IBM i 上的集成式 Web 服务支持的首席架构师和开发人员。



2011 年 9 月 29 日

免费下载:IBM® WebSphere® MQ V7.0.1 试用版
下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。

简介

最近几年,PRG 程序员已经能够使用 wsdl2ws.sh 工具生成的 C 存根来调用 Web 服务。但是,在 RPG 程序中使用 C 存根的流程比较复杂,程序员需要执行以下步骤:

  1. 将 C 结构映射到 RPG 结构。
  2. 添加服务接口功能和操作的 RPG 原型。

如果 WSDL 文件很复杂,步骤 1 可能会是一个噩梦。

就算您竭尽全力完成了上面的步骤,还必须处理指针,并且担心内存泄露!

现在,RPG 程序员可以松口气了。IBM 已经增强了 wsdl2ws.sh 工具,用户能够生成 RPG 存根。本文通过一个简单示例,展示如何使用 wsdl2ws.sh 工具生成的 RPG 存根代码创建一个使用 ILE RPG 编写的 Web 服务客户端应用程序。


先决条件

软件

表 1 根据受支持的 IBM i 操作系统版本列出了所需的 PTF。

表 1. 软件先决条件
IBM i 版本PTF
i 7.1 SI43607
i 6.1 SI43608
i 5.4 SI43609

假设

由于本文的示例依赖于 ConvertTemp 服务,所以您需要按照 了解 Web 服务服务器 中所述创建一个 Web 服务服务器(创建 Web 服务服务器时,不需要部署 Web 服务,我们将使用服务器创建时自动部署的样例 Web 服务)。否则,运行应用程序会出现错误。

注意:ILE 的 Web 服务客户端的安装目录为 /QIBM/ProdData/OS/WebServices/V1/client。在本文中,安装目录显示为 <install_dir>


创建一个使用 RPG 存根代码的 RPG 应用程序

按照下面的步骤开发一个 Web 服务客户端应用程序:

  1. 使用 wsdl2ws.sh 命令生成客户端 Web 服务存根。
  2. 构建客户端应用程序。
  3. 运行客户端应用程序。

下面将逐一讨论这些步骤。为了便于演示,我们将使用产品包含的样例代码,该代码位于目录 <install_dir>/samples/ConvertTemp 中。表 2 中列出了我们要使用的文件,它们包含在 下载 部分的样例下载中。

表 2. 示例中使用的文件
文件说明
ConvertTemp.wsdlWSDL 文件
ConvertTempClientWSDL2RPG.RPGLE 用 RPG 编写的客户端实现代码

清单 1 显示的 WSDL 文件是一个温度转换服务,该服务用于将华氏温度转换为摄氏温度。文件定义了两个操作:

  • converttemp
  • converttemp_XML

converttemp 操作返回摄氏温度,而 converttemp_XML 操作返回 XML 文档形式的结果。注意,清单 1 只提供了这个服务的 WSDL 的部分视图,这部分只涉及 converttemp 操作,这是 RPG 应用程序将要使用的操作。

清单 1. WSDL 定义
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl=http://schemas.xmlsoap.org/wsdl/
                  xmlns:axis2=http://converttemp.wsbeans.iseries
                  xmlns:ns1="http://org.apache.axis2/xsd"
                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
                  xmlns:ns0="http://converttemp.wsbeans.iseries/xsd"
                  xmlns:xs="http://www.w3.org/2001/XMLSchema"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
                  targetNamespace="http://converttemp.wsbeans.iseries">
<wsdl:types>
  <xs:schema xmlns:ns="http://converttemp.wsbeans.iseries/xsd"
             attributeFormDefault="qualified" elementFormDefault="qualified" 
             targetNamespace="http://converttemp.wsbeans.iseries/xsd">
     ... 
     <xs:complexType name="CONVERTTEMPInput">
       <xs:sequence>
         <xs:element minOccurs="0" name="_TEMPIN" nillable="true" type="xs:string"/>
      </xs:sequence>
    <xs:complexType>
    ... 
    <xs:element name="converttemp">
      <xs:complexType>
        <xs:sequence>
          <xs:element minOccurs="0" name="param0" nillable="true" 
                      type="ns:CONVERTTEMPInput"/>
         </xs:sequence>
        <xs:complexType>
     </xs:element>
     <xs:element name="converttempResponse">
       <xs:complexType>
        <xs:sequence>
          <xs:element minOccurs="0" name="return" nillable="true" 
                      type="ns:CONVERTTEMPResult"/>
           </xs:sequence>
         <xs:complexType>
      </xs:element>
      <xs:complexType name="CONVERTTEMPResult">
        <xs:sequence>
          <xs:element minOccurs="0" name="_TEMPOUT" nillable="true" 
                      type="xs:string"/>
        </xs:sequence>
       <xs:complexType>
    </xs:schema>
  </wsdl:types>
  ... 
  <wsdl:message name="converttempRequest">
    <wsdl:part name="parameters" element="ns0:converttemp"/>
  </wsdl:message>
  <wsdl:message name="converttempResponse">
    <wsdl:part name="parameters" element="ns0:converttempResponse"/>
  </wsdl:message>
  <wsdl:portType name="ConvertTempPortType">
    <wsdl:operation name="converttemp_XML">
      <wsdl:input message="axis2:converttemp_XMLRequest" 
                  wsaw:Action="urn:converttemp_XML"/>
      <wsdl:output message="axis2:converttemp_XMLResponse" 
                   wsaw:Action="urn:converttemp_XMLResponse"/>
    </wsdl:operation>
    <wsdl:operation name="converttemp">
      <wsdl:input message="axis2:converttempRequest" wsaw:Action="urn:converttemp"/>
      <wsdl:output message="axis2:converttempResponse" 
                   wsaw:Action="urn:converttempResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="ConvertTempSOAP11Binding" type="axis2:ConvertTempPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <wsdl:operation name="converttemp_XML">
      <soap:operation soapAction="urn:converttemp_XML" style="document"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="converttemp">
      <soap:operation soapAction="urn:converttemp" style="document"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ConvertTemp">
    <wsdl:port name="ConvertTempSOAP11port_http" 
               binding="axis2:ConvertTempSOAP11Binding">
      <soap:address location="http://localhost:10022/web/services/ConvertTemp"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

步骤 1. 创建客户端存根代码

在创建 Web 服务客户端应用程序之前,必须先使用 wsdl2ws.sh 工具生成 RPG 客户端存根。wsdl2ws.sh 工具使用它收到的 WSDL 文件以及 WSDL 文件中引用的关联 XSD 文件来创建客户端存根。

要从 WSDL 源文件生成客户端存根,请完成以下步骤:

  1. 从 CL 命令行运行命令 CRTLIB,创建一个名为 CVTTEMP 的库,该库用于存储程序对象:
    CRTLIB CVTTEMP
  2. 从 CL 命令行运行 QSH CL 命令,启动一个 Qshell 会话。
  3. 运行 wsdl2ws.sh 工具生成的客户端 RPG 存根代码,如下所示:
    <install_dir>/bin/wsdl2ws.sh -o/convtemp/RPG -lrpg
    -s/qsys.lib/cvttemp.lib/wsrpg.srvpgm<install_dir>/samples/ConvertTemp/ConvertTemp.wsdl

如果您检查这个命令,就会发现我们向 wsdl2ws.sh 工具提供了两点指示:一是应该生成 RPG 存根代码并将它存储到 /convtemp/RPG 目录中;二是应该使用生成的存根代码创建服务程序 /qsys.lib/cvttemp.lib/wsrpg.srvpgm

wsdl2ws.sh 工具生成的文件如清单 2 所示:

清单 2. 生成的存根文件
ConvertTempPortType_util.rpgle 
ConvertTempPortType_util.rpgleinc 
ConvertTempPortType_xsdtypes.rpgleinc 
ConvertTempPortType.c 
ConvertTempPortType.cl 
ConvertTempPortType.h 
ConvertTempPortType.rpgle 
ConvertTempPortType.rpgleinc 
CONVERTTEMPInput.c 
CONVERTTEMPInput.h 
CONVERTTEMPResult.c 
CONVERTTEMPResult.h

注意,除生成 RPG 存根代码之外,还生成了 C 存根代码,这是因为 RPG 存根代码构建于 C 存根代码之上。对于所有实用目的,都可以忽略 C 存根代码。

下面是生成的每个 RPG 文件的说明:

  • ConvertTempPortType_util.rpgle:RPG 实用工具例程。
  • ConvertTempPortType_util.rpgleinc:RPG 实用工具例程包含文件。
  • ConvertTempPortType_xsdtypes.rpgleinc:标准数据类型包含文件。
  • ConvertTempPortType.rpgle:RPG Web 服务实现代码。
  • ConvertTempPortType.rpgleinc:RPG Web 服务包含文件。

从 RPG 程序员角度来看,您只需检查

ConvertTempPortType.rpgleincConvertTempPortType_xsdtypes.rpgleinc 文件即可。ConvertTempPortType.rpgleinc 文件定义创建和销毁 Web 服务接口对象的 RPG 函数,以及 Web 服务操作。文件中还定义了 Web 服务操作所需的类型。ConvertTempPortType_xsdtypes.rpgleinc 文件定义了所有基元类型和各种常量。

清单 3 展示了 ConvertTempPortType.rpgleinc 文件。

清单 3. ConvertTempPortType.rpgleinc 文件
        * ********************************************************************
        * ********************************************************************
        * D A T A T Y P E S 
        * ********************************************************************
        * ******************************************************************** 
       D CONVERTTEMPInput_t... 
       D                 DS                   qualified based(Template) 
       D isNil_CONVERTTEMPInput_t... 
       D                                1n
       D TEMPIN                               likeds(xsd_string) 
 
       D CONVERTTEMPResult_t... 
       D                 DS                   qualified based(Template) 
       D isNil_CONVERTTEMPResult_t... 
       D                                1n
       D TEMPOUT likeds(xsd_string) 
 
 
       * ********************************************************************
       * ********************************************************************
       * P R O T O T Y P E S 
       * ******************************************************************** 
       * ********************************************************************
 
 
       * ********************************************************************
       * WEB SERVICE CLIENT STUB PROTOTYPES
       * ********************************************************************
 
       * **************************************************************
       * RPG Call : stub_create_ConvertTempPortType
       * **************************************************************
      D stub_create_ConvertTempPortType... 
      D                PR              1N     extproc('stub_create_ConvertTempPo+
      D                                       rtType@') 
      D this                                  likeds(This_t) 
 
       * **************************************************************
       * RPG Call : stub_destroy_ConvertTempPortType
       * **************************************************************
      D stub_destroy_ConvertTempPortType... 
      D                 PR             1N     extproc('stub_destroy_ConvertTempP+
      D                                       ortType@') 
      D this                                  likeds(This_t) 
 
       * ********************************************************************
       * WEB SERVICE OPERATION PROTOTYPES
       * ********************************************************************
 
       * **************************************************************
       * RPG call : stub_op_converttemp_XML
       * **************************************************************
      D stub_op_converttemp_XML... 
      D                 PR             1N     extproc('converttemp_XML@') 
      D this                                  likeds(This_t) 
      D Value0                                likeds(CONVERTTEMPInput_t) 
      D out                                   likeds(xsd_string) 
 
       * **************************************************************
       * RPG call : stub_op_converttemp
       * **************************************************************
      D stub_op_converttemp... 
      D                 PR             1N     extproc('converttemp@') 
      D this                                  likeds(This_t) 
      D Value0                                likeds(CONVERTTEMPInput_t) 
      D out                                   likeds(CONVERTTEMPResult_t)

检查清单 3 时,请注意以下几点:

  • ConvertTemp.wsdl 只有一个服务:ConvertTemp
  • 该服务只有一个 PortType:ConvertTempPortType
  • ConvertTempPortType 端口类型拥有两个操作:converttempconverttemp_XML。(生成的 ConvertTempPortType.rpgleinc 包含文件中定义的)相应的 RPG 存根操作是 stub_op_converttemp()stub_op_converttemp_XML()
  • Web 服务名为 ConvertTempPortType。要获得一个 Web 服务实例,可以调用 stub_create_ConvertTempPortType () 函数。该函数返回的句柄 (handle) 稍后应该在调用 Web 服务操作时可以用到。要销毁 Web 服务实例,可以调用 stub_destroy_ConvertTempPortType () 函数。(这两个函数都是在生成的 ConvertTempPortType.rpgleinc 包含文件中定义的。)

另外,还生成了文件 ConvertTempPortType.cl。该文件是一个 CL 源文件,它拥有重建包含存根代码的服务程序所需的 CL 命令。您可以将这个源文件复制到某个源物理文件 (source physical file) 中并创建 CL 程序。

步骤 2. 构建客户端应用程序

生成客户存根后,就可以使用存根创建 Web 服务客户端应用程序。

清单 4 展示的 RPG 客户端应用程序使用创建的 RPG 存根来调用 converttemp Web 服务操作。

清单 4. 使用生成的 RPG 存根的 RPG 应用程序
     h DFTNAME(CVTTEMP) 
       *
       /copy ConvertTempPortType.rpgleinc 
 
     d OutputText       s             50 
     d WsStub           ds                  likeds(This_t) 
     d Input            ds                  likeds(CONVERTTEMPInput_t) 
     d Result           ds                  likeds(CONVERTTEMPResult_t) 

      *--------------------------------------------------------------------
      * Program entry point. The input parameter is a character field
      * representing the temperature in Fahrenheit. 
      *--------------------------------------------------------------------
     C     *ENTRY        PLIST
     C                   PARM                      TEMPIN         32
      *--------------------------------------------------------------------
      * Web service logic. The code will attempt to invoke a Web 
      * service in order to convert temperature in Fahrenheit to Celsius
      * and then display the results. 
      *--------------------------------------------------------------------
 
      /free 
         // Get a Web service stub. The host and port for the endpoint may need 
         // to be changed to match host and port of Web service. Or you can pass 
         // blanks and endpoint in the WSDL file will be used. 
         clear WsStub; 
         WsStub.endpoint = 'http://localhost:10000/web/services/ConvertTemp'; 
 
         clear input; 
         Input.TEMPIN.value = %trim(TEMPIN); 
 
         if (stub_create_ConvertTempPortType(WsStub) = *ON); 

           // Invoke the ConvertTemp Web service operation. 
           if (stub_op_ConvertTemp(WsStub:Input:Result) = *ON); 
              OutputText = Input.TEMPIN.value + ' Fahrenheit is ' 
                         + Result.TEMPOUT.value + ' Celsius.'; 
           else; 
              OutputText = WsStub.excString; 
           endif; 
 
           // Display results. 
           dsply OutputText; 

           // Destroy Web service stubs. 
           stub_destroy_ConvertTempPortType(WsStub); 
         endif; 
 
         *INLR=*ON; 
      /end-free

要构建客户端应用程序,请完成以下步骤:

  1. 将当前工作目录更改为 RPG 存根代码的位置。从 CL 命令行运行以下命令:
    cd ’/convtemp/RPG’
  2. 从 CL 命令行运行以下命令,这会将使用所生成的存根代码的样例 RPG 代码从产品样例目录复制到当前工作目录:
    COPY OBJ(’<install_dir>/samples/ConvertTemp/ConvertTempClientWSDL2RPG.RPGLE’)
    TODIR(’/convtemp/RPG’)
  3. 如果您创建了一个 Web 服务服务器,那么将上一步中所复制文件中的端点服务器名称和端口号更改为服务器中安装的 ConvertTemp Web 服务的服务器和端口号。文件中的端点为:
    'http://localhost:10000/web/services/ConvertTemp'
  4. 从 CL 命令行运行以下命令,以构建客户端应用程序:
    CRTRPGMOD MODULE(CVTTEMP/CNVRTTEMP)
    SRCSTMF(’/convtemp/RPG/ConvertTempClientWSDL2RPG.rpgle’)


    CRTPGM PGM(CVTTEMP/CNVRTTEMP)
    MODULE(CVTTEMP/CNVRTTEMP)
    BNDSRVPGM(QSYSDIR/QAXIS10CC CVTTEMP/WSRPG)

步骤 3. 运行客户端应用程序

完成 Web 服务客户端应用程序的编码和构建工作后,应该运行和测试客户端应用程序。要运行客户端应用程序,请从 CL 命令行运行以下命令(在本例中,我们想知道华氏 5 度等于摄氏几度):

CALL CVTTEMP/CNVRTTEMP  '5'

检查客户端应用程序是否显示上述华氏温度的摄氏温度值。图 1 中的屏幕快照显示了从命令行运行的客户端应用程序(注意,要查看结果,只需按下 "F10=Include detailed messages" 功能键):

图 1. 调用客户端应用程序
图 1. 调用客户端应用程序

结束语

生成 RPG Web 服务存根的能力是一个巨大飞跃,以前,RPG 程序员必须弄清如何使用 C Web 服务存根。那么,现在您还在等什么呢?


下载

ConvertTemp.wsdl

ConvertTempClientWSDL2RPG.RPGLE

参考资料

学习

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=IBM i
ArticleID=758312
ArticleTitle=使用 RPG 代理调用 Web 服务
publish-date=09292011