System i で RPG または COBOL プログラムから Web サービスを使う

IBM Web Services Client for C++ を使って、Web サービス・クライアントのスタブを生成する方法と、SOAP メッセージを管理して HTTPで送信するプログラムを収容するライブラリーを生成する方法を学んでください。

Jerome Tarte, Advisory IT Architect, IBM

Jerome TarteJerome Tarte は、フランス La Gaude にある Europe Business Solutions Center の IT アーキテクトです。9 年間にわたり多数の J2EE プロジェクトに携わってきた彼は、この 3 年間、SOA および Web サービスを重点に取り組んでいます。現在は IBM System and Technology Group Lab Services Europe の一員として、アーキテクチャー設計、SOA、WebSphere、高可用性をはじめとする最先端技術の IBM ソフトウェアおよびハードウェアの分野で IBM Systems に貢献しています。



Philippe Guerton, IT Specialist, IBM

Photo of Philippe GuertonPhilippe Guerton は、フランス La Gaude にある Europe Business Solutions Center の IT スペシャリストです。9 年間、数々の System i5 Integrated Language Environment プロジェクトならびに System i5 での SAP 実装に取り組んできた実績を持ちます。この 3 年間は J2EE を中心に、ネイティブ 5250 アプリケーションの Web 対応化に専念してきました。現在は IBM System and Technology Group Lab Services Europe の一員として、IBM Systems ハードウェア・プラットフォームで動作する最先端技術に取り組んでいます。専門分野には、高可用性システム、WebSphere Portal、そして SAP、JDE、5250 アプリケーションなどのバックエンド統合があります。



2007年 5月 21日

はじめに

RPG アプリケーションを Web サービスから利用可能な情報にアクセスできるようにして、各種ソースの XML データを操作できるようにするというニーズが次第に高まっています (RPG アプリケーションの詳細は、「参考文献」のリンクを参照)。XML Toolkit for iSeries™ では SAX パーサーと DOM パーサーの両方にアクセスできますが、それでも RPG プログラム内で共通して使用されるフィールドおよび構造への XML データのマッピングを処理するには大々的なプログラミングを行わなければなりません (XML ツールキットの詳細は、「参考文献」のリンクを参照)。SOAP メッセージを作成して HTTP で送信するとなると、相当なプログラミング作業と SOAP プロトコルに関する十分な知識が必要になります。

Web サービスの呼び出しを単純にするため、IBM では XML ツールキットとは別に、SOAP プロトコルをマスターしなくても簡単に Web サービスを呼び出せるもう 1 つのツールキット、IBM Web Services Client for C++ を用意しています。このツールキットは、Web サービス・クライアントのスタブを生成するためのツール、そして SOAP メッセージを管理して HTTP で送信するためのプログラムを収容するライブラリーを提供します。Web Services Client for C++ ツールキットについての詳細は、「参考文献」のリンクを参照してください。


Web Services Client for C++ ツールキットのアーキテクチャーについて

Web Services Client for C++ ツールキットは外部で記述されたデータ構造を使って、XML 文書の要素とフィールド間に RPG アプリケーションで使用可能なマッピングを定義します。また、SOAP 処理に必要な HTTP ヘッダーの生成をはじめ、XML 文書を HTTP 要求によって送受信するための機能も提供します。

図 1 は、このツールキットを使って統合言語環境 (ILE) プログラムから Web サービスを呼び出す場合のアーキテクチャーです。

図 1. Web Services Client for C++ ツールキットを使用したアーキテクチャー
Web Services Client for C++ ツールキットを使用したアーキテクチャー

サンプル Web サービスの紹介

この記事では基本的な Web サービスの一例として、ユーロ単位の数値を別の通貨に換算する Web サービスについて説明します。Java Bean として実装されるこのサンプル Web サービスのビジネス・コードはリスト 1 のとおりです。

リスト 1. Web サービス・ツールキットのビジネス・コード
…
/**
 * convert am amount in euro into an amount in the currency given in parameter
 * @param currency the targeted currency
 * @param amount, the amount
 * @return the converted amount.
 */
public float convert(String currency, float amount)
{
        float result =  (float)(getRate(currency)*amount);
        return result;
}
….

Web サービスは、Web サービス記述言語ファイル (WSDL) というファイルによって記述されます。WSDL は、インターネットの特定の場所で利用できる Web サービスに関するすべての情報が含まれる XML 形式のファイルです。最も単純なレベルでは、要求応答メッセージ・ペアの詳細な記述など、WSDL には Web サービスのあらゆる具体的な内容が含まれます。この記事のサンプルで使用する WSDL をリスト 2 に記載します。

