System API プログラミングの基礎 (1) - 2つの結果取得方法 -

最近の iSeries の OS の進化の中で、あまり目立ちませんが、おそらく一番充実してきているのが System API のような気がします。

新機能などは、まず API の形で提供され、後のリリースでほとんどのその API を使ったユーザープログラムのような形でより使いよい形のコマンドやメニュー等がサポートされてきています。たとえば、クラスタリングなどがそうですね。

そこで今回は System API を使ったプログラミングについて、ごくごく簡単な入門編といったかんじの紹介です。


System API プログラミングの敷居の高さ

初めて System API を使ったプログラミングをしようとすると、たいていの場合まず、圧倒的にいろんな API があって、しかもちょっと見てみるとどうやって結果を取得するか、どうも結果を取得するのにさらに API を使う必要がありそうだ、などわからないことがいろいろあります。
何をどういう使ったらいいか、どういう風にコーディングしたらよいか、わからないことだらけです。

今回はひとつの切り口として、結果をどうやって取得するか、に注目してみました。

System API の二つのタイプ

System API を使用してシステムの情報を取得するやり方にはいくつかのタイプがあります。
インフォメーション・センターを見ると、以下のように大きく 2つのやり方があることがわかります。

User spaces and receiver variables

APIs that return information to a caller generally return the information in a user space (used by list APIs) or a receiver variable (used by retrieve APIs).

たとえば、「活動ジョブの検査」で紹介したプログラムのように、ユーザースペースというオブジェクトを作成し、そこに情報を受け取るものがあります。(List API と呼ばれています)
つまり、ある情報を得たいために API を呼び出すわけですが、その呼び出しパラメータにユーザースペース名が必須になっているようなタイプの API です。

このような API の場合、ユーザースペースを (ユーザースペースを作成する API を呼び出して) 作成し、目的とする情報を取得するための API をその後に呼び出すと、実行結果がはそこに格納される、という仕組みです。
そのユーザースペースから情報を切り出すためには、ユーザースペースの内容を開始位置と長さを指定して取り出す、という API を実行する必要があります。

また、「ジョブの最終実行 SQL ステートメントの取得」で紹介したような、ユーザースペースなどを介さずに直接APIの戻り値として情報を受け取ってしまうものがあります。(最近はこちらの方が多くなってきているような気がします)

このタイプの API の場合は、プログラムの中にそれを受け取るメモリ領域さえあればいいので、ユーザースペースをいちいち API で操作するようなわずらわしさはありません。
C のような言語の場合、こちらのタイプの方が相性がよさそうです。そういった理由で増えてきてるのかもしれませんね。System API というものは上で触れたように IBM の開発元でさえ使うものですし。

ユーザースペースの扱いと値戻しの注意点

前者の場合、そもそも API が情報を戻す先がユーザースペースと決まっているので、これはもうやり方を覚えるしかありません。QUSCRTUS という API でユーザースペースを作成し、そのユーザースペースを指定して目的の API を呼び出して結果をもらい、結果の入ったユーザースペースに対して QUSRTVUS という API を開始位置と長さを指定して呼び出すことで情報を切り出す、という流れになります。QUSRTVUS については、複数項目がある場合はその度に開始位置を計算して長さをセットして呼び出す、ということを項目がなくなるまでループさせることが必要になります。(実際 [chkactjob] ではそのようにしてますね)

後者の場合は、いちいちそういったユーザースペースの内容を読むための API を呼び出したりする手間がない分、煩雑ではないのですが、戻されてくる値の長さが不定の場合の対処が問題になってしまいます。
C のようにポインタなどを使用して動的にエリアを確保していくことができる言語であれば、戻されてくる値の大きさによって受け取るフィールドを確保し直す、といったようなことができますが、わかりやすさを尊ぶ RPG のような言語ですとちょっと多めにサイズを取っておく、というような対処が妥当なところでしょう。あまり大きなフィールドにしてももったいないですが、受け取る情報によってはそれでも足りないかもしれません。実際、[RTVSQLINF] では大きいフィールドを用意しておくことで対処しています。
ただし、足りないような場合には不測のエラーが起きてしまいます。

