リバース Ajax: 第 5 回 イベント駆動型 Web 開発

Ajax リバースの手法を適用したイベント駆動型 Web アプリケーションを開発する方法を探るこの連載では、第 1 回でリバース Ajax 通信を実装するさまざまな方法を紹介し、第 2 回で WebSocket を使用してリバース Ajax を実装する方法と、Comet と WeSocket を使用する場合の制約事項について検討しました。第 3 回では、独自の Comet または WebSocket 通信システムを実装する難しさについて説明した上で Socket.IO を紹介し、第 4 回では Atmosphere と CometD を紹介しました。連載最終回となる今回の記事では、イベント駆動型の開発について学び、記事に付属のソース・コードを使って、イベント駆動型のサンプル Web アプリケーションを構築します。

Mathieu Carbou, Java Web Architect, Ovea

Mathieu Carbou photoMathieu Carbou は、Ovea の Java Web アーキテクト兼コンサルタントとしてサービスおよび開発ソリューションを提供しています。彼は複数のオープンソース・プロジェクトでコミッターやリーダーを務め、講演を行うとともに、モントリオールの Java ユーザー・グループのリーダーとしても活躍しています。コード設計およびベスト・プラクティスに経験を積んでいる彼は、クライアント・サイドからバックエンドに至るまでのイベント駆動型 Web 開発のスペシャリストです。極めてスケーラブルな Web アプリケーションでのイベント駆動型およびメッセージング・ソリューションを提供することに専念しています。彼のブログを読んでください。



2011年 10月 21日

はじめに

リバース Ajax 手法を適用したイベント駆動型 Web アプリケーションの開発方法を探るこの連載では、第 1 回でリバース Ajax、ポーリング、ストリーミング、Comet、そしてロング・ポーリングを紹介しました。第 2 回では、WebSocket を使用してリバース Ajax を実装する方法を紹介し、Comet と WebSocket を使用した Web サーバーの制約について検討しました。そして、第 3 回では複数のサーバーをサポートする必要や、ユーザーが自分のサーバーにデプロイできる独立した Web アプリケーションを提供する必要がある場合に、独自の Comet または WebSocket 通信を実装する難しさを説明し、Socket.IO についても取り上げました。第 4 回では、Java 技術サーバー対応のオープンソースのリバース Ajax ライブラリーとして最も知名度の高い Atmosphere および CometD を取り上げました。

このように、これまでに学んできたのは、イベントによって通信するコンポーネントを作成する方法です。連載の最終回となる今回の記事では、イベント駆動型開発の原則を適用し、イベント駆動型のサンプル Web アプリケーションを構築します。

記事で使用するソース・コードは、ここからダウンロードすることができます。

前提条件

この記事を最大限有効に活用するには、JavaScript および Java の知識と Web 開発の経験があると理想的です。記事のサンプル・コードを実行するには、最新バージョンの Maven および JDK も必要になります (「参考文献」を参照)。


用語

イベント駆動型アーキテクチャー (EDA: Event-Driven Architecture)、EventBus システム、メッセージング・システム、複合イベント処理 (CEP: Complex Event Processing )、およびチャネルについてはおそらくお馴染みのことでしょう。これらの用語や概念は登場してから既にかなりの時間が経過しているため、技術が成熟するにつれ、今後は耳にする機会が多くなってくるはずです。このセクションでは、これらの概念について簡単に説明しておきます。

イベント
システムで発生した何らかの事象。イベントには通常、発生日 (タイムスタンプ)、発生源または発生場所 (クリックしたコンポーネント)、そのイベントについて説明するデータなどの属性があります。システムによっては、オプションでその他の属性をイベントに設定することができます。
イベント駆動型アーキテクチャー (EDA)
イベント・ベース・プログラミングとも呼ばれます。イベント駆動型アーキテクチャーを用いて設計されたアプリケーションは、アプリケーションを構成するコンポーネント間の通信や、コンポーネント自体の実行をイベントの送受信によって行います。Java Swing グラフィック・ユーザー・インターフェース (GUI) は EDA の一例です。Swing の各コンポーネントはイベントをリッスンし、イベントに応答して他のイベントを送信する等々の処理を行います。EDA を構成するコンポーネントには、イベント・プロデューサー、イベント・コンシューマー、イベント、およびイベント処理ソフトウェアなどがあります。
  • イベント・プロデューサー ― イベントを発行するコンポーネント。この記事の例では、フォーム送信ボタンがイベント・プロデューサーになります。
  • イベント・コンシューマー ― 特定のイベントをリッスンするコンポーネント。例えば、フォームの送信の場合には、フォーム・データをサーバーに送信するためのフォーム送信ボタンのクリックをリッスンするブラウザーが、イベント・コンシューマーになります。
  • イベント処理ソフトウェア ― このシステム中核部では、イベント・プロデューサーがイベントをパブリッシュし、イベント・コンシューマーがイベントの受信側として自らを登録します。イベント処理は、ソフトウェアによって単純な場合 (単に受信したイベントをコンシューマーに転送するだけの処理など) と、複雑な場合 (CEP (複合イベント処理: Complex Event Processing)) があります。CEP の場合、イベント処理ソフトウェアはイベントの集約、フィルタリング、変換を含め、さまざまな処理手段をサポートします。

    このようなソフトウェアの例には Esper が挙げられます。イベント処理ソフトウェアは、単独で実行されるアプリケーションである場合のみならず、アプリケーションに統合されたライブラリーである場合もあります。

