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さん当選おめでとうございます」のメッセージを出すことも可能です。