HTTPサーバーとWeb開発

64. Ajax + RPG によるプログラミングの実例

それでは具体的に Ajax を使う CGI を RPG で開発した事例を紹介しよう。
下記は実行時の画面であって「次頁→」「前頁←」ボタンでレコードの前後退を行うことが
できるという「商品マスターの照会」である。

【 Ajax を組み込んだ HTML の例 】
<html>
<head><title>商品マスターの照会</title>
<script type="text/javascript" src="/AS400-NET.USR/JS/PROTOTYPE.JS"></script>
<script type="text/javascript"><!--
var OPT = "NXT";
function Load_Data(httpObj){
  document.SNDFORM.SHCODE.value = httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data;
  document.SNDFORM.SHNAME.value = httpObj.responseXML.getElementsByTagName("SHNAME").item(0).firstChild.data;
  document.SNDFORM.SHTANK.value = httpObj.responseXML.getElementsByTagName("SHTANK").item(0).firstChild.data;
  document.SNDFORM.SHSCOD.value = httpObj.responseXML.getElementsByTagName("SHSCOD").item(0).firstChild.data;
}
function INZSR(){
  var CGI = "/cgi-bin/AJX001.PGM?OPT=" + OPT + "&SHCODE=" + document.SNDFORM.SHCODE.value;
  CGI+="&dummydate="+new Date().getTime();
  new Ajax.Request(CGI, {method :"GET", onComplete:Load_Data});
}
// --></script>
</head>
<body>
<style type="text/css">body, table, td {font-size: 10pt;}</style>
<body>
<center>
<form name="SNDFORM">
<h3>商品マスターの照会</h3>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="1"><td width="180"></td><td></td></tr>
<!-- // START -------------------------------------------- // -->
<tr><td><font size="-1">商品コード</font></td>
<td><font size="-1"><input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12"></font></td></tr>
<tr><td><font size="-1">商品名</font></td>
<td><font size="-1"><input id=SHNAME type="TEXT" name="SHNAME" maxlength="24" size="32"></font></td></tr>
<tr><td><font size="-1">単価</font></td>
<td><font size="-1"><input id=SHTANK type="TEXT" name="SHTANK" maxlength="8" size="10"></font></td></tr>
<tr><td><font size="-1">品種コード</font></td>
<td><font size="-1"><input id=SHSCOD type="TEXT" name="SHSCOD" maxlength="4" size="5"></font></td></tr>
<!-- // END ---------------------------------------------- // -->
<!-- // 実行ボタン // -->
<tr><td colspan="2"><br>
<input type="SUBMIT" value=" 実行 ">
<input type="button" value=" 次頁→ " onClick="OPT='NXT';INZSR();">
<input type="button" value=" 前頁← " onClick="OPT='PRV';INZSR();">
</td></tr></table>
</form>
</center>
<script language="JavaScript">INZSR()</script>
</body>
</html>
【 解説 】

雑誌アイマガジン誌ではページの関係上、HTML の全コードを示すことができなかったが、
これがHTML の全容である。
最初に

<script type="text/javascript" src="/AS400-NET.USR/JS/PROTOTYPE.JS"></script>

によって PROTOTYPE.JS という Ajax のライブラリーを挿入していることに注意して欲しい。
次に body タグの最後に

<script language="JavaScript">INZSR()</script>

とある記述によって、この body タグが読み込まれたら INZSR というユーザー独自の
JavaScript 関数を起動することを指示している。
INZSR という関数は、

function INZSR(){
  var CGI = "/cgi-bin/AJX001.PGM?OPT=" + OPT + "&SHCODE=" + document.SNDFORM.SHCODE.value;
  CGI+="&dummydate="+new Date().getTime();
  new Ajax.Request(CGI, {method :"GET", onComplete:Load_Data});
}

という内容であり、

/cgi-bin/AJX001.PGM?OPT=NXT&SHCODE=NV-CF1&dummydate=20070727192030

というような 要求をサーバー(System i) に送信する。
ここで要求URL の末尾に dummydate という変数を送信しているのは、この要求がブラウザに
よってキャッシュされないようにするためである。
もしこの毎回、異なる値となるはずのdummydate =「日付 + 時刻」という変数が付加されて
いなければ最初だけは要求はサーバー(system i ) に送信されるが、送信されて得られた結果は
ブラウザへキャッシュされる。
2回目以降では、この要求に対する結果がキャッシュされているので、再びサーバー(System i)
に要求が送信されることはない。
つまり、要求は一度だけしか送信されることはない。
このように要求そのものもキャッシュされるのは具合が悪く、要求はキャッシュされることなく毎回、
必要なときには送信されなければならないのでキャッシュを防止するためにdummydate を付加
しているのである。dummydate の代わりに乱数であってもよい。
またオプションとしての変数 : OPT は NXT=次のレコードPRV=前のレコード を意味する値
が入る。初めて実行される場合は

var OPT = "NXT";

として記述されているので OPT= NXT としてサーバーへ送信されることになる。
しかし、前後退ボタンによって OPT は次のように変化する。