メッセージング・システム
イベント・プロデューサーがメッセージをチャネルにパブリッシュし、イベント・コンシューマーがそれらのチャネルをサブスクライブする、イベント駆動型アプリケーションの一種。イベント・プロデューサーとイベント・コンシューマーの間に関連性はなく、それぞれが完全に独立しています。このようなイベント駆動型アプリケーションでは、イベントの代わりとしてメッセージという用語を使用することがよくあります。
チャネル
メッセージング・システム内でイベントを分類する手段。チャネルは、イベント・プロデューサーが意図するイベントの送信先を表します。例えば、チャット・ルームのアプリケーションに、/chatapplication/chatrooms/asdrt678 というチャネルがあるとします。このチャネルによって具体的なチャット・ルームが特定され、そのチャット・ルームでイベント・プロデューサーがメッセージを送信することができ、グラフィック・コンポーネントが新着メッセージを表示するためにそのチャット・ルームをサブスクライブすることになります。

メッセージング・システムのなかには、キューとトピックという 2 つのタイプのチャネルを用意しているものもあります。

  • キュー ― キューに配信されたメッセージを取得して処理するイベント・コンシューマーは 1 つに限られます。他のイベント・コンシューマーがそのメッセージを見ることはありません。キューは、配信を保証するために永続キューにすることができます。キューの好例は、メーリング・リクエストです。Web アプリケーションはユーザーの登録時に、メッセージをキュー /myapp/mail/user-registration にパブリッシュします。このキューは、複数のメーリング・アプリケーションがサブスクライブすることができます。サブスクライブしているメーリング・アプリケーションがなくても、メッセージが失われることはありません。
  • トピック ― メッセージがトピックに配信されると、すべてのサブスクライバーがそのメッセージを受信します。トピックは通常、永続化されません。監視ソフトウェアの場合のトピックの例は、/event/system/cpu/usage です。ここに、イベント・プロデューサーは定期的に CPU 使用状況を送信します。そのトピックに対する関心の高さによっては、メッセージを受信するサブスクライバーがいないこともあれば、複数のサブスクライバーがメッセージを受信することもあります。
パブリッシュ/サブスクライブ
イベント駆動型ソリューションは、パブリッシュ/サブスクライブ・パターンを実装します。このパターンでは、イベント・プロデューサーが処理ソフトウェアのイベントをパブリッシュし、イベント・コンシューマーがイベントの受信側としてサブスクライブします。イベント・コンシューマーのサブスクライブ方法は、ソフトウェアによって異なります。メッセージング・アプリケーションの場合、イベント・コンシューマーはチャネルをサブスクライブします (オプションで、例えばイベント・タイプにフィルタリング・ルールを適用することもあります)。Esper などの CEP では、サブスクライブする方法として、SQL のようなリクエストを使って興味のあるイベントを定義することができます。

イベント駆動型ソリューションを適用する理由

従来の通信方式では、システム A がシステム B から情報を取得する必要がある場合、システム B にリクエストを送信します。システム B がリクエストを処理する間、システム A はレスポンスを待機します。レスポンスがシステム A に返されるのは、リクエストの処理が完了した時点です。このような同期通信モードでは、レスポンスの待機中に処理時間が失われるため、リソースが効率的に利用されません。

非同期モードの場合には、システム A はシステム B から取得する必要のある情報をサブスクライブします。そして、システム A はオプションでシステム B に通知を送信し、すぐに戻ります。これで、システム A は他のあらゆる処理に対応できる状態になります。このステップはオプションです。通常、イベント駆動型アプリケーションでは、相手側のシステムにイベントを送信するよう要求する必要はありません。それは、発生するイベントには感知しないためです。システム B がレスポンスをパブリッシュすると、システム A は即時にレスポンスを受信します。

