AS/400展望台

iSeries開発者用J2EEガイド



ポール・コンテ著

J2EEベースのWebアプリケーションの開発を始めた方も、Sun(java.sun.com)やTheServerSide.comなどといったJ2EE指向のWebサイトを閲覧しているだけの方も、J2EEは恐ろしく複雑なものであることがお分かりでしょう。私の経験から言うと、J2EEの複雑さを克服する秘訣はSunやIBMの研究所から現在次々と出されているあるいはこれから出されるテクノロジーをすべてマスターしようとすることではありません。流れ出てくるものをすべて飲み込もうとすると溺れてしまうでしょう。

そうではなくて、J2EEテクノロジーの中で重要な部分はどこか、アプリケーション中にそうした重要なテクノロジーをどのように組み入れるのか、組み入れるためのプロセス全体を簡素化するためのツールやこつは何か、を見極めていく戦略の方が良いでしょう。ここではJ2EEアプリケーション実装の基礎についてみていきます。本稿では、オーダー・エントリ・システムのようなマルチ・ユーザーで対話型のトランザクション処理(ITP: Interactive Transaction Processing)アプリケーションにのみ着目します。本稿の目的は、一般的なアプリケーションの開発に必要なJ2EEの中心部分を明確にすることですが、最終的には本稿でカバーする範囲以上の知識を身に付けたいと思うようになるでしょう。まずは従来のiSeriesアプリケーションを構成するものとそれに相当するJ2EEアプリケーションの構成要素とを比較するところから始めましょう。

従来のiSeriesアプリケーション
図1にITPアプリケーションの主要な機能部分と、その機能をサポートするiSeriesおよびJ2EEの機能を一覧にしました。電子メール通知機能のように、ITPアプリケーションを実装する際に必要となるすべての機能を一覧にしたわけではありません。また代わりとなるOS/400の機能をすべて一覧にしたわけでもありません。たとえば、データの検証や処理をサポートする機能としてSQLストアード・プロシージャは一覧に入れてありません。iSeriesでのITPの実装を素直に説明するだけで本稿の目的には十分でしょう。iSeries上の従来のITPアプリケーションの主要な部分について詳しくみていきましょう。

ユーザー・インタフェース
フォーマットされたデータを5250表示装置(または5250エミュレータ)上に表示するために、ITPアプリケーションはコンパイラが生成したレコード構造にデータをプログラム内で移動しておいて、次にWriteなどといった高級言語(HLL、たとえばRPG IVやCobol)のI/O文を使用して外部に規定された表示ファイルに対してそのデータを発行するだけです。Write操作の一部としてOS/400は出力データを適切な装置コマンドストリームにパッケージ化し、パッケージ化されたデータはそこから装置に送信され、フォーマットされた結果が画面上に表示されます。iSeriesアプリケーションの開発者はDDSを使用して表示ファイル用のフォーマットを定義します。この定義はアプリケーションのHLLコードとは別にコーディングされます。

典型的なITPプログラムは表示ファイルに書き込んだ直後にRead文を実行し、システムはユーザーの入力を待っている間、そのRead文を実行する箇所でプログラムを停止します。(RPGの場合、ExFmt opcodeがwrite操作とread操作を1つの文に結合します。)ユーザーが[Enter]キー(あるいは他の何かのキー)を押すと、OS/400とHLLランタイムは装置に入力されたデータをコンパイラが生成したレコード構造にプログラム内で移動し、プログラムの実行をRead文の後から再開します。以後のプログラム文はユーザーの入力をレコード構造フィールドとして(つまり通常のHLL変数として)アクセスします。

典型的なiSeriesのITPアプリケーションには認識しておくべき重要な点がいくつかあります。まず、特定の表示装置(実際の端末あるいはエミュレートされている端末セッション)は対話型の各ジョブの実行中はそのジョブに関連付けられていて、その結果、ジョブ中で実行されている各対話型のプログラムと関連付けられていることになります。OS/400はどの表示装置が各ユーザーのジョブに関連付けられているかを把握しており、プログラマがこれを意識する必要はありません。

次に、次のセクションで説明しますが、OS/400は表示ファイルの入出力に使用するレコード構造などの各ジョブのプログラム状態を把握しています。以上の2つの機能により、ユーザー・インタフェースのプログラミングが飛躍的に簡素化され、開発者はプログラムが1台の表示装置がついているスタンドアロン・システム上で実行されていると考えてユーザー・インタフェースのコードを記述することができます。

これ以外にも緑色画面のユーザー・インタフェースの実装を劇的に簡素化するための機能が3つあります。まず、5250画面用のフォーマット・オプションは若干制限されており、DDSでユーザー・インタフェースをプログラムすることは事実上できません。その結果、基本的な表示ファイルDDSを習得するのが容易です。さらに従来のITPアプリケーションのユーザーとの対話を、表示出力の書き出し、ユーザー入力の読み込み、入力の処理、出力の生成、という単純なループだけでコントロールできます。

最後に、iSeriesのHLLコンパイラとDDSコンパイラ、およびOS/400の装置サポートはいずれも入出力データを、そのデータを表現したり入力したりする際のフォーマットから分離するという単純なモデルをサポートしています。このモデルには以下の3つの基本的な要素があります。