<input type="button" value=" 次頁→ " onClick="OPT='NXT';INZSR();">
<input type="button" value=" 前頁← " onClick="OPT='PRV';INZSR();">

さて、INZSR は、GETメソッドによって上記のような要求がサーバーに送られて、サーバーからの
受信が完了すると Load_Data という関数を実行するように指示されている。

関数 : Load_Data とは

function Load_Data(httpObj){
  document.SNDFORM.SHCODE.value = httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data;
  document.SNDFORM.SHNAME.value = httpObj.responseXML.getElementsByTagName("SHNAME").item(0).firstChild.data;
  document.SNDFORM.SHTANK.value = httpObj.responseXML.getElementsByTagName("SHTANK").item(0).firstChild.data;
  document.SNDFORM.SHSCOD.value = httpObj.responseXML.getElementsByTagName("SHSCOD").item(0).firstChild.data;
} 

のような関数であって、商品コードの HTML タグでは

<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12">

と記述されているので、この id は SHCODE という識別名である。
document.SNDFORM.SHCODE.value = .... とは、SHCODE という id タグに値をセットする
ことを示している。
何の値をセットするのかと言えば、それはサーバーから取得した XML の項目の値であって
サーバーから送られてきた次のような XML :

<?xml version="1.0" encoding="Shift_JIS"?>
<result>
<SHCODE>NV-BS30S</SHCODE>
<SHNAME>目次ビデオ</SHNAME>
<SHTANK>0165000</SHTANK>
<SHSCOD>0002</SHSCOD>
</result>

の項目(エレメント) を SHCODE という名前によって識別して取り出して、その値を指示しているのが

httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data;

である。

これによってブラウザのメモリ上では、

<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12">

は、

<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12" value="NV-BS30S">

のように書き換えられることになる。残りの項目についても同様である。

それでは次に Ajax の相手をする RPG による CGI の実例を紹介する。