リスト 2. サンプル Web サービスの WSDL ファイル
<wsdl:definitions targetNamespace="http://currencyquote.ibm.com" 
     xmlns:impl="http://currencyquote.ibm.com" 
     xmlns:intf="http://currencyquote.ibm.com" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
     xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
     xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd" 
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <wsdl:types>
  <schema targetNamespace="http://currencyquote.ibm.com" 
           xmlns="http://www.w3.org/2001/XMLSchema" 
           xmlns:impl="http://currencyquote.ibm.com" 
           xmlns:intf="http://currencyquote.ibm.com"                                     
           xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
           xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <element name="convertResponse">
    <complexType>
     <sequence>
      <element name="convertReturn" type="xsd:float"/>
     </sequence>
    </complexType>
   </element>
   <element name="convert">
    <complexType>
     <sequence>
      <element name="currency" nillable="true" type="xsd:string"/>
      <element name="amount" type="xsd:float"/>
     </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>
   <wsdl:message name="convertResponse">
      <wsdl:part element="impl:convertResponse" name="parameters"/>
   </wsdl:message>
   <wsdl:message name="convertRequest">
      <wsdl:part element="impl:convert" name="parameters"/>
   </wsdl:message>
   <wsdl:portType name="CurrencyQuoteBean">
      <wsdl:operation name="convert">
         <wsdl:input message="impl:convertRequest" name="convertRequest"/>
         <wsdl:output message="impl:convertResponse" name="convertResponse"/>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="CurrencyQuoteBeanSoapBinding" 
           type="impl:CurrencyQuoteBean">
      <wsdlsoap:binding style="document" 
           transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="convert">
         <wsdlsoap:operation soapAction=""/>
         <wsdl:input name="convertRequest">
            <wsdlsoap:body use="literal"/>
         </wsdl:input>
         <wsdl:output name="convertResponse">
            <wsdlsoap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="CurrencyQuoteBeanService">
      <wsdl:port binding="impl:CurrencyQuoteBeanSoapBinding" 
           name="CurrencyQuoteBean">
         <wsdlsoap:address location=
           "http://localhost:9080/CurrencyQuote/services/CurrencyQuoteBean"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

Web サービス・プロキシーの生成

WSDL を一連の C++ スタブとデータ・オブジェクトとして呼び出し、情報を渡せるように変換するには、Web Services Client for C++ パッケージに含まれる WSDL2WS という Java プログラムを使用します。これらの C++ スタブとデータ・オブジェクトもサーバーの情報を要求し、対応する応答を待機してから応答データ・オブジェクトをクライアントに返します。スタブはこのネットワーク通信をアプリケーション書き出しプログラムには見えないようにします。そのため、皆さんが知る必要があるのは、サービスの名前、サービスに含まれるメソッド、そして渡されるデータ・オブジェクトの構造だけとなります。

このサンプルのプロキシーを生成するには、QShell から以下のコマンドを実行します。

/wscc-1.1.0-os400/bin/wsdl2ws.sh CurrencyQuoteBean.wsdl

WSDL2WS ツールには以下のオプションがあります。

  • -lc は C プロキシーを生成します。デフォルト出力は C++ です。
  • -v は生成状況を詳細に追跡します。
  • -otarget_directory は出力先を指定します。

出力されるファイルのタイプには .cpp.h があり、一方はサービスを表す C++ ファイル、もう一方はサービスが使用する基本型以外のデータ用です。この通貨見積りのサンプルで出力されるのは、CurrencyQuoteBean.cpp および CurrencyQuoteBean.h のみとなります。このツールキットには実質上、WSDL ファイルに関する制約があるためです。すなわち、使用する WSDL で定義できるサービスは 1 つだけで、そのサービスに指定するポート・タイプも 1 つに限られます。


C プロキシーと C++ プロキシーのどちらを使うべきかの判断

WSDL2WS では、C または C++ でコードを生成できます。どちらの出力言語を選択するかは重要で、以下の 2 つの側面を考慮しなければなりません。

  • C++ コードを RPG または COBOL プログラムから呼び出すのは厄介です。RPG と COBOL はいずれもオブジェクト指向ではないため、オブジェクトの C++ メソッドを呼び出すよりも C 関数を呼び出すほうが簡単です。
  • WSDL の構造は、XML パラメーターの記述によるオブジェクト指向の構文に似ています。Web サービス要求を開発するときには、オブジェクト指向の構文を使用すると便利です。

