リバース Ajax: 第 4 回 Atmosphere と CometD

Java サーバーに最も適したオープンソースのリバース Ajax ライブラリーを使用する方法を学んでください

Ajax リバースの手法を適用したイベント駆動型 Web アプリケーションを開発する方法を探るこの連載では、第 1 回でリバース Ajax 通信を実装するさまざまな方法を紹介し、第 2 回で WebSocket を使用してリバース Ajax を実装する方法と、Comet と WeSocket を使用する場合の制約事項について検討しました。第 3 回では、複数のサーバーをサポートする必要や、ユーザーが自分のサーバーにデプロイできる独立した Web アプリケーションを提供する必要がある場合に、独自の Comet または WebSocket 通信システムを実装する難しさについて説明し、最後に Socket.IO を紹介しました。今回の記事では、Java サーバーを対象としたオープンソースのリバース Ajax ライブラリーとして最も有名な Atmosphere と CometD について学んでください。

Mathieu Carbou, Java Web Architect, Ovea

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



2011年 10月 14日

はじめに

リバース Ajax 手法を適用したイベント駆動型 Web アプリケーションの開発方法を探るこの連載では、第 1 回でリバース Ajax、ポーリング、ストリーミング、Comet、そしてロング・ポーリングを紹介しました。第 2 回では、WebSocket を使用してリバース Ajax を実装する方法を紹介し、Comet と WebSocket を使用した Web サーバーの制約について検討しました。そして、第 3 回では複数のサーバーをサポートする必要や、ユーザーが自分のサーバーにデプロイできる独立した Web アプリケーションを提供する必要がある場合に、独自の Comet または WebSocket 通信を実装する上での難しさを説明しました。クライアント・サイドの JavaScript コードは単純だとしても、例外処理、再接続、確認応答の機能が必要になります。一方サーバー・サイドでは、グローバル API がなく、複数の Web サーバー API があることから、抽象化のためのフレームワークが必要になってきます。そこで、第 3 回では Socket.IO についても取り上げました。

今回の記事では、Atmosphere と CometD について学びます。この 2 つは、Java サーバーを対象としたリバース Ajax ライブラリーとして最も知名度の高いオープンソースのライブラリーです。

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

前提条件

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


Atmosphere フレームワーク

Atmosphere は、Web サーバーの多くが持つ Comet 機能と WebSocket 機能を使用するための共通 API を提供する Java 技術フレームワークです。このフレームワークは、Tomcat、Jetty、GlassFish、Weblogic、Grizzly、JBossWeb、JBoss、Resin の他、Servlet 3.0 仕様をサポートするあらゆる Web サーバーをサポートします。この記事で紹介する 2 つのフレームワークのうち、大半のサーバーをサポートするのは Atmosphere のほうです。

Atmosphere は、(Comet および WebSocket 用の) ネイティブ・サーバー API を検出することができます。Comet に対しては、Servlet 3.0 (使用可能な場合) に切り替えるか、あるいは「管理された」非同期モードにフォールバックします (ただし、Jetty Continuations ほどにはスケーラブルではありません)。Atmosphere が登場してからすでに 2 年以上が経過していますが、その開発は今でも活発に進められています。Atmosphere は大規模な Web アプリケーションで使用されており、その一例には最もよく知られた問題追跡ツールの 1 つ、JIRA が挙げられます。図 1 に、Atmosphere のアーキテクチャーを示します。

図 1. Atmosphere のアーキテクチャー
Atmosphere のアーキテクチャー

Atmosphere フレームワークを構成するのは、Atmosphere ランタイムです。Atmosphere ランタイムは、さまざまに異なる Web サーバーのソリューションおよび標準のすべてに共通する API を提供します。このランタイムをベースに、クライアントは単なるサーブレットをセットアップすることで、GWT (Google Web Toolkit) を介して API およびリバース Ajax の機能にアクセスすることができます。あるいは、JSR-311 (JAX-RS 仕様) を実装するフレームワークである Jersey を使用することもできるので、アノテーションを追加した Restful なサービスで Atmosphere を使用することができます。モジュールを選択して構成した後、いくつかのクラス (この記事で追って説明します) を実装すると、Atmosphere ランタイムにアクセスできるようになります。さらにオプションで、クラスタリング、メッセージング、依存性の注入などのサポートを追加するために用意されたプラグインを使用することもできます。Web フレームワーク (Wicket、Struts、Spring MVC) を使用している場合、Atmosphere の MeteorServlet を使用すれば、リバース Ajax サポートをトランスペアレントに追加することができます。このサーブレットが公開する Meteor オブジェクトをコントローラーおよびサービス内で取得して、リクエストを一時停止または再開することができます。