【 Ajax を処理する RPG の例 】
0001.00 H DATEDIT(*YMD/) COPYRIGHT('(C) OfficeQuattro Co,.Ltd Japan 2009-')                            
0002.00 F**********  商品マスター商会サンプル     *****************************                        
0003.00 FSHOHIN    IF   E           K DISK    EXTFILE(SHOHIN_LIB)                                      
0004.00 F**********************************************************************                        
0005.00                                                                                                
0006.00 F*---------------------------------------------------------------------                        
0007.00 F* CRTMOD=CRTRPGMOD QTEMP/AJX001 SRCFILE(PGMRLIB/QRPGLESRC) +                                  
0008.00 F*           OPTION(*NOXREF *NOSECLVL *NOEXPDDS *NOSHOWCPY) AUT(*ALL)                          
0009.00 F* CRTPGM=CRTPGM    CGIBIN/AJX001 MODULE(QTEMP/AJX001) +                                       
0010.00 F*          BNDSRVPGM(ASNET.COM/RPGENGINE5) ACTGRP(*NEW) AUT(*ALL)                             
0011.00 F* USER=QTMHHTTP                                                                               
0012.00 F* TYPE=RPG-CGI                                                                                
0013.00 F* HTML=/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM                                               
0014.00 F* LIBL=CGIBIN     QTRFIL     QGPL       QTEMP      ASNET.COM                                  
0015.00 F* CALL=HTTP://218.44.135.18/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM                           
0016.00 F* TEXT= 商品マスターファイル                                                                  
0017.00 F* SESSION=*NO                                                                                 
0018.00 F* KEEP-ALIVE=*NO                                                                              
0019.00 F* SMARTCONNECTION=*NO                                                                         
0020.00 F* CCSID=5026                                                                                  
0021.00 F* CHARSET=SHIFT_JIS                                                                           
0022.00 F* 作成日 :CRTDATE=2015/05/12  時刻 11:17:10   BY QTMHHTTP                                     
0023.00 F* 変更日 :CHGDATE=2015/05/12  時刻 11:17:10   BY QTMHHTTP                                     
0024.00 F*---------------------------------------------------------------------                        
0025.00                                                                                                
0026.00 D*( プログラム状況データ構造 )                                                                 
0027.00 D INFDSP         SDS                                                                           
0028.00 D                              512A                                                            
0029.00  /COPY ASNET.USR/QRPGLESRC,INFDSP                                                              
0030.00  /COPY ASNET.USR/QRPGLESRC,PROTOTYPE6                                                          
0031.00  /COPY ASNET.USR/QRPGLESRC,ERRCONST                                                            
0032.00 D SHOHIN_LIB      S             21    INZ('QTRFIL/SHOHIN')                                     
0033.00 D XML             PR                                                                           
0034.00 D   FIELD                       10A   VALUE                                                    
0035.00 D   VALUE                      256A   VALUE                                                    
0036.00 D OPT             S              3A                                                            
0037.00 D XMLBUF          S          32767A   INZ VARYING                                              
0038.00 D EBC             S              5I 0 INZ(1)                                                   
0039.00 D XML_VER         C                   CONST('<?xml version="1.0" -                             
0040.00 D                                     encoding="Shift_JIS" ?>')                                
0041.00 C*-----------------------------------------------------                                        
0042.00 C     SETKEY        KLIST                                                                      
0043.00 C                   KFLD                    SHCODE                                             
0044.00 C******************************************************                                        
0045.00 C*               メイン・ルーチン                                                              
0046.00 C******************************************************                                        
0047.00 C*( GET パラメータから OPT,SHCODE を取得 )                                                     
0048.00 C                   EVAL      OPT    = CGIPARM('OPT')                                          
0049.00 C                   EVAL      SHCODE = CGIPARM('SHCODE')                                       
0050.00 C*( OPT によって SHOHIN を READ または READP )                                                 
0051.00 C                   SELECT                                                                     
0052.00 C                   WHEN      OPT = 'NXT'                                                      
0053.00 C     SHCODE        SETGT     SHOHIN                                                           
0054.00 C                   READ      SHOHIN                                 50                        
0055.00 C                   WHEN      OPT = 'PRV'                                                      
0056.00 C     SHCODE        SETLL     SHOHIN                                                           
0057.00 C                   READP     SHOHIN                                 50                        
0058.00 C                   ENDSL                                                                      
0059.00 C*( XML を作成 )                                                                               
0060.00 C                   EVAL      XMLBUF = XML_VER                                                 
0061.00 C                   EVAL      XMLBUF += '<RESULT>'                                             
0062.00 C                   CALLP     XML('SHCODE':SHCODE)                                             
0063.00 C                   CALLP     XML('SHNAME':SHNAME)                                             
0064.00 C                   CALLP     XML('SHTANK':%EDITC(SHTANK:'J'))                                 
0065.00 C                   CALLP     XML('SHSCOD':SHSCOD)                                             
0066.00 C                   EVAL      XMLBUF += '</RESULT>' + X'00'                                    
0067.00 C*( ブラウザへ XML を送信 )                                                                    
0068.00 C                   MOVEL     XMLBUF        BUFF            512                                
0069.00 C                   CALLP     SEND(%ADDR(BUFF):EBC)                                            
0070.00 C                   SETON                                        LR                            
0071.00 C                   RETURN                                                                     
0072.00  ***************************************************************                               
0073.00  *               XML    プロシージャーの定義                   *                               
0074.00  ***************************************************************                               
0075.00  *   XML : <FIELD> ... </FIELD> を作成する                                                     
0076.00 P XML             B                                                                            
0077.00 D XML             PI                                                                           
0078.00 D   FIELD                       10A   VALUE                                                    
0079.00 D   VALUE                      256A   VALUE                                                    
0080.00 C                   EVAL      XMLBUF += '<' + %TRIMR(FIELD) + '>' +                            
0081.00 C                             %TRIMR(VALUE) + '</' + %TRIMR(FIELD)+ '>'                        
0082.00 P XML             E                                                                            
【 解説 】

このわずか 82 ステップに過ぎない RPG は、非常に小さなものであるが深い洞察を与えるに
十分な機能を含んでいる。
詳細は雑誌アイマガジン vol.3 (2007年7月27日号) を参照されることにして頂いて、
この CGI の目的は

<?xml version="1.0" encoding="Shift_JIS"?>
<result>
<SHCODE>NV-BS30S</SHCODE>
<SHNAME>目次ビデオ</SHNAME>
<SHTANK>0165000</SHTANK>
<SHSCOD>0002</SHSCOD>
</result>

のような XML を送信することだけである。
RPG の中には HTML に関しては、一切の記述を含んでいない。
つまり、このことがデータとフレームが完全に分離されていることを意味している。
CGI の開発者にとっては HTML や JavaScrit の知識は一切、必要でない。
このわずかな XML に関する知識があればよいだけである。

Ajax を実行して体験してみよう !!

読者は今すぐこの場で 上記の Ajax のサンプルを起動して実行してみることができる。
http://218.44.135.18/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM
をクリックすると次のような画面が表示されるはずだ。

「次頁→」と「前頁←」ボタンを押して挙動を確かめて欲しい。
HTML フレーム部分は何のチラツキもなく、データ部分だけが素早く入れ替わって更新されて
いるはずである。これこそが Ajax の動作である。
これは従来のリッチクライアント製品と同じ動作であり、しかもリッチクライアントのような再配布
の手間もなく、HTML の修正も非常にカンタンである。
おまけにリッチクライアント製品の有償購入の必要もない。
Ajax そのものが Web2.0 としての最新鋭のリッチクライアントであるのだ。
Ajax の登場によってリッチクライアント製品は、その終焉を迎えることになった。
米国では既にかなりの数のソフトウェア製品が Ajax の採用を行っており、独自のライブラリーの
配布なども行っている。
今回、紹介したサンプルはわかりやすくするために非常に簡単なものであるがAjax を使えば、
従来、不可能と思われてきた機能が次々と実現することができる。
次の項では Ajax の応用事例をご紹介しよう。