前の記事 (参考文献を参照) において、私はXML-RPC (参考文献を参照) を、リモート・マシン上で機能を実行する容易な方法として紹介しました。しかしながら、この機能だけでは、このプロトコルを学ぶ価値はそれほどありません。この記事ではこの機能の代わりに、XML-RPCと関連する興味深いWeb Servicesのアプリケーションであるミドルウェアについて調べます。
最も基本的なレベルにおいて、ミドルウェアはまさにアプリケーション・プログラミング・インターフェース (API) を使用してリソースへのアクセスを抽象化する手段ということができます。関数型プログラミングとオブジェクト指向プログラミングではどちらでも、APIを使用して、プログラマーの便宜のためにソフトウェア・リソースの操作に関する詳細 (ユーザー認証データベースやファイル・システムなど) を単純化します。明らかに、APIを呼び出すユーザーは、基礎を成すインプリメンテーションの詳細から守られるため、このような抽象化から益を受けます。ただし、APIのインプリメンテーション担当者は、古いインプリメンテーションを使用しているプログラムを破壊しなくても基礎を成すライブラリーを徹底的に変更することができるため、この抽象化から益を得られるかどうかは明らかではありません。
コードのインプリメントと呼び出しの間でこのような独立性を認めるソフトウェア・ライブラリーは、疎結合と見なされます。呼び出し側のコードで、APIやライブラリー・コード内にある操作されたデータ構造が無視されると、ライブラリーのインプリメンテーションの変更時に正しく動作しないクライアントが破壊される可能性があるため、この独立性は失われます。このようなシステムは、密結合と呼ばれます。優れたソフトウェア・エンジニアリングでは、システムが可能な限り疎結合になっている必要があります。
APIの考えは、プログラミング・ライブラリーを超えた大きな概念に拡張することができます。もちろん、ユーザーがデータベースの内容をグラフィカルに変更できるようなアプリケーションは、それ自体がロー・データを操作できなければなりません。MySQLやPostgreSQLなどのデータベースには、この目的のためにCインターフェースが備わっています。この仮説アプリケーションが特定のリレーショナル・データベース管理 (RDBM) システムのネイティブ・インターフェースを直接使用している場合、新しいRDBMシステムに切り替える際に以前のシステムと対話していたコードをすべて作り直す必要が生じます。表示論理と特定の基礎データベースを処理するために必要なルーチンとの間のすべてのコミュニケーションを調整するミドルウェア層を導入すれば (図1 を参照)、プログラマーはAPIというフェンスのいずれの側に対しても、他方の側に対応する変更を行わずに、徹底的な変更を加えることができます。
図1: 典型的なミドルウェアのセットアップ
簡潔な理論的背景を得たところで、現実世界の問題をXML-RPCミドルウェアによって解いてみましょう。
ログインが必要なユーザー・アカウントが存在するWebサイトを考えてみましょう。Apacheに組み込まれたHTTP認証を使用すれば、ユーザー名とパスワードを保管することができます。多くの場合、該当ユーザーが認証された時点で使用可能になる、他のアカウント固有のデータも保管しなければなりません。これは、アカウント情報をMySQL (参考文献を参照) などのRDBMシステムに保管すべき理由となります。フロントエンドがPerlやPHPで作成されていれば、MySQLへのアクセスをCGIスクリプトにハードコーディングすることは魅力的でしょう。しかしながら、これを行うと、表示論理と特定のRDBMシステムが密結合になります。そこで、ミドルウェアが登場します。ミドルウェアは、基礎を成すRDBMをシームレス (継ぎ目なし) に変更できることに加え、単一のデータベース・サーバーを一群のマシンに置き換えることができる、柔軟なシステムを実現します。ミドルウェアの使用によって実現する疎結合は、スケーラブルなWebアプリケーション・アーキテクチャーを作成するかぎとなります。
フロントエンドのCGI表示コードとバックエンドのデータベースおよびビジネス論理コードとの間にミドルウェア・ブリッジを構築するには、必要なすべてのデータ・アクセスを許可するAPIを定義する必要があります。表1 は、このアプリケーションのユーザー・アカウント・データ・ストアに使用するXML-RPCミドルウェアAPIを示しています。フロントエンド・コードは、これらのXML-RPC呼び出しを行うことによってのみ、基礎となるデータを取得することができます。
表1: アカウント情報用のXML-RPC API
|
ホスト: |
http://marian.daisypark.net/RPC2 |
ポート: |
1080 |
|
プロシージャー定義 | |||
|
プロシージャー名 |
入力 |
出力 |
説明 |
|
authenticate |
<string>, <string> |
<int> |
ユーザー名とパスワードを指定すると、証明書が一致した場合に新規セッションIDを戻します。 |
|
get_account_info |
<int> |
<struct> |
有効なセッションIDを指定すると、そのIDに関連したユーザーのアカウント情報を戻します。この構造には以下のフィールドがあります。 |
|
set_account_info |
<struct> |
<int> |
以下のフィールドを含む構造を指定すると、 |
ユーザーがこの架空のサイトにログインしようとすると、ユーザー名とパスワードの入力を求めるHTMLフォームが表示されます (図2 を参照)。ユーザーが発信ボタンを押すと、フォームを処理するCGIスクリプトにより、Perlリスナーに対してauthenticate() XML-RPC呼び出しが出されます。ユーザーの証明書が有効であれば、フロントエンドはセッションIDを受け取り、それを使用してサイトでユーザーをトラックするか、他のアカウント情報を検索することができます。この例では、ユーザーがログインすると、アカウント情報を更新できるページに移動します。
図2: IDとPWの入力を求めるHTMLフォーム
フロントエンドのWebコードを開発する上で、PHPより優れた言語はほとんどありません。リスト1 は、図3 のログイン画面を生成するPHPコードを示しています。
図3: PHPによるログイン
PHPの初心者は、PHPのホーム・ページ (参考文献) を参照してください。他のサーバー・サイド組み込みテクノロジー (Active Server PagesやColdFusionなど) と同様、PHPでは静的ページと動的コンテンツの境界があいまいになっています。ログイン・ページのコード (リスト1 を参照) は、最初にXML-RPC PHPライブラリーをプルします。ユーザーがユーザー名とパスワードを入力すると、新規のxmlrpc_client オブジェクトがURLとTCPポート番号で初期化されます。PHPの場合、このオブジェクト初期化ルーチンの引数は、path、host、およびport です。
Perlライブラリーと同様に、PHPを使用すれば、プログラマーは基礎を成すXML-RPC対話をデバッグのために参照することができます。6行目と7行目では、RPCの引数となるオブジェクトを作成します。各引数は、特殊なオブジェクトにラッピングして、PHPライブラリーが値をXMLに正しくエンコードできるようにする必要があります。
8行目では、RPC呼び出しの内容をエンコードする新規xmlrpcmsg オブジェクトを作成します。このオブジェクトには、リモート・プロシージャー名の後に、すべてが1つのPHP配列オブジェクトにラッピングされた引数のリストを指定する必要があります。最後の9行目では、XML-RPCリスナーとの接続が確立され、応答オブジェクトが戻されます。リスナーが戻した値を検索するには、応答オブジェクトに保管されたxmlrpcval オブジェクトを取得してから、xmlrpcval オブジェクトのスカラー値を要求します。なぜスカラー値なのでしょうか? APIのことを思い出してください。authenticate() がある種の整数を戻すことが宣言されていました。この正数が非ゼロであれば、これはセッションIDであり、ユーザーはセッションIDがURLに結合されたアカウント保守画面に移動します。authenticate() が失敗すると、失敗メッセージが発行され、ユーザーは再びログインする機会を与えられます。
4~7行目ですべてのライブラリーをプルした後、すべてのサブルーチンがデータベースへのアクセスに使用するグローバルDBIハンドル$Dbh が作成されます。END() サブルーチンはスクリプトの終了時に実行され、データベースから明示的に切断しないことに関連したDBIエラー・メッセージの発生を確実に抑えます。15~22行目では、発行されたAPIプロシージャー名がそのプロシージャーをインプリメントするPerl関数と関連付けられます。
36行目は、認証が行われる様子を示しています。この関数では2つのスカラーが必要です。これらは、必要なサイズと形状を満たしているかどうか確かめられます。SQLテーブルusers が照会され、証明書が一致するかどうかが検査されます。リスト3 は、このテーブルを作成したSQLコードを示しています。
リスト3: usersテーブルを作成したSQL
<![if !supportEmptyParas]> <![endif]>
create table users (
username char(12) not null default '',
password char(13) not null default '',
fullname char(50) not null default '',
points int default 0,
Primary Key (username),
index (fullname)
|
ユーザー名が一致する行を検索するSQLステートメントが用意されています。ユーザー名は基本キーなので、最低でも1つは一致する行があります。"password" という名前でDES暗号化ストリングである1つの列が戻されます。Perlのcrypt() 関数により、渡された$password がDES暗号化され、このスカラーがテーブル内で見付かったものと比較されます。これらのストリングが一致すると、このイベントはlogger() 関数を介して別のMySQLテーブルに記録され、新規のセッションIDが生成されます。210行目のnew_session_id() 関数は、ユーザー名をsessions テーブルに挿入するだけです。リスト4 は、sessionsテーブルを作成したSQLコードを示しています。
リスト4: sessionsテーブルを作成したSQL
create table sessions (
id int auto_increment not null primary key,
username char(13),
issued timestamp
);
|
セッションIDは単なるauto_increment フィールドです。挿入が実行されるたびに、固有の新規IDと挿入発生時のタイム・スタンプが作成されます。MySQLのすばらしい点は、DBIハンドルのmysql_insertid 属性を使用すれば、最後に使用された挿入IDを容易に識別できることです。その後、この整数はXML-RPCクライアントに戻されます。
セッションIDが生成される秘密が明らかになったところで、そのセッションIDを使用する関数について考えましょう。66行目で、get_account_info() が始まっています。これには、有効なセッションIDを渡す必要があります。セッションIDはsessions テーブルに属していますが、必要な情報はusers テーブルに保管されていることを思い出してください。必要な情報を取得するには、SQLJOIN 演算子を使用します。sessions とusers のどちらにも、"username" という名前のフィールドがあります。sessions でセッションIDが入っている行を見付ければ、そのセッションのユーザー名も見付かります。このユーザー名を使用して、users でそのユーザー名が入っている行が検索されます。これが成功すれば、その行がハッシュ参照として取り出され、呼び出し元に戻すことができます。このSQLが少し分かりにくい場合は、2つの小さなSELECT 呼び出しを使用して、行を手作業でPerlハッシュと結合してください。
最後の関数はset_account_info() で、構造を必要とします。このXML-RPC構造は、Perlではハッシュ参照として表されます。この関数はINSERT やUPDATE を行うので、これら2つのSQL操作の間に競合状態が起こらないように、users テーブルはロックされます。理想的なのは、トランザクションをサポートしている最新バージョンのMySQLを使用することですが、依然として以前のMySQLが多く出回っているため、トランザクションが歴史的にこのRDBMで処理されてきた方法を理解しておくのは役立ちます。
DBIのdo() メソッドは、実行されたSQLステートメント (種類は関係ない) に影響された行の数を戻します。この戻り値を使用すれば、アカウントの更新や作成が必要かどうかを知ることができます。既存のユーザーだけが自分のフルネームやいくつかの点を変更できることを思い出してください。"username" フィールドは基本キーであり、このフィールドの変更はより制御された方法で行う必要があります。明らかにパスワードはここで変更することができますが、この処理は別の関数に分離したほうがよいように思えます。更新が完了すると、テーブルはアンロックされ、このトランザクションはlogger() によってログに記録されます。新しいアカウントを作成しなければならなくなると、テーブルはアンロックされ、パスワードが暗号化された後でこの情報がINSERT されます。このときも、操作の成功または失敗がXML-RPCクライアントに戻されます。
SQL、XML-RPCリスナー、およびクライアントのコードの行数を数えると、このミドルウェア・システムは600行以内のコードで済んでいます。XML-RPCは強力で単純なメッセージ受け渡しシステムであり、この記事で説明したようなアプリケーションにおいて際立っています。XML-RPCの後継者はSimple Object Access Protocol (SOAP) (参考文献を参照) であり、この記事で説明した一部の機能が拡張されています。SOAPについては、次の連載記事で扱います。XML-RPCについて詳しくは、ホーム・ページ (参考文献を参照) をご覧ください。
- Web Services、XML-RPC、およびPerlについて扱った最初の記事 (Getting Started with XML-RPC in Perl) を参照してください。
- XML-RPCの完全な概要については、XML-RPCホーム・ページにアクセスしてください。
-
PHP の資料を参照してください。
- MySQLの詳細については、MySQLホーム・ページにアクセスしてください。
Joe Johnston (jjohn@cs.umb.edu) は、昼間はO'Reilly and Associatesでプログラマーとして勤務しています。愛猫がキーボードの上にいないときには、Perl Journal、use.perl.org、www.perl.com、およびO'Reilly Networkのために記事を書いています。また、Michael Lordと協力して、ユーモラスなUFO伝説のサイトAliens, Aliens, Aliens も作成しました。