コーディングの共通化

こうした値を直接受け取るタイプの API の場合、ポインタとユーザースペースを活用することによって、この受け取り領域のサイズの問題にうまく対処することができます。

ユーザースペースというオブジェクトを使う利点は、単なるプログラムの中のメモリ領域と違って永続性があることと、永続性がある故に属性をセットできることです。
たとえば、初期サイズは適当に決めておいて自動拡張させることも可能です。(16MB まで)

インフォメーション・センターには以下のように載っています。

Following are some of the advantages of using user spaces:

 - User spaces can be automatically extendable.
 - User spaces can be shared across jobs.
 - User spaces can exist across IPLs.

ポインタを使用すると、動的にメモリ領域を確保することが可能になります。ポインタに格納した開始アドレスを起点としたフィールドをあらかじめ定義しておくことによって、単純にフィールドとして使用すること、そのフィールドの取得先の変更をアドレスの計算をするだけでによって簡単におこなうことが可能になる、といったようなある種の二面性を持つことができるようになります。

このポインタを利用して、ユーザースペースを作成し、その開始位置のポインタを同じように開始位置とするフィールドを API の受け取りフィールドとしてやることで、オブジェクトとしてはユーザースペースであるが、プログラムから見るとフィールドである、というように使用することができるのです。

つまり、ユーザースペースを受け取るタイプも戻り値で受け取るタイプも、ほぼ同じロジックで使用することができる、というわけです。

ポインタを利用した戻り値へのユーザースペースの利用

ポインタを使用することによって、ユーザースペース取得用の API (QUSRTVUS) を使用しなくても内容を取得することができます。
リストとして返された複数の項目を順次アクセスするにはやはり開始位置とオフセットの計算が必要になりますが、これはポインタの場合もユーザースペース取得 API もどちらの場合でも必要になってしまうものなので、どのみち同じことですね。

ユーザースペースを作成し、その開始位置のポインタを取得し、自動拡張属性をセットする、という処理は、ユーザースペースを使用する API でも、受け取り値を使用する API でユーザースペースを使用する場合でも、まったく同じ処理になります。
つまり、共用できる、汎用的な処理なわけですね。

汎用的なプロシージャの作成

たとえば、自動拡張可能な属性を持つユーザースペースを作成する処理、そして、プログラムの中で用意したポインタフィールドにそのユーザースペースの開始アドレスをセットする、というような処理をそれぞれ以下のようにサブプロシージャとして実装することができます。

まず、自動拡張可能な属性を持つユーザースペースの作成です。ユーザースペース名は当然必須ですが、権限とテキスト、同名のものが存在したときにリプレイスするか否か、を指定できるようにしています。