Atmosphere の利点は、サーバー・サイドにあります。その利点とは、WebSocket または Comet と通信するためのあらゆる手段およびソリューションを網羅した標準 API が提供されることです。Atmosphere はクライアントとサーバーとの間でプロトコルを使用することはしません。この点は、Socket.IO や CometD とは異なります。この 2 つのライブラリーは、(ハンドシェーク、メッセージング、確認応答、およびハートビートに) 特定のプロトコルを使用して通信するクライアント・サイドの JavaScript とサーバー・サイドのサーブレットを提供しますが、Atmosphere で目標としているのはサーバー・サイドに共通の通信チャネルを提供することです。したがって、例えば Bayeux (CometD で使用するプロトコル) などの特定のプロトコルを使用する必要がある場合、Atmosphere では独自の「ハンドラー」を開発しなければなりません。このハンドラーの役目を果たすのが、CometD プラグインです。CometD プラグインは、Atmosphere の API を利用してリクエストを一時停止および再開する一方、Bayeux プロトコルを使用した CometD 通信の管理については CometD クラスに委任します。

接続のセットアップを容易にするために、Atmosphere には jQuery クライアント・ライブラリーが付属しています。このライブラリーは、使用可能なトランスポートのうち最も優れたトランスポート (WebSocket または CometD) を自動的に検出することができます。Atmosphere の jQuery プラグインを使用する方法は、HTML5 WebSockets API と同様で、まずサーバーに接続し、メッセージを受信するためのコールバックを登録すると、データをプッシュできるようになります。

この記事に付属のソース・コードに、Atmosphere サーブレットで直接ハンドラーを使用したサンプル Atmosphere が用意されています。クライアント・コードは常に同じです。つまり、連載の第 1 回、第 2 回、第 3 回で使用したコード (Comet ロング・ポーリングによるサンプル・チャット・アプリケーションでのコード) と何も変わりません。Atmosphere の jQuery プラグインを使用することも可能ですが、Atmosphere では一切の通信プロトコルを強制しないため、その必要はありません。Atmosphere プロジェクトの他のサンプルも見てください。特に、JSR-311 アノテーション (Jersey) を使用したサンプル・アプリケーションを確認することをお勧めします。これらのアノテーションによって、ハンドラーの作成が実に単純化されます。

リスト 1 に、Atmosphere ハンドラー・インターフェースを記載します。

リスト 1. AtmosphereHandler インターフェース
public interface AtmosphereHandler<F, G> { 
    void onRequest(AtmosphereResource<F, G> resource) 
        throws IOException; 
    void onStateChange(AtmosphereResourceEvent<F, G> event) 
        throws IOException; 
    void destroy(); 
}

onRequest メソッドは、クライアントからのリクエストをすべて受信して、そのリクエストを一時停止するか、再開するか (あるいは何もしないか) を決定します。リクエストが一時停止または再開される時点、ブロードキャストが送信される時点、あるいはタイムアウトが発生した時点で、常にイベントが送信され、onStateChange メソッドがそのイベントを受信します。

リスト 2 に、Comet チャット・アプリケーションの onRequest メソッドの実装を記載します。

リスト 2. AtmosphereHandler インターフェース ― onRequest
Broadcaster broadcaster = 
        BroadcasterFactory.getDefault().lookup(
        DefaultBroadcaster.class, ChatHandler.class.getName(), true); 
broadcaster.setScope(Broadcaster.SCOPE.APPLICATION); 
resource.setBroadcaster(broadcaster); 
HttpServletRequest req = resource.getRequest(); 
String user = (String) req.getSession().getAttribute("user"); 
if (user != null) { 
    if ("GET".equals(req.getMethod())) { 
        resource.suspend(-1, false); 
    } else if ("POST".equals(req.getMethod())) { 
        String cmd = req.getParameter("cmd"); 
        String message = req.getParameter("message"); 
        if ("disconnect".equals(cmd)) { 
            close(resource); 
        } else if (message != null && message.trim().length() > 0) {
            broadcaster.broadcast("[" + user + "] " + message); 
        } 
    } 
}

通常の規定では、GET リクエストを一時停止し、POST リクエストを使ってメッセージを送信します。受信されたメッセージは、ブロードキャスターに登録されたすべてのリソースにブロードキャストされます。このサンプル・コードでは、HttpServlet 出力ストリームに何も書き込まないことに注意してください。ブロードキャストまたは一時停止のアクションは、実装されているもう一方のメソッドが受信したイベントを送信するだけに過ぎません (リスト 3 を参照)。