妥協案としてふさわしいのは、RPG または COBOL コードと生成される C++ コードとの間に C ラッパーを使うことです。このソリューションでは、RPG または COBOL で C 関数を呼び出すことが可能になります。C 関数は、C++ プロキシーを使った Web サービス呼び出しを実装することにより、C++ の振る舞いを模倣します。

RPG または COBOL モジュールから呼び出した関数をエクスポートするには、#pragma map ディレクティブを以下のように使用します。

#pragma map (convert(xsd__string,float), "CONVERT")

生成された C++ コードの前に C ラッパーを使用することで、この宣言は C ラッパー内に含まれることになります。つまり、生成されたコードを変更する必要はありません。リスト 3 に記載するコードは、このサンプルの場合の C ラッパーです。

リスト 3. サンプル C ラッパー
#include <iostream>
#include <string.h>
#include "CurrencyQuoteBean.hpp"

using namespace std;
#pragma map (convert(xsd__string,float), "CONVERT")
float convert(xsd__string devise, float amount)
{
        // create the service proxy object
        // give the service port as parameter
        CurrencyQuoteBean* cb = new CurrencyQuoteBean("http://
       9.212.15.63:9080/CurrencyQuote/services/CurrencyQuoteBean") ;
        // invoke the web service by calling the business method.
float result = cb->convert(devise,amount);
        return result;
}

RPG から呼び出す場合

C++ コードと C コードは、Web サービス呼び出しを処理します。RPG 側でコードが管理するのは C/C++ モジュールとの統合だけで、Web サービスに固有のものは何もありません。C/C++ モジュールとの統合には、パラメーターと戻りの型を指定した C 関数 (サンプルでは convert) の宣言が必要です。RPG プログラムの場合、convert メソッドの呼び出しはリスト 4 のようになります。

リスト 4. RPG プログラムによる convert メソッド呼び出し
     H NOMAIN

     Dconvert          PR             8F
     D P_currency                     6A
     D P_amount                       8F

     DconvertTo        PR            15P 5
     D P_curr                       256A
     D P_amnt                        15P 5

     PconvertTo        B                   EXPORT
     DconvertTo        PI            15P 5
     D P_curr                       256A
     D P_amnt                        15P 5

     D cvt             S              8F
     D amount          S              8F

     D converted       S             15P 5
     D currency        S              6A

     C                   EVAL      currency = P_curr
     C                   Z-ADD     P_amnt        amount
     C                   EVAL      cvt = convert(currency:amount)
     C                   Z-ADD     cvt           converted
     C                   RETURN    converted
     PconvertTo        E

COBOL から呼び出す場合

RPG プログラムの場合と同じく、COBOL プログラムの手法を使って Web サービスを呼び出すのも COBOL プログラムと C プログラムを統合すればいいだけの話です。COBOL プログラムでは、C 関数の定義は Special-Names セクションに宣言します。C 関数が宣言されると、Call Procedure 文を使ってこの関数を呼び出せるようになります。リスト 5 を参照してください。

リスト 5. COBOL プログラムによる convert メソッド呼び出し
            Identification Division.
        Program-ID.     WRAPCBL.
        Author.

       Environment Division.
        Configuration Section.
        Source-Computer.   IBM-ISERIES.
        Object-Computer.   IBM-ISERIES.
          Special-Names.  LINKAGE PROCEDURE FOR "convert"
                                   USING ALL DESCRIBED.

        INPUT-OUTPUT SECTION.
        FILE-CONTROL.
        DATA DIVISION.
        FILE SECTION.
        WORKING-STORAGE SECTION.

        01 DEVISE          PIC x(6).
        01 AMOUNT          USAGE IS COMP-1.
        01 RETVAL          USAGE IS COMP-1.

       Procedure Division.

       Main.
           string "dollar" delimited by size X"00"
               delimited by size INTO devise.
           ADD 123.45 to amount.
           Call Procedure "convert" using DEVISE
                                          By value AMOUNT
                returning INTO RETVAL.
           DISPLAY RETVAL.

コンパイルとバインディング

C および C++ ソース・ファイルのコンパイルでは、include ディレクトリーが Web Service Client for C++ ツールキットで指定された include ディレクトリーを定義していなければなりません。このディレクトリーは、wscc-1.1.0-os400 インストール・ディレクトリー (デフォルトでは、/QIBM/ProdData/xmltoolkit/wscc-1.0-OS400) でローカライズされます。

C++ プロキシー・モジュールが含まれるプログラムまたはサービス・プログラムを作成するときには、AXIS サービス・プログラム (デフォルトでは、/QXMLTOOLS/QAXIS10C) とのバインディングを定義する必要があります。プログラムの生成を容易にするには、すべてのコンパイルおよびリンク・コマンドが含まれるリスト 6 の CL スクリプトを使用してください。

リスト 6. CL スクリプトによる通貨見積りサンプル・プログラムの作成
               PGM

             ADDLIBLE   LIB(BOP) POSITION(*FIRST)
             MONMSG     MSGID(CPF2103)

             CRTCPPMOD  MODULE(ILETOWS/CURRMODC) +
                          SRCSTMF('/CTC_assets/ILEtoWS/CurrencyQuoteBean.cpp+
                          ') DBGVIEW(*SOURCE) REPLACE(*YES) +
                          INCDIR('/QIBM/ProdData/xmltoolkit/wscc-1.0-OS400/include')

             CRTCPPMOD  MODULE(ILETOWS/CURRWRAP) +
                          SRCSTMF('/CTC_assets/ILEtoWS/wrapper.c') +
                          DBGVIEW(*SOURCE) REPLACE(*YES) +
                          INCDIR('/ QIBM/ProdData/xmltoolkit/wscc-1.0-OS400/include')

             CRTRPGMOD  MODULE(ILETOWS/CVTCURR) SRCFILE(ILETOWS/QRPGLESRC) +
                          SRCMBR(CVTCURR) DBGVIEW(*SOURCE) REPLACE(*YES)

             CRTCLMOD   MODULE(ILETOWS/TESTCVT) SRCFILE(ILETOWS/QCLLESRC) +
                          SRCMBR(TESTCVT) REPLACE(*YES) DBGVIEW(*SOURCE)

             CRTPGM     PGM(ILETOWS/RPGTOWS) MODULE(ILETOWS/TESTCVT +
                          ILETOWS/CVTCURR ILETOWS/CURRWRAP +
                          ILETOWS/CURRMODC) ENTMOD(*FIRST) +
                          BNDSRVPGM(QXMLTOOLS /QAXIS10C) ACTGRP(*CALLER) +
                          REPLACE(*YES) STGMDL(*SNGLVL)

構造とオブジェクトについて

入力パラメーターと出力パラメーターに基本型 (String, int, float … など) しか使用しない Web サービスは滅多にありません。Web サービスは基本型と併せて複合型も使うため、クライアントには基本型より複雑な型でも処理する能力が求められます。そこで 2 つ目のサンプルとして、複数の属性が含まれる複合型 Address で応答する Web サービスを紹介します。この Web サービスを記述する WSDL ファイルは、リスト 7 のとおりです。

リスト 7. 2 つ目の Web サービスを記述する WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://user.ibm.com" 
     xmlns:impl="http://user.ibm.com" 
     xmlns:intf="http://user.ibm.com" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
     xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
     xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd" 
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <wsdl:types>
  <schema targetNamespace="http://user.ibm.com" 
     xmlns="http://www.w3.org/2001/XMLSchema" 
     xmlns:impl="http://user.ibm.com" xmlns:intf="http://user.ibm.com" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <element name="getAddressResponse">
    <complexType>
     <sequence>
      <element name="getAddressReturn" nillable="true" type="impl:Address"/>
     </sequence>
    </complexType>
   </element>
   <element name="getAddress">
    <complexType>
     <sequence>
      <element name="id" type="xsd:int"/>
     </sequence>
    </complexType>
   </element>
   <complexType name="Address">
    <sequence>
     <element name="number" type="xsd:int"/>
     <element name="street" nillable="true" type="xsd:string"/>
     <element name="town" nillable="true" type="xsd:string"/>
     <element name="zipCode" nillable="true" type="xsd:string"/>
    </sequence>
   </complexType>
  </schema>
 </wsdl:types>
   <wsdl:message name="getAddressResponse">
      <wsdl:part element="impl:getAddressResponse" name="parameters"/>
   </wsdl:message>
   <wsdl:message name="getAddressRequest">
      <wsdl:part element="impl:getAddress" name="parameters"/>
   </wsdl:message>
   <wsdl:portType name="User">
      <wsdl:operation name="getAddress">
         <wsdl:input message="impl:getAddressRequest" name="getAddressRequest"/>
         <wsdl:output message="impl:getAddressResponse" name="getAddressResponse"/>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="UserSoapBinding" type="impl:User">
      <wsdlsoap:binding style="document" 
            transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="getAddress">
         <wsdlsoap:operation soapAction=""/>
         <wsdl:input name="getAddressRequest">
            <wsdlsoap:body use="literal"/>
         </wsdl:input>
         <wsdl:output name="getAddressResponse">
            <wsdlsoap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="UserService">
      <wsdl:port binding="impl:UserSoapBinding" name="User">
         <wsdlsoap:address location="http://localhost:9080/CurrencyQuote/services/User"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

WSDL2WS ツールを使用して、以下の 2 つのクラスの CPP ファイルを取得します。

  • Web サービス・クライアントのスタブを表すクラス
  • 複合型Address を表すクラス

RPG から C++ オブジェクトを操作する手間を省くには、C ラッパーを使用してください。このサンプルでは、.h ファイルに住所を表す基本構造を定義しています。.h ファイル内の構造定義で C ラッパーを使用すると、RPG コードと生成されるコードの疎結合が実現します。RPG で操作されるデータ構造を定義すれば、一層制御しやすくなります。リスト 8 を見るとわかるように、.h ファイル内ではストリングは String やメモリー内のポインターではなく、文字の配列として表されます。

リスト 8. .h ファイル
#if !defined(__CURRENCYQUOTEBEAN_CLIENTSTUB_H__INCLUDED_)
#define __CURRENCYQUOTEBEAN_CLIENTSTUB_H__INCLUDED_

#include "user.hpp";

struct MyAddress
{
        int number;
        char street[256];
        char town[256];
        char zipcode[256];
};

MyAddress * getAddress2(int id);

int getAddressPointer(int id, MyAddress* myAddress);

#endif /* !defined(__CURRENCYQUOTEBEAN_H__INCLUDED_) */

リスト 9 に、C ラッパーの構造を示します。

リスト 9. C ラッパーの構造
#include <iostream>
#include <string.h>


#include "wrapper2.h";

using namespace std;
#pragma map (getAddress2(int), "GETADDRESS2")
        
MyAddress * getAddress2(int id)
{
        User * user = new User("http://9.212.15.63:9080/CurrencyQuote/services/User") ;
        //allocate Memory for the structure
        MyAddress * myaddr = (MyAddress*) malloc(sizeof(MyAddress));
        //invoke the service
Address * address  = user->getAddress(id);
        xsd__string str = address->street;
        //copy the attribute from the web service result to the structure
myaddr->number = address->number;
        strcpy(myaddr->street,address->street);
        strcpy(myaddr->town,address->town);
        strcpy(myaddr->zipcode,address->zipCode);

        return myaddr;    
}

RPG コードには、住所のやりとりに使用する構造を定義する必要があります。リスト 10 に示すように、ストリングは 256 文字の配列として表されます。

リスト 10. C ラッパーの構造
     H NOMAIN

      *
      * Following are the data fields for the RPG program

     DpRtnData         S             16*
     DADDRESS          DS                  BASED(pRtnData)
     D  number                       10I 0
     D  street                      256A
     D  town                        256A
     D  zipcode                     256A

     Did               S              8S 0
     Drc               S              5I 0


     Dgetaddress       PR            16*   EXTPROC('GETADDRESS2')
     D P@Id                           8S 0

     DgetaddressA      PR           256A

      *
      * Following prototype describes the C procedure being called
     PGetAddressA      B                   EXPORT
     DgetaddressA      PI           256A


      *
      * Code
     C                   eval      id = 10
     C                   eval      pRtnData = getaddress(id)
     C                   return    Street

     PGetAddressA      E

まとめ

この記事を読んで、i5/OS® 上の RPG、COBOL、そしてあらゆる ILE プログラムを Web サービスのコンシューマーにする一般的な方法がわかったはずです。このような技術上の可能性を持つ ILE プログラムは、会社の内外で公開された既存のサービスをその実装と関係なく再利用することができます。

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services
ArticleID=276893
ArticleTitle=System i で RPG または COBOL プログラムから Web サービスを使う
publish-date=05212007