![]() ![]() ![]() ![]() ![]() ![]() | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
第5章
Telnetベースの計算は、他の計算(他の端末ベースの処理を含む)と次のような多くの重要な点で異なります。
これらの要因によって、(アクションモデルを通じて)自動的にTelnetと対話することが困難になる可能性があります。この章では、eXtendアクションモデルに関連する一般的な(問題となる可能性がある)Telnet処理の対処方法をいくつか紹介します。
この章の大部分を理解するためには、第4章「Telnetアクションの実行」を読み、アクションモデルのプログラミング構造(Repeat Whileアクションを利用したループなど)を理解しておくことが必要です。また、ECMAScriptの使用経験も必要になります。
Telnet計算では、頻繁に複数の画面にまたがるデータセットをキャプチャする必要があります。画面に「1/4ページ」などと表示される場合、(42ページの「Telnet固有の式ビルダ拡張」で説明されているECMAScriptのScreenオブジェクトメソッドの1つを使用して)その場所から画面を検証したり、すべての画面を反復するループを組み込むことは簡単です。ただし、何ページの画面にデータがあるか不明な場合があります。また、最後の画面になると画面の上部または下部に表示される[戻る](または[終了]、あるいはその他のメッセージ)の代わりに[次へ]などが表示されていることが唯一の手がかりの場合もあります。その他、「レコード」の数の合計がわかっていて、「1画面あたり」に表示されるレコードの数を(見た目で)判断でき、それによって情報が含まれる画面の合計を計算できる場合もあります。
ここで重要なことは、(可能性として)クエリが複数の画面にわたる情報になる場合、Repeat/Whileアクションを使用して情報が含まれているすべての画面を繰り返したり、情報が含まれている画面が終了した場合に停止したりする準備をしておく必要がある、ということです。繰り返しを停止する場合、それを判断する特別なカスタムロジックを作成する必要があります。ロジックは、次の1つまたは複数の方法により異なる可能性があります。
明らかに、使用する方法は適用するTelnetアプリケーションの実装により異なります。アプリケーションによっては、ブランクのレコードが表示されるまで画面を繰り返す必要があるものや、同じ方法が適当でないものがあります。
このうち2つの条件を組み合わせたアクションモデルの例について、後で詳しく説明します。
Telnetアプリケーションでは、通常、複数画面セットの最後の画面に前の画面からのデータが「当てられて」います。全画面の表示がそのように維持されています。
次の2つのスクリーンショットについて考えます。前のスクリーンショットには、6画面にわたる情報を返すクエリの最後から2番目の画面に当たる情報が表示されています。黒くハイライトされているステータス行(上から2行目)に「43 entries found, entries 33-40 are:」とあり、その後に行レコードがあることに注目してください。合計のデータセットが43レコードあり、最後から2番目の画面が40番目のレコードで終わっているため、次の(最後の)画面にはレコード41からが表示されると予想できます。最後の画面はその次のスクリーンショットに表示されているような画面になります。 画面にはレコード36から43が表示されています。つまり、「前の」画面から5つのレコード(36から40)が含まれています。ほとんどの場合、この余分なデータは必要ありません。問題は、このような余分なデータを検出、削除する方法です。
ECMAScriptでは、重複していないレコードのリストを簡単に管理できる便利な方法が提供されています。コツは、生の(初期化されていない)オブジェクトを作成して、プロパティとしてレコード名を添付することです。1つのオブジェクトに同じの名前のプロパティを2つ設定することはできないため、プロパティ名としてレコード名を割り当てると、そのオブジェクトのプロパティリストが重複していないレコード名のリストになります。
わかりやすく説明するために簡単な例を使用します。重複する項目がいくつか含まれたアレイがあるとします。
var myArray = new Array( "Tom","Amy","Greg","Tom","Amy");
このアレイの重複する項目を削除するために、生のオブジェクトにプロパティを割り当てることができます(プロパティ名はアレイの値と等しくなります)。
var myObject = new Object(); // create a bare object
for (var i = 0; i < myArray.length; i++) // loop over array
{
var arrayMember = myArray[ i ]; // fetch array member
myObject[ arrayMember ] = true; // create the property
}
// Now obtain all property names
// in a new, unduplicated array:
var uniqueValues = new Array();
var n = 0; // counter
for (var propertyName in myObject) // enumerate property names
uniqueValues[ n++ ] = propertyName;
//これで 「uniqueValues」に含まれるのは「Tom」、「Amy」、「Greg」
のみ
この要領は、後で説明するTelnetアプリケーションの例で活用します。
これまで取り上げてきた複数の条件を組み合わせるTelnetコンポーネントの例について考えます。ホストアプリケーションは、ある大学の図書館システムで本を検索するサービスです。この例では、著者名を指定する入力ドキュメントを使用します。その名前を基に、著者名によって利用可能な本のタイトルすべてのライブラリを照会し、出力DOMへの結果をキャプチャします。出力ドキュメントには、タイトルが重複していないリストを含めるようにします。
この例では次の内容について説明します。
アクションモデルのメインループに関するロジックは、次のようにまとめることができます(擬似コード)。
Determine the number of records-per-screen
While (true) // enter a "forever" loop
Fetch a record
IF Record is Valid // i.e., not blank
Write data to Output DOM
IF Screen has been completely processed
IF this is not the final screen
Fetch next screen
ELSE BREAK // final screen processed
ELSE BREAK // blank record reached
この例のアクションモデルにおける最初の部分は、前の例(50ページの「Telnetセッションの記録」を参照)ので作成したアクションとほぼ同様です。ただし、この例では著者名としてThomas Aquinasを使用する点が例外です。初期アクションは、「Thomas Aquinas」という著者名検索を実行するために必要な画面の確認アクションおよびバッファの送信アクションのみです。
検索結果の初期画面は次のようになります。
2行目の初めに、見つかったレコード数(エントリ)が表示されています。関数アクションを使用すると、この情報をキャプチャできます。
この3行のスクリプトにより、ローカル変数line2
にある2行目がすべて取得され、行頭のスペースは削除され、スペース文字がある行は分割されます(結果アレイの0番目のメンバーを変数totalHits
にキャプチャします)。それから、マップアクションを使用して「total hits (ヒット総数)」の数を出力DOMに書き込みます。
この時点で、「ヒット総数」の数をメインループの基本として使用できます。ただし、「ヒット総数」の情報がすべてのTelnetホストによって最初の応答画面に報告されるわけではないため、図の問題からこの段階は省略します。ただし、この特定のアプリケーションにより1画面あたりのレコード数(2行目)が報告される機能は活用します。 繰り返しになりますが、便利なECMAScriptのプログラミングを使用して、1画面あたりのレコード数に関する情報をランタイム時にダイナミックに取得することも可能です。また、画面を目で調べた後、この値をハードコード化することもできます。
注記: そのうち、1画面あたりのレコード数をハードコード化するか、ランタイムロジックを適用するか判断が必要になります。Telnetアプリケーションでは、重要な画面の特徴をすべてダイナミックに判断することは困難です。最終的なアクションモデルでは、ホストアプリケーションの動作に関して常に必要となる知識もあります。
ECMAScript変数booksPerScreen
に、1画面あたりのレコード数を保存します。この例では、1画面あたり8つのレコードがあります。
メインループを作成する前に、出力DOMでノードを作成する際に使用するインデックス変数を設定する必要があります。このインデックス(bookNumber
)は、1から始まり出力に本のタイトルをキャプチャするたびに1増加します。このインデックスが「0」ではなく「1」から始まる理由は、DOMノードでは1からインデックス処理が行われるためです。bookNumber
を使用して、ノードにインデックスを作成します。
また、(関数アクションの)ECMAScript式を使用して、ブランクのECMAScriptオブジェクトを作成します。
var bookTable = new Object();
前で説明したように、本のタイトルをこのオブジェクトのプロパティ名として保存することにより、「重複しない」レコードのリストを維持できます(「余分なデータの処理」を参照)。
ループを作成するには、アクションモデルでRepeat Whileアクションを使用します(マウスを右クリックして[新規アクション]>[繰り返し]>[Repeat While]の順に選択します)。ダイアログボックスの設定は、次のようになります。
条件をtrueに設定すると、実質的には無限のループを作成することになります。ループの終了条件は2重になっています。
後の条件は、無限ループを終了するための適切かつ安定した方法です。 例として挙げた図書館照会システムだけでなく、さまざまなTelnetアプリケーションで使用できます。
インデックス変数i
は、0からbooksPerScreen - 1
の間で変化し、2つの役割があります。
ループ前に追加の設定コードを使用して、現在画面をキャッシュに保存します。ループが開始される前すぐに、次の関数アクションステートメントを含めます。
previousScreen = Screen.getTextAt(1,1,Screen.getColumnCount() * Screen.getRowCount());
変数previousScreen
により、最後に表示された画面のコンテンツがキャッシュされるため、新たに取得された画面を確認することができます。新たに取得された画面に、処理された画面と同一のコンテンツが含まれる場合、最後の画面に到達したこと(そしてループを終了する必要があること)がわかります。
ここでは、アクションモデルのメインループで実際に行われることを見てみます。
ループの最初の部分を次の図に示します。図の内容は、実際の動作とほぼ同様です。
ループ内の最初のアクションは関数アクションで、カラム9の行4 + i
から始まる53文字を取得します。目的の行は4から11まで(11を含む)で、これはホストによって品目が報告されるゾーンです。i
は0から7まで変化するため、コードで「4 + i」を行オフセットとして使用できます。
レコードの取得後、処理を実行する前に検証を行います。レコードが取得されるゾーンがブランクでない場合に限り、ループが続行されます。次の決定式を含むDecisionアクションを使用します。
Screen.getTextAt( 4 + i, 9, 53) != (new Array(53) ).join(" ")
式の右側のステートメントは、「新しい、53文字長のブランクのアレイを作成し、区切り文字としてスペース文字1文字を使用しアレイのメンバーをともに結合することによって文字列に変換する」という意味です。各アレイのメンバーはnullであるため、実質的にはこれにより行に53のスペース文字を含む文字列が作成されます。この文字列と画面の文字列を比較して、ブランクのレコードが見つかったかどうか判断できます。
決定アクションのTRUEブランチでは、取得した本のタイトルがすでに見つかっているものかどうか即時に確認します(重複を避けます)。本のタイトルはbookTable
オブジェクトにプロパティ名として保存する方法を使用しているため(前の説明を参照)、次の式に対して決定アクションを実行するだけで既出の本かどうか確認できます。
bookTable[ bookTitle ] == null
このステートメントがtrueの場合、bookTable
オブジェクトにはbookTitle
の文字列に一致するプロパティ名がない、ということになります。その場合は、先に進んでマップ操作を実行できます(そうでない場合は、不成立のため動作が繰り返されます)。
この判断に関するTRUEブランチでは、bookTable[ bookTitle ]
にtrueとマークを付けます。bookTable
に新しいnullでないプロパティが割り当てられます。次に、インデックス番号および本のタイトルを出力DOMの新しいノードにマップします。次の式を適用することにより、マップできます。
Output.createXPath("InquiryResponse/Books[$bookNumber]/Title")
マップの際、bookNumber
で作成中のインデックスを使用して、InquiryResponse/BooksにTitleという要素名を持つ新しいノードインスタンスを作成することができます。
これで、bookNumber
が増えます。
ループの後半部分では、新しい画面を取得するかどうかを確認します。取得する場合は、[バッファの送信]コマンドを実行して、ホストに対しページを次の画面に進めるよう命令します。
新しい画面が取得されると、すぐにそのコンテンツを文字列変数thisScreen
にキャプチャします。そして、thisScreen
とpreviousScreen
を比較する決定アクションを実行します。2つの値が等しい場合は、ブレークアクションを使用してループを終了します。等しくない場合は、式が不成立のため実行を続けます。
注記: 図に表示されている画面の確認アクションに対する最小待機時間の設定に注意してください。最小待機時間が短い場合に実行条件がtrueになると、誤って画面をスキップして条件が満たされる前にループを抜けてしまう可能性があります。
まだ実行中の場合は、i
(行のインデックス変数)をリセットし、次のラウンドの準備でpreviousScreen
にthisScreen
を入力します。
ループが終了すると、出力DOMは次の図のように表示されます。
DOMに、この著者に対して見つかったすべてのタイトルが順番に表示されます。最後の画面のデータに、以前の画面と重複する情報が含まれている場合でも、DOMには重複するタイトルは含まれません。
タイミングの呼び出しについて個別のアクション(またはアクションのブロック)を調整することにより、アクションモデルの動作をミリ秒単位で調整することができます。
アクションモデルをクリックし、時間を調整するアクションのすぐ前に新しい「関数アクション」を挿入します(マウスを右クリックして[新規アクション]>[関数]の順に選択します)。
一時DOMの要素にendTime \x96 startTime
をマッピングする「マップアクション」を作成します(マウスを右クリックして[新規アクション]>[マップ]の順に選択します)。
アクションモデルの処理を詳しく分析すると、実行時間の大部分が画面の確認アクションに費やされていることがわかります。(画面の確認が150ミリ秒以下で実行されることはほとんどありません)。これにより、次の2つの点を考慮する必要があります。
最後に、展開されたサービスをアプリケーションサーバでテストするまで(かつ信頼性が証明されるまで)、テストは完全に終了しないことに注意してください。
共有する接続に関するその他のパフォーマンスの最適化については、次の章「ログオンコンポーネント」を参照してください。
Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved. more ...