・データは単純な1つのレベルのレコード構造で整理され、プログラム変数と表示装置の間を移動します。
・開発者は各画面レイアウトにおける入力フィールドと出力フィールドに名前を割り当てます。この画面レイアウトはその画面に対応するレコード構造を暗に定義し、変数名、構造中での位置、データ・タイプ、その他の属性を含んでいます。
・各レコードのレイアウトのまったく同じ定義が表示ファイル・オブジェクトとその表示ファイルを使用するすべてのプログラム・オブジェクト中に格納され、プログラム中の入出力エリアと画面上の入出力エリアとの間の実行時のマッピングをシステムが扱えるようにしています。

データベースへのアクセス
iSeriesでは、データベース・ファイル(SQLの用語で言えばテーブル)はDDSまたはSQLで定義できます。同様にHLLプログラムでも「ネイティブ」のHLLのI/O文(たとえばReadやWriteなど)またはSQLでファイルを読み込んだり更新したりできます。HLLのI/O文でもSQLでも、プログラマは基本的な読み込み、書き出し、更新、削除操作を使用してファイルを直接処理することに変わりはありません。大きな違いはSQLの1つのUpdate文あるいはDelete文が複数のレコードを処理できるのに対して、各々のHLLのI/O操作は2つ以上のレコードを処理することはありません。

OS/400にはオブジェクト指向(OO: Object Oriented)のデータベースはありませんし、データベース・レコードよりも複雑な構造をした永続的データを格納するためのその他の手段を直接サポートしていません。たとえば、新しい「注文」を挿入するのに従来のITPでは1つのファイルに1つのヘッダー・レコードを書き出して別のファイルに1つ以上の行項目レコードを書き出さなければなりません(これはC++などのオブジェクト指向型言語でも同じです)。

検証および処理ロジック
ユーザーが入力したデータをiSeriesのITPアプリケーションが読み込むと、手続き文(If、Eval、Callなど)がデータの検証、計算の実行、実行フローの制御、新しい出力データの生成を行います。iSeriesのHLLのすべてのプログラムは再投入可能で、OS/400は各ジョブのプログラムの現在の実行ポイントやプログラム変数の内容などのプログラムの状態を記録します。ユーザー・インタフェースについては前述の通り、従来のITPアプリケーションをシングル・ルーザー向けプログラムとして記述し、複数のユーザーに対して別々の表示装置でプログラムを実行させることで並列実行処理をオペレーティング・システムに任せることができます。

重要なのは、iSeriesのITPアプリケーションも一般的には(少なくともHLLアプリケーションのコード中では)複数の同時実効ユーザーをサポートするためにスレッドを使用しないということです。つまり、スレッド・セーフなアプリケーション・プログラムを書かなければならないという課題を開発者は気にしなくて良いということです。(iSeriesのジョブはスレッドと比較した場合リソースを大量に消費して並列性を実装しているため、スレッド・セーフを意識しなくても良いという利点はパフォーマンスを犠牲にしています。)

トランザクション
iSeries上の複数のジョブが同じデータベース・ファイルにアクセスする際、更新が競合する可能性があります。OS/400は、HLLプログラム用にさまざまなトランザクションの分離レベルをサポートしています。レコードのロック(場合によってはファイル・オブジェクトのロック)はトランザクションを分離するためのOS/400の主要なメカニズムです。プログラムがデータベース操作を行おうとする際、そのプログラムが特定の分離レベルに必要なロックを取得できない場合、そのデータベース操作は失敗します。

開発者は、OS/400のトランザクションの分離がどのように動作するのかに注意して、HLLのコンパイラ・オプションを指定するか、またはSQLランタイム文をコーディングして各プログラムやモジュール用の分離(すなわちロッキング)レベルを設定しなければなりません。またプログラマはロックの競合により失敗したデータベース操作を明示的に処理するためのコードを書く必要もあります。

OS/400標準のレコード・ロックはいわゆる「悲観的な並列性」テクニックをサポートしており、トランザクション中でデータが最初にアクセスされた時にレコードのロックが取得され、必要とされるロックはトランザクションが完了するまでリリースされません。「楽観的な並列性」テクニック(すなわち、トランザクション中はロックを保持せずにトランザクションの完了直前に更新の競合がないかを調べるだけ)を使用することを考えているプログラマは、必要となる後始末用のコードを自分で実装する必要があります。これはSQLでは(いわゆる「過負荷更新」を使用して)比較的容易に実装できますが、組み込みのHLLのI/Oでは多少面倒です。

データベースに対して複数の更新を行うようなトランザクションでは、プログラマは適切なトランザクション分離レベルを設定することで、OS/400の「all or none」コミットメント・コントロール・サポートを有効にすることができます。コミットメント・コントロール下で実行されるプログラム中では、プログラマはHLLまたはSQLのCommit文やRollback文を明示的にコーディングして各トランザクションの完了や中止を行わなければなりません。OS/400はデータベースに対する更新をジャーナル(ログ)に記録し、予期せぬシステム停止時に、ジャーナル・エントリを使用して今までコミットしたトランザクションを復元したり、トランザクションの一部をロールバックしたりすることができます。

呼び出しモジュールやプログラム中でトランザクションの動作を管理するのはOS/400では難しい部類に入り、アクティベーション・グループを使用して独立したトランザクション環境を確立する必要が生じる場合があります。この問題は通常、単純なITPアプリケーションの場合には発生しませんが、正確に実装するには膨大な専門的知識を必要とする場合があります。

iSeriesのトランザクション・サポートについては注意すべき点がいくつかあります。1つのランタイム設定、すなわちトランザクション分離レベルの設定で、実際の分離レベル(ロック経由)とall or noneトランザクション復元/ロールバックの有効化/無効化をまとめてコントロールします。もう1つの注意点は、プログラマは実行可能文を明示的にコーディングしてトランザクション境界を画定しなければなりません。(後述しますが、J2EEでは違った方法でこれを行います。)