ユーザースペースの入手、ということで GetUsrSpc という名前のプロシージャにしてみました。
戻り値は作成できたかできなかったかのリターンコードとしています。

     H NOMAIN                                                                                       
      *                                                                                             
     D GetUsrSpc       PR              N                                                            
     D  Name                         20a                                                            
     D  pubAut                       10a                                                            
     D  Text                         50a                                                            
     D  Replace                      10a                                                            
      *                                                                                             
     P GetUsrSpc       B                   export                                                   
      *                                                                                             
     D GetUsrSpc       PI              N                                                            
     D  Name                         20a                                                            
     D  pubAut                       10a                                                            
     D  Text                         50a                                                            
     D  Replace                      10a                                                            
      *                                                                                             
     D*CrtUsrSpc       PR                  ExtPgm('QusCrtUs')                                       
     D CrtUsrSpc       PR                  ExtPgm('QUSCRTUS')                                       
     D  usName                       20a   CONST                                                    
     D  usAtr                        10a   CONST                                                    
     D  usSize                       10I 0 CONST                                                    
     D  initVal                       1a   CONST                                                    
     D  usPubAut                     10a   CONST                                                    
     D  usText                       50a   CONST                                                    
     D  usReplace                    10a   CONST                                                    
     D  errorCode                          like(APIErr) Options(*NoPass)                            
     D  usDomain                     10a   CONST Options(*NoPass)                                   
      *                                                                                             
     D*ChgUsrSpcA      PR                  ExtPgm('QusCusAt')                                       
     D ChgUsrSpcA      PR                  ExtPgm('QUSCUSAT')                                       
     D  rtnLibName                   10a                                                            
     D  usaName                      20a   CONST                                                    
     D  usaAtr                       64a   Options(*VarSize)                                        
     D  errorCode                          like(APIErr)                                             
      *                                                                                             
     D APIErr          DS                  Qualified                                                
     D  ErrSize                      10i 0 inz(%size(APIErr))                                       
     D  ErrLen                       10i 0 inz(0)                                                   
     D  ErrID                         7a                                                            
     D  rsvd                          1a                                                            
     D  ErrData                   32767a                                                            
      *                                                                                             
     D usAttr          DS                  Align                                                    
     D  rcdCount                     10I 0 inz(1)                                                   
     D  atrKey                       10I 0 inz(3)                                                   
     D  atrLen                       10I 0 inz(%size(extendable))                                   
     D  extendable                    1a   inz('1')                                                 
      *                                                                                             
     D rtnVal          S               N                                                            
      *                                                                                             
     D rtnLibName      S             10a                                                            
      *                                                                                             
     D*HandleErr       PR                                                                           
      *                                                                                             
      /Free                                                                                         
          rtnVal = *on;                                                                             
                                                                                                    
           if pubAut = *blanks ;                                                                    
              pubAut = '*ALL      ' ;                                                               
           endif ;                                                                                  
                                                                                                    
           if Replace = *blanks ;                                                                   
              Replace = '*YES      ' ;                                                              
           endif ;                                                                                  
                                                                                                    
          CrtUsrSpc( Name :                                                                         
                    'extendable' :                                                                  
                     64 :                                                                           
                     x'00' :                                                                        
                     pubAut :                                                                       
                     Text :                                                                         
                     Replace :                                                                      
                     APIErr :                                                                       
                     '*USER     ') ;                                                                
                                                                                                    
          If APIErr.ErrLEN <> 0 ;                                                                   
                                                                                                    
           rtnVal = *off ;                                                                          
        // HandleErr() ;                                                                            
           return rtnVal ;                                                                          
                                                                                                    
          endif ;                                                                                   
                                                                                                    
          ChgUsrSpcA( rtnLibName :                                                                  
                      Name :                                                                        
                      usAttr :                                                                      
                      APIErr) ;                                                                     
                                                                                                    
            If APIErr.ErrLEN <> 0 ;                                                                 
                                                                                                    
             rtnVal = *off ;                                                                        
         //  HandleErr() ;                                                                          
                                                                                                    
            endif ;                                                                                 
                                                                                                    
            return rtnVal ;                                                                         
                                                                                                    
      /End-Free                                                                                     
      *                                                                                             
     P GetUsrSpc       E

こんなかんじで呼び出します。
ちゃんと処理ができているかどうかのテストプログラムでもあります。

     D RtnCode         S               N                                                            
      *                                                                                             
     D pName           S             20a                                                            
     D ppubAut         S             10a                                                            
     D pText           S             50a                                                            
     D pReplace        S             10a                                                            
      *                                                                                             
     D GetUsrSpc       PR              N                                                            
     D  Name                         20a                                                            
     D  pubAut                       10a                                                            
     D  Text                         50a                                                            
     D  Replace                      10a                                                            
      *                                                                                             
      /Free                                                                                         
          pName =    'USOBJ     QTEMP     ' ;                                                       
          ppubAut =  '*ALL      ' ;                                                                 
          pText =    '' ;                                                                           
          pReplace = '*YES      ' ;                                                                 
                                                                                                    
          RtnCode =                                                                                 
          GetUsrSpc (pName                                                                          
                    :ppubAut                                                                        
                    :pText                                                                          
                    :pReplace) ;                                                                    
                                                                                                    
          *inLR = *on ;                                                                             
          Return ;                                                                                  
                                                                                                    
      /End-Free