イベント駆動型アーキテクチャーの 1 つの利点は、より優れたスケーラビリティーが実現できることです。スケーラビリティーとは、システムが需要、容量、あるいは負荷の変化に適応できると同時に、システムの目的を果たし続けられることです。イベント駆動型アーキテクチャーは、中断時間を排除することで、一般にパフォーマンスに優れ、処理速度も速くなります。

アプリケーションの開発と保守に関しても利点があります。イベント駆動型ソリューションでは、アプリケーションの各コンポーネントを完全に切り離して独立させることができるからです。

イベント駆動型のソリューションでは通信の待ち時間が短縮されるため、最短の応答時間を実現することができます。


イベント駆動型ソリューションを Web へ適用する

Web フレームワークはこれまで、ページのリフレッシュを伴う従来のリクエスト・レスポンス・パターンに依存していました。Ajax とリバース Ajax、そして CometD や Atmosphere などの強力なフレームワークの登場により、今ではイベント駆動型アーキテクチャーの概念を Web に適用することで、疎結合、スケーラビリティー、応答性といった利点を容易にもたらせるようになっています。

クライアント・サイド

クライアント・サイドでは、GUI 開発にイベント駆動型アーキテクチャーを適用することができます。この場合、従来の Web ページを作成する代わりに、たった 1 つの Web ページをコンテナーとして機能させて、各コンポーネント (ページの各構成部分) を切り離すことができます。この Web ページで Java Swing の GUI を使用すれば、複数のガジェットを収容した Google ページのようになります (リンクについては「参考文献」を参照)。

このような Web ページに必要なのは、イベント・バスです。例えば、各ページ・コンポーネントがチャネルをサブスクライブし、チャネルにパブリッシュできるようにする JavaScript イベント・バスを開発することができます。イベントを同期させて、複数のイベントを受信した後にアクションをトリガーすることも可能です。ページ内でのローカル・イベントにはイベント・バスを使用できる一方、CometD または Socket.IO を使用すれば、リモート・イベントをプラグインでサポートすることもできます。

サーバー・サイド

サーバー・サイドでは、イベント駆動型アーキテクチャーをサポートするリバース Ajax をセットアップする必要があります。連載のこれまでの記事で検討した手法のうち、イベント駆動型の手法を使用するのは CometD だけです。他のフレームワークにイベント駆動型の手法を適用するには、カスタム・サポートを追加する必要があり、簡単なことではありません。また、JMS (例えば、Apache ActiveMQ) などのサード・パーティーのメッセージング・システムや、Esper のような CEP を追加することもできます。それよりも単純なソリューションとなるのは、基本的なパブリッシュ/サブスクライブをサポートする Redis です。

この連載はイベント駆動型 Web およびリバース Ajax を話題にしているため、以下の例ではクライアントの部分に専念し、複雑なメッセージング・システムはセットアップしません。


イベント駆動型 Web の例

この記事で作成するサンプル・アプリケーションは、接続中のユーザー・リストをユーザー・パネルに表示するチャット・ルーム Web アプリケーションです。このアプリケーションでは、ユーザー本人の名前は太字で表示され、アクティブなユーザー (20 秒経過してもアクティブなユーザー) は緑色、20 秒経過後に非アクティブなユーザーはオレンジ色で表示されます。ユーザーが接続または接続解除すると、ユーザーのリストが最新の状態に更新されます。

セキュリティー上の理由から、web.xml ファイルに 2 分のセッション・タイムアウトを構成します。2 分間、何の操作も行わなければポップアップを表示して、ユーザーをログイン・ページにリダイレクトするようにします。

実行中のセッションがなくなると同時に、ユーザーはログイン・ページにリダイレクトされます。これは、ユーザーがまだ接続していない場合も同じです。ログイン・ページはユーザー名を入力するよう求め、入力されたユーザー名を使ってユーザーがチャット・ルームにログインできるかどうかを調べます。

ログインに成功すると、ユーザーはチャット・ルームですべてのユーザーにメッセージを送信できるようになります。コンソールも表示され、コンソールには受信したすべてのイベントがログとして記録されます。