セキュリティ
OS/400ではユーザー・プロファイル・オブジェクトですべてのシステム・ユーザーを識別します。ユーザー・プロファイルはプログラムやファイルなどといった他のタイプのオブジェクトへのアクセスを所有しています。各ユーザー・プロファイルには(システムごとに)ユニークな名前とそれに関連付けられたパスワードがあります。ユーザーがiSeriesにサイン・オンすると、OS/400はユーザー・プロファイル名とパスワードが正当なものかどうかをチェックしてそのユーザーを認証します。サイン・オンが正常に行われると、システムは対話型のユーザーに対してそのユーザーのユーザー・プロファイルが適切な権限を(直接またはグループ・プロファイルや公開権限を介して間接的に)所有しているプログラム・オブジェクトだけを実行させます。

ITPプログラムが実行されるとHLLのI/O文およびSQL文はユーザー・プロファイルが権限(たとえば読み出し権限、更新権限など)をもっているファイル操作だけを実行することができます。適切な権限を持っていないデータベースにプログラムがアクセスしようとすると、I/O文の実行が失敗します。ITPプログラムはこのような場合に備えて例外処理用のコードを用意しておかなければなりません。またOS/400には、グループ・ユーザー・プロファイルやプログラム採用権限などといった基本的なオブジェクト・レベルの権限に組み込まれた機能もあります。

権限に関連した例外を処理するための実行可能コード以外は、従来のiSeriesのITPアプリケーション用のセキュリティはそのほとんどが宣言型です。つまり、認証および権限許可はプログラムの外で定義され、システムがこれを処理します。データベース権限のレベルの細かさは、標準はファイル・レベルですが、カラム・レベルでの更新権限を指定することもできます。アクセス権限を1つ以上の論理ファイル(またはSQLビュー)に制限することでアクセスが可能なレコードやカラムを指定することができますが、特定のアプリケーションの機能にアクセスを許可するレコードやフィールドを動的に実行時に決定する方法をデータベースは直接サポートしていません。

OS/400のセキュリティでは、データベース・ファイルへのアクセスを制御するためにread/add/update/deleteの一般的な権限だけを指定できます。アプリケーション固有の機能(たとえば「部門の社員に対して昇給する」など)に対する権限を定義する方法をデータベースは直接サポートしていません。OS/400のプログラム採用権限機能はアプリケーション固有の権限を展開するためのエレガントであるものの若干面倒な機能です。

プログラム採用権限を使用する際に問題となる点が2つあります。1つは独自プログラムだけに存在するさまざまなユーザー・プロファイルを作成しなければならないことで、もう1つは各プロシージャに対してその独自プログラム中でそれぞれ異なる採用権限を実装しなければならないことです。実際、iSeriesアプリケーションの開発者(あるいはその開発者が属する組織のセキュリティ管理者)のほとんどはユーザー・プロファイルに対して読み出し、追加、更新、削除の権限を付与するだけで、(基本的には)ユーザーができることを制限しているアプリケーション・プログラム以外で権限を利用しようとするユーザーなどはいないはずだと願っているに過ぎません。必要なプログラム採用権限を実装してITPアプリケーションにおけるアクセスをよりセキュアに制限しようとする開発者が少なくなってきています。

J2EEのアプローチ
ITPアプリケーションと同様のアプリケーションをJ2EEでどのように実装するのかについて考えてみましょう。基本的な実運用レベルのITPアプリケーションの開発に必要となると思われるJ2EEのコア部分の基本的な側面についてのみ説明しますので、その点を忘れないでください。

話をわかりやすくするために、J2EEサーバーとしてEnterprise JavaBeans(EJB)をサポートしているWebSphere Application Server(WAS)V5(基本となる製品)を前提としましょう。これを頭に入れておいて、アプリケーションの実装に使用できるEJBがない場合に使用するさまざまなテクニックについてはここでは触れないこととします。ただしWASやEJBの前提を受け入れる前に、ここでこれから説明する以上にライセンス費用や開発上の課題について深く理解しておくことが必要です。

図2にJ2EEアプリケーション・サーバー・プラットフォームの主要な部分を示します。WebサーバーはApacheなどのHTTPサーバーであり、HTTPリクエストを処理してHTTPレスポンスをブラウザに送り返します。J2EEアプリケーションは通常Webサーバーを構成して、HTMLや画像ファイルに対する要求などのセキュアでないスタティックなデータを処理します。そのようなリクエストをJ2EEアプリケーション・サーバーに処理させるよりこの方法の方がずっと速いからです。

WebコンテナはWASなどのJ2EEアプリケーション・サーバーの一部で、サーブレットやJava Server Pages(JSP)の実行のランタイム・サポートを提供しています。サーブレットやJSPはリクエストを処理してHTTPレスポンス中に送り出される動的な出力を生成することができます。「コンテナ」という用語に馴染みがない場合は、単に「ランタイム・サポート」と読み替えてください。WebサーバーとWebコンテナは同じものではないので注意してください。EJBコンテナはEJBの実行をサポートしているJ2EEアプリケーション・サーバーの一部です。

データベース・サーバー、メッセージ指向のミドルウェア、エンタープライズ・システム、その他のリソースはJ2EEアプリケーション・サーバーの一部ではありません。ただしWASやその他のJ2EEアプリケーション・サーバーには、ITPアプリケーションから呼び出してこれらのリソースにアクセスできるような標準的なAPIが備わっています。