リスト 3. AtmosphereHandler インターフェース ― onStateChange
Broadcaster broadcaster = 
    BroadcasterFactory.getDefault().lookup(
        DefaultBroadcaster.class, ChatHandler.class.getName(), true); 
// Client closed the connection. 
if (event.isCancelled()) { 
    close(event.getResource()); 
    return; 
} 
try { 
    String message = (String) event.getMessage(); 
    if (message != null) { 
        PrintWriter writer = 
            event.getResource().getResponse().getWriter(); 
        writer.write(message); 
        writer.flush(); 
    } 
} finally { 
    if (!event.isResumedOnTimeout()) { 
        event.getResource().resume(); 
    } 
}

Comet チャット・アプリケーションを動作させるために必要なものは、これですべてです。要するに、Atmosphere の重要な概念は、接続を表すリソース・オブジェクト、そしてリソースに対してイベントを起動してリクエストを一時停止または再開するタイミングを決定するブロードキャスターということになります。ただし、このサンプル・アプリケーションは Comet だけを使用したものであることに注意してください。WebSocket と Comet の両方を使用できるようにするためには、クライアント・サイドのライブラリーを使用しなければなりません。そのため、より複雑なハンドラーが必要になってきます。

表 1 に、Atmosphere フレームワークを使用する利点と欠点をまとめます。

表 1. Atmosphere の利点と欠点
利点欠点

制御が及ばない複数の Web サーバーに Web アプリケーションをデプロイしなければならない場合、Atmosphere でサポートされる Web サーバーの数は非常に多いため、Atmosphere を使用することでアプリケーションのリバース Ajax 機能が正しく機能する可能性が高くなります。

リバース Ajax 通信と直接インターフェースを取る共通 API が必要な場合、プロトコルを開発または拡張できるようにプロトコルが定義されていません。

ソース・コードを詳しく調べたり、いくつかの付属のサンプルを分析したりする場合に役に立つ Atmosphere のアーキテクチャー、プロジェクト、概念、および API に関するドキュメントがありません。Socket.IO や CometD などの他のフレームワークが提供する単純な API と比べ、Atmosphere の API は極めて専門的で、不明瞭な点がある場合もあります。Atmosphere アノテーションを使用したとしても、一部の名前および属性はあまりにも専門的です。

サーバー・サイドでの抽象化には優れていますが、妥当なクライアント・ライブラリーがありません。プロトコルは一切ないため、追加の機能はすべて開発者に委ねられます。特にモバイル機器に取り組んでいる場合など、高度なタイムアウト検出、確認応答、バックオフ、クロスドメインなどが必要だとしても、現在のライブラリーはあまりにも単純で、大規模でスケーラブルな Web アプリケーションの要求には対応しきれません。そのような場合には、CometD のほうが遥かに信頼できます。CometD は通信プロトコルを利用して、CometD 内部で制御フローおよびエラー検出を起動することができます。追加の機能が必要な場合には、Atmosphere CometD プラグインを備えた CometD JavaScript クライアントを使用するのが有効な代替手段となるはずです。


CometD フレームワーク

CometD フレームワークは、HTTP をベースとしたイベント駆動型の通信ソリューションで、数年前に登場したものです。アノテーション構成と WebSocket のサポートは、バージョン 2 で追加されました。CometD フレームワークは、Java サーバーと Java クライアント両方の部分を提供するだけでなく、jQuery と Dojo をベースとした JavaScript クライアント・ライブラリーを提供します。CometD は Bayeux という標準化された通信プロトコルを使用して、メッセージ確認応答、フロー制御、同期、クラスタリングなどの拡張機能を起動できるようにしています。

CometD で採用しているイベント駆動型の手法は、イベント駆動型 Web 開発の新しい概念と非常に上手く適合します。従来のデスクトップ・ユーザー・インターフェースと同様に、すべてのコンポーネントはバスを介して通信を行い、通知を送信してイベントを受信します。したがって、通信はすべて非同期で行われます。

CometD フレームワークには以下の特徴があります。

  • ドキュメントが十分にあります。
  • プロジェクトを容易に立ち上げられるように、サンプルおよび Maven アーキタイプが用意されています。
  • 拡張機能の開発を可能にする、優れた設計の API を備えています。
  • Oort というクラスタリング・モジュールにより、複数の CometD Web サーバーをロード・バランサーの背後でクラスター内のノードとして実行できるため、さらに多くの HTTP 接続に応じてスケーリングすることができます。
  • セキュリティー・ポリシーをサポートし、どのユーザーにどのチャネルでメッセージの送信を許可するかをきめ細かく構成できるようになっています。
  • Spring および Google Guice (いずれも依存性注入フレームワーク) との統合にも極めて適しています。