この Web アプリケーションは、イベント・ベースのアプリケーションです。上記の情報から簡単に、以下のイベントを定義することができるはずです。

  • ユーザーの接続
  • ユーザーの接続解除
  • セッションの有効期限切れ
  • チャット・メッセージの受信
  • セキュリティー・フィルターによるリクエストのブロック (ユーザーがログインしていない場合)
  • ユーザーの非アクティブ化
  • ユーザーのアクティブ化
  • UI 調整に関するその他すべてのイベント

一部のイベントは、Web アプリケーション内に限られたローカル・イベントです。これらのイベントは、ローカル・バスによって識別されます (リスト 1 を参照)。

リスト 1. バスのセットアップ
bus = { 
 
    local: new EventBus({ 
        name: 'EventBus Local' 
    }), 
 
    remote: EventBus.cometd({ 
        name: 'EventBus Remote', 
        logLevel: 'warn', 
        url: document.location.href.substring(0, 
            document.location.href.length - 
            document.location.pathname.length) + '/async', 
        onConnect: function() { 
            bus.local.topic('/event/bus/remote/connected').publish(); 
        }, 
        onDisconnect: function() { 
            bus.local.topic('/event/bus/remote/disconnected').publish(); 
        } 
    }) 
 
};

その他のイベントはリモート・イベントです。つまり、これらのイベントをすべてのクライアントにパブリッシュするには、CometD などのリバース Ajax システムが必要になります。図 1 に、このサンプル・アプリケーションを示します。

図 1. サンプル・アプリケーション
サンプル・アプリケーションのスクリーン・ショット

サンプル・アプリケーションはダウンロードすることができます。クラスの多くは、セキュリティーのための基本的なクラスであるか、セッションおよびユーザー管理のためのクラスです。この記事にはコードの最も重要な部分を記載しますが、このサンプル・アプリケーションが動作する仕組みをより深く調べるためには、サンプル・アプリケーションをダウンロードして実行することをお勧めします。

サンプル Web アプリケーションは、チャット・ルーム、ユーザー・リスト、そしてコンソールの各コンポーネントで構成されます。それぞれのコンポーネントは完全に独立しているため、いずれかのコンポーネントが削除されたとしても、他のコンポーネントには影響しません。

イベント駆動型システムをローカルおよびリモートにセットアップするために、この例では Ovea の EventBus システムを使用します。EventBus システムは、ローカル・イベント・バス、および CometD がリモート・イベントを取得するためのブリッジになるとともに、(複数のイベントが完了した後にアクションをトリガーするために) イベントを調整するための手段を提供します。もちろん、別のシステムを選択する場合には、そのシステムを代わりに使用しても構いません。リスト 1 に記載されているのは、JavaScript でセットアップする場合の例です。

バスが配置されると、アプリケーションおよびコンポーネントはイベント・ベースになります。この例では、IDLE 検出システムをセットアップしています (リスト 2 を参照)。

リスト 2. IDLE 検出システム
bus.local.topic('/event/dom/loaded').subscribe(function() { 
    $.idleTimer(20000); 
    $(document).bind('idle.idleTimer', function() { 
        bus.local.topic('/event/idle').publish('inactive'); 
    }); 
    $(document).bind('active.idleTimer', function() { 
        bus.local.topic('/event/idle').publish('active'); 
    }); 
})

リスト 2 のコードでは、IDLE システムはアクティビティーを検出するとイベントを送信します。このコードは、IDLE システムを必要とするあらゆるアプリケーションに適用することができます。このサンプル・アプリケーションの場合、上記のコードをユーザー・アクティビティーのリモート・イベントの中で変換する必要があります。この変換についても、JavaScript で行っています (リスト 3 を参照)。

リスト 3. ユーザー・アクティビティーの管理
bus.local.topic('/event/idle').subscribe(function(status) { 
    bus.remote.topic('/event/user/status/changed').publish({ 
        status: status == 'active' ? 'online' : 'away' 
    }); 
}); 
 
bus.remote.topic('/event/user/status/changed').subscribe(function(evt) { 
    if(evt.user != me.name) { 
        $('#users li').filter(function() { 
            return evt.user == $(this).data('user').name; 
        }).removeClass('online')
          .removeClass('away')
          .addClass(evt.status); 
    } 
});

最初のサブスクリプションは IDLE システムからイベントを受信すると、ユーザーの状態をサーバーに送信します。他のサブスクリプションは、サーバーからユーザーの状態イベントを受信するため、ユーザーの状態が変化すると同時に、ユーザー・リストのユーザーの色が緑またはオレンジに変わります。

ユーザーが接続または接続解除した場合にも、イベントが送信されます (リスト 4 を参照)。