ユーザー・インタフェース
J2EEアプリケーションにおけるユーザーとの対話およびアプリケーションの処理を簡素化したものを図3に示します。通常のJ2EEのWebアプリケーションでは、Webブラウザがユーザー・インタフェースを提供します。ブラウザは、ユーザーがボタンをクリックしたとき(あるいはその他のさまざまなアクションを取ったとき)にHTTPリクエストをWebサーバーに送信し、アプリケーションからレスポンスとして送られてきたHTML(またはXML)やその他のデータを表示します。

Webサーバーは受信したHTTPリクエストをチェックし、事前に指定されたWebサーバー構成データに基づいてスタティックなデータを返すかまたはリクエストをWebコンテナに渡して処理させます。Webコンテナはリクエストを受け取ると、自分の構成データ(J2EEアプリケーションの導入時に指定した項目も含む)を使用してどのアプリケーション・サーブレットを実行するかを決定します。簡単に言えば、サーブレットはWebコンテナが呼び出すことのできる標準的なJ2EEメソッド(プロシージャなど)の集合を実装したJavaプログラムです。Webコンテナは、たとえばdoGetやdoPostなどといった標準的なメソッドを呼び出すことによってサーブレットへコントロールを渡します。

Webコンテナはユーザーからの入力データ(ブラウザから受信したHTTPリクエストの一部)を、呼び出されたメソッドのパラメータを介してサーブレットに渡します。サーブレットは渡された入力データを、他のサーブレット、JSP、「旧来の普通のJavaオブジェクト」(POJO)、EJBなどさまざまなタイプの他のJavaプログラム中のメソッドを呼び出すなどといったあらゆる方法で検証し処理できます。(POJOはJavaオブジェクトですが、サーブレットやEJBなどのような特別な種類のオブジェクトではありません。)

J2EEアプリケーション(すなわち最初のサーブレットとそのサーブレットが起動したすべてのプログラム)はアプリケーションの出力データを生成した後、文字列形式の「生の」出力データとHTML(またはXML)タグとを組み合わせて文字列ストリームを作成してWebページを定義しなければなりません。次にアプリケーション・サーブレット(最初に起動されたものあるいはそれとは別のもの)がWebコンテナから提供されている標準のメソッドを呼び出して文字ストリームをWebサーバーに渡し、Webサーバーは文字列ストリームをHTTPレスポンス中のストリームに入れてブラウザに送り返します。

J2EEアプリケーションの開発者は通常、アプリケーションの出力データとHTMLタグとを組み合わせるのに必要なすべてのステップをJavaで書くことはしません。これよりずっと簡単なアプローチはJSPを書くことです。JSPはHTMLタグとJSPタグと若干のJavaコードを含んだソース・ファイルです。WebコンテナはJSPのソースを自動的にサーブレット用のピュアJavaソースに変換します。生成されたコード中ではJavaの文字列リテラルにサーブレットが生成したHTMLタグやその他のHTTP出力ストリームの定数要素が含まれています。またWebコンテナは生成されたサーブレットを自動的にコンパイルし、アプリケーションがJSPを起動したときにはこのコンパイルされたサーブレットが実際に実行されます。

Webページを「書き出した」後(すなわちHTTPレスポンスをブラウザに返した後)、J2EEのITPアプリケーション(すなわちそのサーブレットまたはJSP)は従来のiSeriesのITPアプリケーションとは異なり、「読み込み」操作を実行せずにアプリケーション・コード中の特定のポイントでユーザーの入力を待ちません。その代わりにJ2EEアプリケーションは基本的にはこの時点でWebコンテナにコントロールを戻し、ブラウザからの次のリクエストを待ちます。

以上を簡単にまとめると、Javaプログラムであるサーブレットがユーザーからの入力を受け取り、サーブレットとなるJSPがユーザーへの出力を送り返します。J2EEアプリケーションのサイクルのこの2つの部分は前述した典型的なiSeriesのアプリケーションとは重要な点で異なります。

典型的なJ2EEのITPアプリケーションでは、ブラウザがアプリケーションの出力を表示し、ユーザーからの入力を受け取ります。5250端末や5250エミュレータとは異なり、ブラウザはアプリケーションとの接続を維持せず、アプリケーションはユーザーが取るブラウザのアクションを制限できません。たとえば、ユーザーはアプリケーションが意図した処理の流れの中でどこにいようとも、ブラウザの「Webページの履歴」機能を利用して別のアプリケーション画面に直接ジャンプできます。またユーザーはアプリケーションのサーブレットを参照するURLを直接入力することもできます。つまり、J2EEのITPアプリケーションは従来のiSeriesのITPアプリケーションと同様に、アプリケーションのどの部分をユーザーのリクエストが実行しようとしているかを予測することができず、アプリケーション中の現在の実行ポイントを把握するのにシステムに依存することはできません。

こうしたイベント駆動型のユーザー・インタフェースを取り扱う典型的な方法はアプリケーションのフロントエンドとしての役割を果たす1つのサーブレットをコーディングすることです。(このサーブレットのことを「コントローラ」と呼ぶことがあります。)アプリケーションが導入されるときに、アプリケーションのWebページ上でのユーザーのアクション(あるいはユーザーのURL入力)の結果として生じる可能性のあるあらゆるリクエストをこのサーブレットにルーティングするようにWebコンテナを構成しておきます。開発者はフロントエンド(あるいは下位のプログラム)をコーディングしてリクエストをチェックし、次に何をするのかを決定しなければなりません。