Bayeux プロトコル

Bayeux 通信プロトコルは、主に HTTP 上でクライアントとサーバーが応答性に優れた双方向の通信を非同期で行えるようにします。Bayeux プロトコルはチャネルをベースにメッセージをルーティングして、クライアントからサーバーに、サーバーからクライアントに、あるいはクライアントからクライアントに (ただしサーバー経由で) メッセージを配信します。Bayeux はパブリッシュ/サブスクライブ・タイプのプロトコルです。CometD はこの Bayeux プロトコルを実装することにより、Comet および WebSoket トランスポートの上に Bayeux を介してリクエストをルーティングするための抽象化層を提供します。

サーバーおよび内部構造

CometD には、JSON、JSONP、WebSocket の 3 つのトランスポートがバンドルされています。これらのトランスポートは、Jetty Continuations と Jetty WebSocket API に依存します。CometD はデフォルトで、Jetty 6、7、および 8 で使用できる他、Servlet 3.0 仕様をサポートするあらゆるサーバーで使用することができます。トランスポートは、拡張機能と同じ方法で追加および開発することができます。例えば、Grizzly WebSocket API やその他の API をサポートするトランスポートを作成しておけば、CometD サーバーを構成する段階でそのトランスポートを追加することができます。図 2 に、CometD の主な構成要素の概要を示します。

図 2. CometD のアーキテクチャーの全体像
Bayeux プロトコルを使用して通信するクライアント・サイドのライブラリーとサーバー・サイドのライブラリーで構成された、CometD フレームワークのアーキテクチャーの全体像。CometD は拡張機能と各種のトランスポート層 (WebSocket, Ajax ロング・ポーリングなど) をサポートするとともに、サーバー・サイドで複数の API をサポートします。

図 2 には、メッセージ・チャネルにアクセスするためのセキュリティー層は示されていません。

この記事に付属のソース・コードに、CometD を使用した Web アプリケーションが含まれています。この Web アプリケーションの記述子に、サンプル・チャット・アプリケーションを対象としたリスト 4 の定義が含まれています。

リスト 4. web.xml
<servlet> 
    <servlet-name>cometd</servlet-name> 
    <servlet-class>
        org.cometd.java.annotation.AnnotationCometdServlet
    </servlet-class> 
    <async-supported>true</async-supported> 
    [...]
    <init-param> 
        <param-name>services</param-name> 
        <param-value>ChatService</param-value> 
    </init-param> 
    <init-param> 
        <param-name>transports</param-name> 
        <param-value>
            com.ovea.cometd.websocket.jetty8.Jetty8WebSocketTransport
        </param-value> 
    </init-param> 
</servlet>

CometD サーブレットは、グローバル設定を制御するオプションをいくつかサポートしていて、例えばトランスポートとサービスを設定できるようになっています。このサンプル・コードでは、Jetty 8 の WebSocket サポートを追加するという前提で、サーバー・サイドの CometD サービス・クラス ChatService が、誰もがおしゃべりできるチャット・ルームを制御します (リスト 5 を参照)。

リスト 5. CometD ChatService
@Service 
public final class ChatService { 

    @Inject 
    BayeuxServer server; 

    @PostConstruct 
    void init() { 
        server.addListener(new BayeuxServer.SessionListener() { 
            @Override 
            public void sessionAdded(ServerSession session) { 
                [...]
            } 

            @Override 
            public void sessionRemoved(ServerSession session, boolean timedout) {
                [...]
            } 
        }); 
    } 

    @Configure("/**") 
    void any(ConfigurableServerChannel channel) { 
        channel.addAuthorizer(GrantAuthorizer.GRANT_NONE); 
    } 

    @Configure("/chatroom") 
    void configure(ConfigurableServerChannel channel) { 
        channel.addAuthorizer(new Authorizer() { 
            @Override 
            public Result authorize(
                [..] // check that the user is in session
            } 
        }); 
    } 

    @Listener("/chatroom") 
    void appendUser(ServerSession remote, 
                    ServerMessage.Mutable message) { 
        [...]
    } 

}

リスト 5 には CometD の重要な機能がいくつか示されています。具体的には、以下の機能です。

  • 依存性の注入
  • ライフサイクル管理
  • グローバル・チャネル構成
  • セキュリティー管理
  • メッセージ変換 (すべてのメッセージにユーザー名を追加するため)
  • セッション管理

