JSF2.3の新機能 <f:websocket>について
1. WebSocketのエンドポイントの定義
package html5.asia.push; import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.faces.push.Push; import javax.faces.push.PushContext; import javax.inject.Inject; import javax.inject.Named; @Named("samplePush") @ApplicationScoped public class SamplePushBean implements Serializable { @Inject @Push(channel = "sample") private PushContext push; synchronized public void sendMessage(String message) { push.send(message); } }
16行目で、PushContextクラスのメンバ変数を定義します。Pushアノテーションを付加することにより、エンドポイントが定義されます(エンドポイントは、[サーバ名:ポート番号/コンテキスト名/javax.faces.push/sample]となります)。19行目のsendメソッドを使用することで、WebSocket経由でメッセージを送ることができます。すごくシンプルですね。WebSocket通信するのに、メンバ変数を定義してメソッドを呼んだいるだけです。たったそれだけでWebSocket通信できるのだから。
2. APIをJCDI対応化する
package html5.asia.api; import html5.asia.push.SamplePushBean; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; @RequestScoped @Path("/v1/api_demo") public class SampleApi { @Inject SamplePushBean samplePushBean; @GET @Produces("text/json") public String sendMessage(@QueryParam("message") String message) { samplePushBean.sendMessage(message); return "{}"; } }
APIの処理しては、21行目で、messageリクエストパラメータの値をそのまま、WebSocket通信経由で送信しているだけです。SampleApiのインスタンスは、11行の「RequestScoped」アノテーションを付加することにより、JCDI対応化します。よってsamplePushBeanには、JCDI管理されたBeanが割り当てられます。何が便利だというと、外部からのAPI呼出しで、WebSocket機能の使用を、より簡単に、より少ないコーディングで実現することがでます。当初、samplePushBeanにインスタンスが割当られなくて大変苦労しましたが、IBMの「依存性の注入を使用した JAX-RS リソースの実装」のブログで見つけた、「RequestScoped」アノテーションを付加してJCDI対応化するという発想は、目からうろこが落ちた感じです。IBMのブログは、10数年前から利用していますが、質がともて高く英語版もあるので、英語の勉強にもなります。
3. JSF側の実装
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width; initial-scale=1.0" /> <title>Websocketのサンプル</title> </h:head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script> //<![CDATA[ function socketListener(message, channel, event) { message = message .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); $('#message').append(message + '<br/>'); } //]]> </script> <h:body> 初めての「f:websocket」 <f:websocket channel="sample" onmessage="socketListener"/> <div id="message"/> </h:body> </html>
26行目を解説すると、「channel="sample"」は、SamplePushBeanの「@Push(channel = "sample")」に対応します。そのエンドポイントのリスナーは、13行目のsocketListener()メソッドであるということです。20行目で、WebSocket経由で送られてきたメッセージを、27行目の<div>要素の後に追加するだけですが、メッセージの値に応じで、処理を行ったり、JavaScriptを使用して画面遷移をしたりすることも可能です。フロントサイド、バックサイド含めていたって簡単にWebSokectを利用できることがお分かりになると思います。ここでは、詳しくは解説しませんが、JSFの機能を利用してサーバサイド側の処理を行うこともできます(JSFを実務で使用した経験がある人にとっては、説明を聞くまでもないでしょうが)。JSFの機能を利用してサーバサイド側の処理を行うことについては、JSFについてご存知ない方のために、ブログに書く予定です。
4. サンプルWEBアプリケーションの実行
- ダウンロードボタンから「jsf_demo.war」ファイルをダウンロードして下さい warファイルダウンロード
- 「jsf_demo.war」ファイルをWildflyサーバにデプロイして、再起動して下さい。
- ブラウザで、「http://localhost:8080/jsf_demo/faces/demo.xhtml」のURLを表示して下さい。
- 別のブラウザで、「http://localhost:8080/jsf_demo/api/v1/api_demo?message=Hello!」のURLを表示して下さい。
- 「Hello!」と画面にメッセージが追加されます。
5. 応用例
今回紹介したWebSocket機能とGoogle Home、IFTTTを連携させて、IFTTT側でAPIを呼び出せば、WEB画面で抽選会を行うことがでます。「抽選を開始して」と声を出して抽選を開始し、WEB画面に当選番号を表示することや、当選した人のブラウザのみに、「Aさん当選おめでとうございます」のメッセージを出すことも可能です。