図3に示したように、フロントエンド役のサーブレットは通常POJO中のヘルパー・メソッドを呼び出してどの操作に対するリクエストなのかを判断し、入力データの基本的な検証を行います。操作や入力データが不正なものである場合は、フロントエンドがJSPを起動し、エラー・ページをフォーマットしてレスポンスとして送り返します。

正当なリクエストに対しては、フロントエンドはステートフル・セッションEJB中の適切なメソッドを呼び出します。ステートフル・セッションEJBは、POJOやその他のEJB、その他のリソース用のJ2EEのAPIなどを起動してITPアプリケーションの流れをコントロールし、データの取り出しや更新をしてビジネス用の計算をするための中心的なJ2EEのコードです。ステートフル・セッションEJBはアプリケーションの流れの中でのユーザーの論理的な位置を記録し、セッションに固有なデータすべてをプログラム変数として管理します。

ステートフル・セッションEJBは要求された操作の結果(通常データと例外データの両方)をフロントエンドに対して返します。「注文」などといった複雑なデータは1つ以上のJavaオブジェクトとして返されます。データを返されたフロントエンドは適切なJSPを起動して結果をフォーマッティングし、通常の結果を含むページまたはエラー・ページを返します。

ブラウザとアプリケーションの間には永続的な接続は存在しませんので、Webコンテナは「クッキー」(あるいはその他のメカニズム)を使用してアプリケーションごとにアクティブなセッションを個別に自動的に把握します。セッションは複数のHTTPリクエスト/レスポンスのサイクルに渡って特定のユーザーのブラウザからアプリケーションへのアクセスを把握するためのJ2EEのメカニズムに過ぎません。サーブレットが実行されるとそのサーブレットは標準のメソッドを呼び出して現在そのサーブレットを実行しているセッションに対してシステムが割り当てた識別子を取り出すことができます。ITPアプリケーションは通常J2EE認証(セキュリティのセクションで説明します)を使用しますので、ITPアプリケーションも標準のメソッドを使用してユーザーの識別子を取り出すことができます。つまり、J2EEセッションをサポートしていることでアプリケーションとユーザーの間の論理的な関連を管理するための最小限の基本的な機能が提供されているということです。これは5250用ユーザー・インタフェースを備えるHLLアプリケーションを作成する際には当たり前とされていたことです。

パフォーマンス上の理由により、Webコンテナは通常アプリケーションのフロントエンド用サーブレット(他のサーブレットやJSPも同様)のランタイムコピーを1つだけ起動します。Webコンテナは複数のスレッドを使用して同時使用ユーザーをサポートします。スレッドを使用するにはサーブレットのコードをスレッド・セーフに記述しなければなりません。いろいろ含みはありますが、サーブレットはセッション固有のデータ(たとえばカタログ中に表示されている現在のアイテム)をプログラム変数中に格納できないだけです。

幸いなことに、ステートフル・セッションEJBはスレッド・セーフであり、複数のHTTPリクエストに渡ってプログラム変数中に値を格納することができます。これにより、各ユーザーのセッションに対して別々のステートフル・セッションEJBオブジェクトを持たせたITPアプリケーションを設計することができます。ステートフル・セッションEJBを使用することで、iSeriesの対話型HLLプログラムと同様の方法で、セッション固有のデータをプログラム変数中に格納することができます。

ステートフル・セッションEJBを使用する際の唯一の欠点は、どのEJBオブジェクトが各ユーザー・セッションと関連付けられているのかをフロントエンド用のサーブレットが記録しておかなければならないことです。これを記録しておくために、サーブレットは標準のWebコンテナのメソッドを呼び出してユーザーのステートフル・セッションEJBへの参照をWebコンテナのセッション・データ中に格納することができます。(Webコンテナによるセッション・データ格納のサポートはステートフル・セッションEJBを使用するよりも面倒で非効率的ですので、このEJBへの参照以外のセッション・データのほとんどを格納するときは通常はステートフル・セッションEJBを使用してください。)

ここではJ2EEのITPアプリケーションのユーザー・インタフェース部分にかかわる中心的な問題だけを述べてきましたが、あまり単純なものではないということがおわかりいただけたでしょう。従来のiSeriesのITPアプリケーションを開発した後でJ2EEのテクニックを選択したプログラマは、複数の対話ユーザーを扱うのにサーブレット・プログラミングがいたずらに厄介なものであると当然のことながら思うでしょう。

業界はこのユーザー・インタフェースの複雑性に関していろいろな方法で対処しようとしています。JavaServer Faces(JSF)はJSPのハイレベルなユーザー・インタフェース要素を記述するためのJ2EE標準で、Strutsはレイアウトや入力の検証、アプリケーションの流れ、エラー処理などといったユーザー・インタフェースの構造をより簡単に記述するためのオープン・ソースのフレームワークです。典型的なJ2EEのITPアプリケーションはJSFとStrutsの両方を使用することになるでしょう。

ポータル・コンテナ(WASやその他のアプリケーション・サーバー上で実行されるランタイム)やポートレット(追加の標準メソッドを実装するサーブレット)は、別のJ2EEアプリケーションへのアクセスや場合によっては協調作業を提供するブラウザ・ベースのユーザー・インタフェースを作成するハイレベルな方法です。