クライアント・サイドで、このサンプル・コードがアクティブにした拡張機能はありません。したがって、加工していないままの CometD コードです (リスト 6 を参照)。

リスト 6. CometD クライアント・コード
// First create t cometd object and configure it

var cometd = new $.Cometd('CometD chat client'); 
cometd.configure({ 
    url: document.location + 'cometd', 
    logLevel: 'debug' 
}); 
cometd.websocketEnabled = 'WebSocket' in window;

// Then we register some listeners. Meta channels (those with 
// the form /meta/<name> are specific reserved channels)

cometd.addListener('/meta/disconnect', function(message) { 
    [...]
}); 

cometd.addListener('/meta/connect', function(message) { 
    [...]
});

// Then, starting a connexion can be done using:

cometd.handshake();

// And subscriptions with:

cometd.subscribe('/chatroom', function(event) { 
    [...] //  event.data holds the message
});

// We finally send data to the chatroom like this:

cometd.publish('/chatroom', msg);

CometD のクライアント・サイド API は使用するにも、理解するにも単純なものの、強力で拡張可能であることに変わりありません。この記事では Web アプリケーションの主要な部分だけを取り上げているので、このサンプル Web アプリケーションを調べて CometD の威力を十分に理解してください。

表 2 に、CometD フレームワークを使用する利点と欠点をまとめます。

表 2. CometD の利点と欠点
利点欠点

CometD はクライアント・サイドからサーバー・サイドまで、そしてスタンドアロン Java クライアントからサーバーまでを網羅した完全なソリューションを提供します。このフレームワークには十分なドキュメントがあり、優れた API が用意されているため、簡単に使用することができます。とりわけ大きな利点は、そのイベント駆動型の手法です。そのことから、CometD と Bayeux は数多くのイベント駆動型 Web アプリケーションに組み込まれています。他のリバース Ajax フレームワークにはイベント駆動型のメカニズムがないため、エンド・ユーザーが独自のカスタム・ソリューションを開発しなければなりません。

CometD がサポートする機能には、再接続、確実なタイムアウト検出、バックオフ、バッチ処理、メッセージ確認応答、そして他のリバース Ajax フレームワークにはない機能など、多くの必要な機能があります。CometD を使用することで、最も信頼性の高い、低レイテンシーの通信を実現することができます。

現在 CometD でサポートしている Servlet 2.5 コンテナーは、Comet 対応の Jetty (Tomcat) の他にはありません。また、Glassfish/Grizzly WebSocket はサポートしていません。


まとめ

Atmosphere と CometD はどちらも頼りになるオープンソースのリバース Ajax ソリューションですが、Ovea で選んでいるのは CometD です。その理由は、Ovea ではクラスター環境でのモバイル機器を対象としたスケーラブルなイベント駆動型 Web アプリケーションを開発しており、インフラストラクチャー (Jetty を使用) を全面的に管理しているためです。その一方、Web アプリケーションを販売していて、できる限り多くのサーバーで有効なリバース Ajax 機能が必要だとしたら、追加の開発を行わない限り、CometD は最善の選択肢とは言えません。しかし、Servlet Specification 3.0 をサポートする Web コンテナーが次第に増えている現在、CometD に伴う制約は減りつつあります。トランスポート層について言えば、現在残っている主な違いは、WebSocket のサポートに依存するものです。


ダウンロード

内容ファイル名サイズ
Article source codereverse_ajaxpt4_source.zip28KB

参考文献

学ぶために

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

  • Atmosphere ソース・コード: IoC (Inversion of Control) を使用してプッシュ/Comet および Websocket を大衆にもたらす POJO ベースのフレームワーク、Atmoshphere を入手してください。
  • CometD ソース・コード: Web メッセージンのためのスケーラブルな Comet (サーバー・プッシュ) 実装である CometD プロジェクトを入手してください。
  • CometD 拡張機能: Ovea から CometD 拡張機能を入手して、Jetty 8 WebSocket サポートおよび Google Guice サポートを追加してください。
  • Jetty: WebSocket のサポートを備えた Web サーバー兼 javax.servlet コンテナー、Jetty を入手してください。
  • Apache Tomcat Advanced I/O の資料: 貴重なリンク、ユーザー・マニュアル、リファレンス、Apache Tomcat 開発マニュアルを入手してください。
  • 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=764778
ArticleTitle=リバース Ajax: 第 4 回 Atmosphere と CometD
publish-date=10142011