リスト 4. ユーザー・リストの管理
bus.remote.topic('/event/user/connected').subscribe(function(user) { 
    $('#users ul').append(row(user)); 
}); 
 
bus.remote.topic('/event/user/disconnected').subscribe(function(evt) { 
    $('#users li').filter(function() { 
        return evt.user == $(this).data('user').name; 
    }).remove(); 
});

このアプリケーションのコードは、単純な上に疎結合で互いに独立しています。Ovea の技術を大幅に再利用することで、イベント駆動型 Web アプリケーションを迅速に作成することができます。けれども、皆さんにはその必要はありません。それは、代わりに使用できる別のシステムが既にあるからです。このサンプル・アプリケーションの開発に費やしたのは、たった 1 日だけです。その半分は、以下をはじめとする基本的なコードの部分に関する作業でした。

  • Maven (プロジェクトのビルドに使用)
  • セキュリティー機能 (ログイン、ログアウト、セッション・タイムアウト)
  • Jersey を使用した REST サービス

まとめ

この連載では、快適なユーザー・エクスペリエンスを提供する、応答性に優れたスケーラブルなアプリケーションを構築する方法を紹介してきました。イベント・ベースの Web アプリケーションという概念は、かなり新しいものです。これらの概念を内部で使用している Web フレームワークもありますが、この記事では、Web フレームワークがなくてもイベント・ベースの Web アプリケーションを構築できることを明らかにしました。Java 開発者と Web デザイナーの責任を切り分けるには、充実した専用のライブラリーを使用するだけで十分です。この連載から、イベント駆動型の開発を日常のルーチンの一部として取り込む方法について深く理解していだだけたことを願います。イベント駆動型の開発を一度試してください。試してみたが最後、病みつきになって従来のフレームワークには戻れなくなるはずです。


ダウンロード

内容ファイル名サイズ
Article source codereverse_ajaxpt5_source.zip925KB

参考文献

学ぶために

製品や技術を入手するために

  • Ovea の GitHub で、Ovea による CometD プロジェクトのコントリビューションを調べてください。
  • JavaScript イベント・バス (Ovea の GitHub): ローカルおよびリモート両方での JavaScript のイベント駆動型ソリューションを入手してください。
  • JavaScript イベント同期 (Ovea の GitHub): 非同期イベントを同期します。
  • Esper: 複合イベント処理ソフトウェアを入手してください。
  • Apache から ActiveMQ メッセージング・システムを入手してください。
  • Redis: 非同期機能を備えたメモリー・キャッシング・システムを入手してください。
  • イベント駆動型のサンプル Web アプリケーション: この記事で使用したサンプル・アプリケーションのソース・コードを入手してください。
  • CometD ソース・コード: Web メッセージングのためのスケーラブルな Comet (サーバー・プッシュ) 実装である CometD プロジェクトを入手してください。
  • CometD 拡張機能: Ovea から CometD 拡張機能を入手して、Jetty 8 WebSocket サポートおよび Google Guice サポートを追加してください。
  • Jetty: WebSocket のサポートを備えた Web サーバー兼 javax.servlet コンテナーである Jetty を入手してください。
  • Apache Tomcat Advanced I/O (資料): 価値ある Links、User Guide、Reference、Apache Tomcat Development を参照してください。
  • Grizzly NIO フレームワーク:Java NIO API を利用する上で役立ちます。
  • Grizzly Comet のサンプル: 新しいアプリケーションを一から構築する前に、既存のアプリケーションに必要な変更内容を確かめてください。
  • Glassfish アプリケーション・サーバー: メインの GlassFish Server Open Source Edition を入手してください。
  • Socket.IO Java: 元のプロジェクトと Ovea での最新更新版を入手してください。
  • Apache Maven: ソフトウェア・プロジェクト管理および認識ツール、Maven を入手してください。
  • Java Development Kit バージョン 6: デスクトップ環境、サーバー環境、そして最近必要とされる組み込み環境で Java アプリケーションを開発、デプロイするために使用できる Java SE (Java Platform, Standard Edition) を入手してください。
  • IBM のソフトウェアを無料で試してみてください。試用版をダウンロードすることも、オンライン評価版にログインすることも、Sandbox 環境で製品を操作することも、クラウドを介して IBM 製品にアクセスすることもできます。100 を超える IBM 製品の評価版のなかから選ぶことができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=765695
ArticleTitle=リバース Ajax: 第 5 回 イベント駆動型 Web 開発
publish-date=10212011