データベース・アクセス
J2EEアプリケーションはiSeries上のDB2などの関係データベースに対してさまざまな方法で読み出しや更新が行えます。一番直接的な方法はサーブレットやヘルパー・コードがデータベースに対してJDBC接続を開き、JDBCメソッドを呼び出してSQL文を実行するという方法です。JDBCのコーディングはiSeriesのHLLプログラム中で組み込みのSQLや組み込みのI/O操作を使用するよりもずっと面倒ですが、基本的なアプローチは似たようなものです。すなわち、アプリケーションのビジネス・ロジックは、読み出し、書き込み、更新、削除などの操作を使用してデータベース・テーブル(すなわちファイル)上で直接実行されます。

しかし単純ではない実運用レベルのITPアプリケーションでは、JDBCを直接使用する方法は(後述する) 2つの推奨する選択肢が既に提供しているインフラストラクチャ・コードを大量に書かなければならないため、最善のアプローチではありません。

Javaデータ・オブジェクト
Javaデータ・オブジェクトJDOはデータベース・アクセスを提供するもう1つの選択肢です。JDOでは、オープン・ソースまたはベンダーが提供するツールを使用してコードをPOJO用のクラス・ファイルに組み入れるので、簡単なJavaコードを使用してデータベースの行(レコード)からの値で埋められるフィールドを持つオブジェクトを作成できます。オブジェクトに対してもう1つの簡単なメソッドを呼び出すだけでオブジェクト・データをデータベースの行に戻して格納できます。JDOは問い合わせ言語をサポートしているので複数の行からPOJOの集合を取り出したり、複数の行に対して更新をかけたりすることができます。JDOは3つのアプローチ(JDBC、JDO、エンティティEJB)の中でおそらく間違いなく最も自然な方法でJavaアプリケーション中で使用され、また根強い愛好者がいます。残念ながらJDOはその普及レベルがたとえばStrutsほどにはなっていません。データベース・アクセスにJDOを使用するかどうかを決める前に、JDOのテクニカルな長所だけでなくJDOに対する今後の業界のサポートについても考慮してください。

エンティティEJB
エンティティEJBはデータベース・アクセスのための標準のJ2EEの「主力産業」アプローチを提供します。エンティティEJBは4つの基本的なデータベース操作を提供しているだけでなく、トランザクションのフルサポートとメソッド・レベルのセキュリティ(後述)を備えた分散処理機能も提供しています。EJBはEJBコンテナを備えたWASなどのJ2EEアプリケーション・サーバーを必要とします。アプリケーション・サーバーがなければJDBCまたはJDOを使用する必要があります。

エンティティEJBを実装するためのアプローチとしてBean管理永続性(BMP: Bean-Managed Persistence)とコンテナ管理永続性(CMP: Container-Managed Persistence)の2つのアプローチがあります。BMPではEJBの実装内でJDBCのコードを記述してデータベースにアクセスします。BMPの方がEJBのランタイムの動作に対するコントロールが効きますが、コードを書くときに注意が必要です。

CMPでは通常は開発ツールを使用してEJB属性と1つ以上のデータベース・テーブルのカラム間のマッピングを定義します。またEJBQL問い合わせ言語を使用してCMPのEJBを定義することもでき、CMPのEJB間の関係(たとえば親子関係)を定義できます。EJBコンテナはマッピングや問い合わせ式などといったEJBの定義を、そのほとんどの場合JDBCを使用して実際のデータベース・アクセスを実行する実行可能なコードに変換します。

理論上CMPのEJBはビジネス・ロジックと複雑なデータベース・アクセスを直接的な宣言型のテクニックを使用してEJB内で結合することで高度なエンティティの動作を実装できるように見えます。しかし実際は話が若干違います。データベース・テーブル用のかなり直接的なJavaオブジェクト表現(Customerデータベース・テーブルの簡単なマッピングであるCustomer EJB)を簡単なCMPのEJBで作成するのはそれほど難しくはありません。しかしデータベースの読み出しや書き込みよりも複雑な動作をする実運用レベルの品質の良いCMPのEJBを作成するには豊富な知識と経験が必要となります。多くの組織に良い結果をもたらす戦略はデータ・ソースとして簡単なCMPのEJBを使用し、ビジネス・ロジックのほとんどをセッションEJBに置くことです。これについて次のセクションで説明します。

ITPアプリケーションでエンティティEJBをどのように使用するのか(あるいは使用すべきかどうか)を決めることは重要でしかも難しい判断となります。現在エンティティEJBはSunやIBMが支持しているアプローチです。しかしJ2EEコミュニティの中でも評判の人たちの多くは、エンティティEJBは複雑で重過ぎるため、少なくとも現在の形のままではほとんどのビジネス・アプリケーションにとって最適なソリューションではないと考えています。J2EEの中でもこの分野はしばらくの間は流動的なものであると考えた方が良いでしょう。

検証と処理ロジック
フロントエンドのサーブレットは自作のものであれStrutsなどのフレームワークにより提供されたものであれ、通常は他のサーブレットやヘルパー・オブジェクト中のメソッドを呼び出して最初の入力検証を行い、そしてアプリケーションのメインとなるステートフル・セッションEJB中のメソッドを呼び出してリクエストされた操作を実行します。

サーブレットもステートフル・セッションEJBもともにPOJOまたはステートレス・セッションEJBとして実装されたヘルパー・オブジェクト中のメソッドを呼び出すことができます。POJOは記述が簡単で特定のアプリケーションのフロントエンドやメインのステートフル・セッションEJBだけに必要となるヘルパー・コード用には良いソリューションとなります。

