
FlashプラットフォームとFlexによって、今までにないユーザーエクスペリエンスの提供が可能になります。その理由は5つあります:
表現力(Expressiveness)
パフォーマンス(Performance)
リアルタイム通信(Real time)
リッチメディア(Rich Media)
オフライン・デスクトップ(Offline/Desktop)
このセッションでは、Flex Data Services 2(以下FDS)とFlash Media Server(以下FMS)を使うことによってリアルタイム通信とビデオを含むリッチメディアをユーザーエクスペリエンスに付加する実例を紹介します。
FDSが提供するサービスは大きく3つあります:
RPCサービス
メッセージサービス
データマネジメントサービス
FMSが提供するサービスは:
メディアサービス
リアルタイム通信は、従来クライアントからサーバーに対して高頻度でリクエストを送ることにより、擬似的に実現されていました。しかし、あまり頻度が高いとサーバー負荷が問題になるため、真のリアルタイムとは呼べないものでした。また、サーバー側でデータが更新されていないときには、結果的に無駄なリクエストを延々と送ることになり、非効率的でした。実は、FMSとFlashクライアントを組み合わせることでもリアルタイム通信は可能ですが、FDSとはプログラミングモデルが違い、メッセージを扱うコードを大量に記述する必要がありました。FDSを使うとメッセージはより抽象的なレベルで扱えるため、少ないコード量でリアルタイム通信を実装できます。さらに、FDSはJ2EEを基盤として動作するため、既存のJ2EEセキュリティやJMSメッセージングの仕組みをそのまま利用してアプリケーションを開発することができる、つまりJavaと統合しやすいのも利点となります。
ここでリアルタイム通信を使ったアプリケーションの例を3つ紹介します:
このようなアプリケーションを実現可能にするのがFDSに含まれるメッセージサービス。その役目は、あるメッセージが、そのメッセージにサブスクライブ(購読)しているクライアントすべてにきちんと転送されるようにすることです。このメッセージはFDSに同梱されるJMSアダプターを介して、JMSトピックとしてJMSプロバイダに渡すことも可能です。 これから、シンプルなチャットアプリケーションを見ながら、FDSがどのようにリアルタイムメッセージを処理するかを見ていきます。
シンプルなチャットアプリケーションを3つのブラウザウィンドウで開きます。Jean、Paul、Ericがそれぞれメッセージを送信します。これは1つのチャットデスティネーションを3者が参照しているモデルです。なので、1人のメッセージは全員に届きます。ソースを見てみましょう。Consumer(コンシューマ、つまり購読者)とProducer(発行者)というクラス(MXMLタグ)があり、Consumerにはメッセージを受け取ったときに呼び出すメッセージハンドラを指定しています。
このままだと、たとえばYahoo! Messengerのような大規模なメッセージサービスは構築できません、なぜならば、何百万人もの人が、知らない人も含めて互いに膨大なメッセージを送りあい、それらがすべてアプリケーションに表示されてしまうからです。また、数多くある株式情報から自分の興味のあるものだけを取り出してリアルタイムで見る、ということもできません。これを解決するには、クライアントがメッセージ全体のサブセット(一部)にサブスクライブするか、あるいはログインしているユーザー全体のなかの、選択したごく一部のサブセットだけに対してメッセージを選択的に送る必要があります。
そこでサブトピック(subtopics)という機能が必要になります。ProducerとConsumerクラスは、サブトピックに対してパブリッシュまたはサブスクライブすることが可能です。また、そのサブトピックはあらかじめ宣言しておく必要は無く、実行時に動的に指定することができます。ただし、サブトピックを使うには、デスティネーションの設定にその旨を指定する必要があります。
ソースを見てみます。
buddyIdというフォーム要素を追加し、「お友達」IDを入力するとして、Producerにサブトピックを指定します。これは動的に指定が可能です:
Producer.subtopic = buddyId.text
さらに、自分のIDはuserIdというフォーム要素に入力されるとして、Consumerにサブトピックを追加します:
Consumer.subtopic = userId.text
これは、「私は自分のIDがサブトピックになっているメッセージしか受け取りません」という宣言です。このようにしてチャットアプリケーションを動かすと、JohnはPaulを受取人に指定してメッセージを送ることができ、そのメッセージはEricには届きません。
これには、あらかじめ”flex-message-service.xml”設定ファイル内でdestinationの設定をしておく必要があります:
allow-subtopic:true
subtopic-separator:"."
チャットアプリケーションでは、誰がオンラインなのか、あるいはいつログアウトしたのか、そのたユーザーの状態を「お友達リスト」に表示したい場合があります。お友達リストを追加してみます。これにはFDSのDataServicesオブジェクトを使います(コード抜粋):
--
<mx:DataServices id="ds" destination="chat.buddies"/>
<mx:ArrayCollection id="buddies"/>
--
ds.fill(buddies, userId.text)
--
<mx:List id="List" dataProvider="{buddies}"
--
producer..subtopic=list.selectedItem.buddyId
--
これで、自分がログインした時点でログインを一度でもしたお友達すべてがリスト上に表示されることになります。
では、ユーザーがログインしたりログオフしたりするのをリアルタイムで表示するにはどうすればよいでしょうか。FlexSessionListenerクラスには、sessionCreatedとsessionDestroyedというコールバックメソッドが存在するので、それを利用します。これはFlexSessionオブジェクトの生成や破棄に反応します。
ところで、メッセージアダプターをFlexSessionListenerとして使うこともできます。例えば、チャットから不適切な発言を削除したい場合、”flex-message-service.xml”で”cleanchat”をアダプター参照として指定します。実際のアダプターはJava Classとして記述し、大丈夫なときは:
msgService.pushMessageToClients(message, true);
を呼び出します。ダメなときはnullを返します。(デモ:”I like Ajax”と送信するとメッセージが届かない。クラスを見てみると、”Ajax”が禁止キーワードとして指定されている)
さて、FlexSessionListnerインターフェイスをimplementすると、ログイン時にユーザーのIDを取得することができます。ただし、ユーザーID自体をお友達の状態リストに表示しても意味が無いため、ハッシュマップを使ってユーザー名と関連付けます。そして、そのユーザーがお友達リストに登録されているユーザーにだけ、そのユーザーがオンラインになった、あるいはステータス変更があったことを告知したいわけです。
それにはやはりサブトピックを使います(例:chat.chrystoph)。すべての友達リスト上のユーザーごとに、サブトピックを作成する必要があります。それには、友達リストをループスルーし、新規Consumerを作成して「chat.status.名前」というサブトピックを作成します。
これで、チャットをしていた友達がブラウザを閉じたり、他のページに行った瞬間にその情報が更新されます。
このように、メッセージサービスを使った友達リストの管理方法もありますが、その情報を「アクティブセッション」を示すデータセットに格納しておき、そのデータセットを全ユーザーが購読する、という方法もあります。この場合、全員が同じコードセットを参照しているため、それに変化(チャットから抜ける、あるいは状態が変化する)があればすぐさますべてが更新されます。この方法の利点は、コードをまったく書かなくても自動的にデータが同期されるという点です。
チャットにビデオ会議機能を追加してみます。リストからChrystophの名前をクリックすると、そのビデオが表示されるようにします。ライブカメラの画像をFMSサーバーにパブリッシュするのは非常に簡単で、ストリーム名を指定するだけです。
実際に小さなテストアプリケーションでストリーム名をフィールドに指定して、パブリッシュする。もうひとつアプリケーションで、同じストリーム名を指定し、playを押すと、そのストリームが再生される。
再生側アプリのコード(抜粋):
<mx:Button label="Play" click="vid.source='rtmp://localhost/flex_videoconference/'+ streamName.text"/>
ライブビデオだけではなく、ビデオをサーバーに録画保存し、あとで再生するビデオブログもできます。これは実質的に1行付け加えるだけ。ストリームをパブリッシュするときに:
nsPublish.publish(streamName.text, "record");
この”record”というパラメータを指定するだけです。