次に作成したユーザースペースの開始アドレスを取得します。

作成したユーザースペース名とポインタを引数に取り、それらを結びつける SetUsrSpc というプロシージャを作成してみました。GetUsrSpc とセットで使用するのが自然なので、これもスタイルを合わせて戻り値はセットできたかできなかったかのリターンコードとしています。

     H NOMAIN                                                                                       
      *                                                                                             
     D SetUsrSpc       PR              N                                                            
     D  Name                         20a                                                            
     D  rtnPtr                         *                                                            
      *                                                                                             
     P SetUsrSpc       B                   export                                                   
      *                                                                                             
     D SetUsrSpc       PI              N                                                            
     D  Name                         20a                                                            
     D  rtnPtr                         *                                                            
      *                                                                                             
     D SetUsPtr        PR                  ExtPgm('QUSPTRUS')                                       
     D  usName                       20a   CONST                                                    
     D  usPtr                          *                                                            
     D  errorCode                          like(APIErr) Options(*NoPass)                            
      *                                                                                             
     D APIErr          DS                  Qualified                                                
     D  ErrSize                      10i 0 inz(%size(APIErr))                                       
     D  ErrLen                       10i 0 inz(0)                                                   
     D  ErrID                         7a                                                            
     D  rsvd                          1a                                                            
     D  ErrData                   32767a                                                            
      *                                                                                             
     D rtnVal          S               N                                                            
      *                                                                                             
     D*HandleErr       PR                                                                           
      *                                                                                             
      /Free                                                                                         
          rtnVal = *on;                                                                             
                                                                                                    
          SetUsPtr ( Name :                                                                         
                     rtnPtr :                                                                       
                     APIErr ) ;                                                                     
                                                                                                    
          If APIErr.ErrLEN <> 0 ;                                                                   
                                                                                                    
           rtnVal = *off ;                                                                          
        // HandleErr() ;                                                                            
                                                                                                    
          endif ;                                                                                   
                                                                                                    
            return rtnVal ;                                                                         
                                                                                                    
      /End-Free                                                                                     
      *                                                                                             
     P SetUsrSpc       E

SetUsrSpc の呼び出し方です。SetUsrSpc をテストするためのテストプログラムですね。
GetUsrSpc のテストプログラムの拡張になっています。

     D RtnCode         S               N                                                            
      *                                                                                             
     D pName           S             20a                                                            
     D ppubAut         S             10a                                                            
     D pText           S             50a                                                            
     D pReplace        S             10a                                                            
      *                                                                                             
     D prtnPtr         S               *                                                            
      *                                                                                             
     D GetUsrSpc       PR              N                                                            
     D  Name                         20a                                                            
     D  pubAut                       10a                                                            
     D  Text                         50a                                                            
     D  Replace                      10a                                                            
      *                                                                                             
     D SetUsrSpc       PR              N                                                            
     D  Name                         20a                                                            
     D  rtnPtr                         *                                                            
      *                                                                                             
      /Free                                                                                         
          pName =    'USOBJ     QTEMP     ' ;                                                       
          ppubAut =  '*ALL      ' ;                                                                 
          pText =    '' ;                                                                           
          pReplace = '*YES      ' ;                                                                 
                                                                                                    
          RtnCode =                                                                                 
          GetUsrSpc (pName                                                                          
                    :ppubAut                                                                        
                    :pText                                                                          
                    :pReplace) ;                                                                    
                                                                                                    
          RtnCode =                                                                                 
          SetUsrSpc (pName                                                                          
                    :prtnPtr) ;                                                                     
                                                                                                    
          *inLR = *on ;                                                                             
          Return ;                                                                                  
                                                                                                    
      /End-Free

この 2つのプロシージャを使って、「ジョブの最終実行 SQL ステートメントの取得」を書き直してみました。
長くなるので、別の記事にしてあります。「System API プログラミングの基礎 (2) - 戻り値をユーザースペースに置き換える -」へどうぞ。

[Top Pageに戻る]

Ads by TOK2