基本となるビジネス・ロジックのモデルの一部であり、複数のアプリケーションで必要となると思われるような関数や操作(たとえば価格決定ルーチンなど)にはステートレス・セッションEJBを使用してください。またステートレス・セッションEJBはデータベースへのアクセスや次のセクションで述べるようなトランザクションの一部としなければならないその他の操作を起動する操作を実装するのにも適切な選択肢といえます。

ステートレス・セッションEJBにはサーブレットや他のEJB、POJOなどから呼び出しが可能なJavaコードが含まれています。ステートレス・セッションEJBを理解するための1つの見方はこれを「プロシージャ・ライブラリ」とみなすことです。ステートレス・セッションEJBは、ステートフル・セッションEJB(あるいはiSeriesのHLLプロシージャ)とは異なり、セッション固有あるいは呼び出し側固有の状態をあるメソッドの起動から次のメソッドの起動へと維持することができません。(ステートレス・セッションEJBにつけられた適切とは思えない名前にごまかされないでください。これらの名前はセッションの概念とは実際にはほとんど関係ありません。)

J2EEのITPアプリケーションのこの部分は、HLLの手続き型コーディングではなくJavaとオブジェクト指向のコーディング・テクニックが使用されている点を除けば、多くの意味で従来のITPアプリケーションの対応する部分と似ています。前述した通り、両者の最大の違いは複数のHTTPリクエスト/レスポンスのやりとりにまつわる状態をアプリケーションが保存するように明示的に設計してやらなければならない点です。

トランザクション
JavaトランザクションAPI(JTA: Java Transaction API)ではJ2EEによるトランザクションの基本的なサポートが提供されています。アプリケーションはJTAのメソッドを直接呼び出すことができますが、一般的なITPアプリケーションは宣言的トランザクション用のもっと便利なEJBサポートを代わりに使用します。

EJBのすべての形式では、トランザクション分離レベルとEJBコンテナがトランザクションをいつ開始していつ終了すべきかを宣言的に(すなわちEJBのJavaコードの外で)指定することができます。これにはEJBの各メソッドに対してトランザクションの動作を指定します。EJBコンテナはトランザクションEJBの代わりに適切なJTAを呼び出すので、これを行うためのアプリケーションのコードは不要です。またWASなどのように、EJBコンテナの中には、エンティティEJBを使用した「楽観的」データベース並列性などといったトランザクション・サポート用の追加のオプションを提供しているものもあります。つまり、EJBはITPトランザクションを実装するための最も単純で豊富な機能をもった選択肢を提供します。

サーブレットから呼び出されるサーブレットやPOJOもまたJTAのメソッドを呼び出してトランザクションを定義しておいて、アプリケーション中の適切なポイントでコミットやロールバックを行うためのメソッドを明示的に呼び出すことができる点に注意してください。概念的にはこれは従来のiSeriesのITPアプリケーションでトランザクションが処理されていた方法と似ています。ただし、前述したセッション・ステートとスレッド化の問題があるため、JTAをこのように直接コーディングするのにはiSeriesのHLLを使用するよりも手間がかかります。J2EEのITPアプリケーションでは、EJBのトランザクション・サポートを代わりに使用することをお勧めします。

ITPアプリケーション用のトランザクションを実装するための一番素直な方法は、メインのステートフル・セッションEJBの1つ以上のメソッドに対して、そのメソッドが呼び出されたときに新しいトランザクションを開始し、そのメソッドが終了したときにそのトランザクションがコミットまたはロールバックされるように指定することです。たとえば、アプリケーションのフロントエンド用のサーブレットやヘルパー・コードはオーダー・エントリ・アプリケーションのメインのステートフル・セッションEJBに対して次のようなメソッド呼び出しを順に行います。

curOrder.setCustomerName(customerName);
・・・
curOrder.addItem(itemId, qty);
curOrder.addItem(itemId, qty);
・・・
curOrder.commitOrder();


setXxxxメソッドとaddXxxxメソッドは非トランザクション、すなわちトランザクションを開始するわけでも終了するわけでもないと指定されています。(上記の例中の)各メソッドはステートフル・セッションEJB中のプログラム変数の値を単に変更しているだけです。commitOrderメソッドは呼び出されたときにトランザクションを開始し、メソッドが終了したときにトランザクションをコミットすると宣言されています。EJBコンテナは必要となるJTAメソッドをセッションEJBの代わりに呼び出すので、プログラマがこれに必要な実装コードを書く必要はありません。

一般的なITPアプリケーションでは、ステートフル・セッションEJBのメソッド(たとえば上記の例ではcommitOrder)はCMPのEJBのさまざまなメソッドを呼び出してデータベース・テーブルを更新します。適切なCMPのEJBのメソッドは呼び出し側が開始した既存のトランザクションに参加するように宣言されます。このようにしてEJBのデータベース更新はアプリケーションのメインのステートフル・セッションEJBで定義されたトランザクション内で行われます。

トランザクションと言えばデータベースの更新と思われている一方で、トランザクションは他のタイプのアクション(メッセージの送信など)もカバーしているという点は重要です。EJBのトランザクション・サポートはこうした広範なスコープも取り扱うようにも設計されています。EJBの代わりにJDOを取り入れたステートフル・セッションEJBを用いた宣言的なトランザクション・サポートを使用してデータベースにアクセスできる点にも注意してください。

セキュリティ
J2EEのITPアプリケーションのセキュリティは見方によっては同等のiSeriesアプリケーションに比べて若干複雑に見えます。これは主に次の2つの理由によるものです。1つはHTTPが対話型の(あるいはそれ以外の)ユーザーとアプリケーションとを接続するメカニズムであること、もう1つはJ2EEサーバーがさまざまなタイプのオペレーティング・システム上で実行されることです。またそれぞれが独自のアプローチでユーザー識別、認証、権限付与をしているからです。ただしEJBにはアプリケーション固有の認証要件の実装を大幅に容易にするセキュリティ機能がいくつかあります。

導入されたJ2EEのITPアプリケーションは通常、ブラウザ・ユーザーが最初のリクエストをアプリケーションのセキュアな機能に対して送信したときに、Webコンテナがログオン・ページを返信してユーザーIDとパスワードを要求するように実装されています。これはカスタムのサーブレット/JSPコードで実現するか、またはフロントエンドのサーブレットがプロテクトされていると宣言してログオン用Webページとログオン・エラー用のWebページの両方を指定することで実現します。(また導入されたアプリケーションはログオン・リクエスト、それ以後のリクエスト、パスワードやクレジットカード番号などといった機密性に注意すべきデータを含むリプライに対してSSLを使用しなければなりません。) ユーザーが認証されると、Webコンテナはセッション中そのユーザー用のセキュリティ・トークンを維持します。サーブレット(またはヘルパー・コード)がEJBのメソッドを呼び出したりJDBCを呼び出してデータベースにアクセスするといったように他のリソースにアクセスしようとしたときに、このセキュリティ・トークンがチェックされます。

システム・セキュリティの管理者はユーザーのレジストリとユーザーがどのグループに属しているのかを管理する責任があります。たとえばiSeriesのユーザー・プロファイルやグループ・プロファイルなどがこれにあたります。J2EEの用語でこれらはセキュリティ原理と呼ばれています。WAS(あるいは他のJ2EEアプリケーション・サーバー)のセキュリティ管理者はWebコンテナを構成してアプリケーションのユーザーの認証時にレジストリを使用します。

Webコンテナ(およびWebサーバー)は基本的な認証機能を提供してサーブレット、JSPまたはスタティックなデータ(画像ファイルなど)へのアクセスを制限します。J2EEのITPアプリケーションの認証執行の心臓部はEJBレベルで提供されます。

各セッションEJBやエンティティEJBに対して開発者はメソッドを呼び出す権限をもった1つ以上のセキュリティ・ロールを指定できます。EJBが導入されると、このロールはシステムのユーザー・レジストリで定義されたセキュリティ原理にマップされなければなりません。実行時に、EJBメソッドを呼び出すサーブレット(または他のJavaプログラム)を実行しているユーザーはそのメソッドを呼び出す権限のあるロールに(直接あるいはグループ・メンバーシップを介して)関連付けられていなければなりません。このセキュリティに対するJ2EEのアプローチは開発者がアプリケーションのビジネス機能のための「論理的な」認証を定義する一番綺麗なやり方を提供し、セキュリティ管理者はどのシステム・ユーザーがそのビジネス機能にアクセスするのかを管理します。

高額給与の社員の個人情報へのアクセスを一部のユーザー・グループだけに制限するなどといったデータ依存型の認証規則については、現在のセキュリティ原理がEJBの導入時にプログラマによって定義されたロール(たとえば人事部長)に関連付けられているか否かをEJBのメソッドがチェックします。EJBが導入されるときWASのセキュリティ管理者がロールと実際のユーザーやグループとを関連付けます。

またサーブレットやEJBを定義してメソッドを呼び出す際に「特定のユーザーとして実行」し、OS/400のプログラム採用権限と同様の機能を提供することもできます。J2EEアプリケーションの外部にある保護されたリソース(たとえばデータベース・テーブル、レガシーな企業アプリケーション)へのアクセスをサポートするために、セキュリティ管理者はJ2EEアプリケーションのユーザーのセキュリティ情報を外部のリソースが要求する適切なログオン情報や認証情報にどのようにマッピングするかを指定できます。

学ぶべきことは多い
以上おわかりいただけた通り、簡単なJ2EEのシナリオからでも学ぶべきことはたくさんあります。しかも残念なことにHTTPを基礎としていることで必要以上に複雑になっています。J2EEは将来のリリースでもっと簡素化されるのでしょうか。それは私にはわかりません。Sun、IBM、その他の主要なJ2EEプレーヤーたちはWebサービスなどといった強力な新機能を追加していく意向のようです。J2EEのような複雑なプラットフォームはHTTPのリクエスト/レスポンスのサイクルやクッキーなどよりももっと確固とした基礎の上にあるべきだなどという声や非難を聞いたことはありません。注意するに越したことはありません。

もっと肯定的な見方をすれば、J2EEテクノロジーは妥当な「設計図」さえあれば先行きの見えている開発者でさえも克服可能なものです。J2EEアプリケーション用のより新しいIDEやコード生成機能がJ2EEのコーディングの難しい部分をウィザードやフレームワークで隠してくれるようになっています。

ポール・コンテ氏はiSeries NEWS誌およびe-Pro Magazine誌を発行しているPenton Media, Inc.(ベル・データ株式会社の業務提携会社)のシニア・テクニカル・エディタです。同氏はオレゴン州ユージーン市で教育やコンサルティングを実践しているPCES社の社長でもあります。



↑このページのトップへ
TOPPAGE

BELLDATA, Inc. Copyright reserved.