SpringによるWebSocketsの紹介

1概要

この記事では、Spring Framework 4.0で導入された 新しいWebSocket機能 を使用してメッセージングを実装する簡単なWebアプリケーションを作成します。

WebSocketsは、Webブラウザとサーバー間の 双方向 全二重 永続的接続 です。 WebSocket接続が確立されると、クライアントまたはサーバーがこの接続を閉じることを決定するまで、接続は開いたままになります。

典型的なユースケースは、チャットのように、アプリが互いに通信する複数のユーザーを含む場合です。

2 Mavenの依存関係

これはMavenベースのプロジェクトなので、まず pom.xml に必要な依存関係を追加します。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.2.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>4.2.4.RELEASE</version>
</dependency>

さらに、メッセージの本文を作成するために JSON を使用するので、 Jackson 依存関係を追加する必要があります。これにより、SpringはJavaオブジェクトを JSON との間で変換します。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.7.3</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.3</version>
</dependency>

上記のライブラリの最新版を入手したい場合は、https://search.maven.org/classic/[Maven Central]でそれらを探してください。

3 Spring でWebSocketを有効にする

最初にすべきことは、WebSocket機能を有効にすることです。これを行うには、アプリケーションに設定を追加し、このクラスに @ EnableWebSocketMessageBroker というアノテーションを付ける必要があります。

その名前が示すように、メッセージブローカーに支えられてWebSocketメッセージ処理を有効にします。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
         registry.addEndpoint("/chat");
         registry.addEndpoint("/chat").withSockJS();
    }
}

メソッド configureMessageBroker は、メッセージブローカーを設定するために使用されます。まず、インメモリのメッセージブローカーが「/topic」という接頭辞を付けた宛先でクライアントにメッセージを戻すことができます。

アプリケーションのアノテーション付きメソッドをターゲットとする宛先をフィルタリングするために“/app”プレフィックスを指定することでW設定( @ MessageMapping を介して)

registerStompEndpoints メソッドは“/chat”エンドポイントを登録し、 Springのhttp://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP]サポート を有効にします。弾力性のためにSockJSなしで動作するエンドポイントもここに追加していることに注意してください。

このエンドポイントは、「/app」というプレフィックスが付いている場合、 ChatController.send() メソッドが処理するようにマッピングされているエンドポイントです。

また、 SockJS フォールバックオプションを有効にします ので、WebSocketが利用できない場合は別のメッセージングオプションを使用できます。 WebSocketはまだすべてのブラウザでサポートされているわけではなく、制限的なネットワークプロキシによって除外される可能性があるため、これは便利です。

このフォールバックにより、アプリケーションはWebSocket APIを使用できるようになりますが、実行時に必要に応じてWebSocket以外の代替方法に段階的に低下します。

4メッセージモデルを作成する

プロジェクトを設定してWebSocket機能を設定したので、送信するメッセージを作成する必要があります。

エンドポイントは、本文が JSON オブジェクトであるSTOMPメッセージ内の送信者名とテキストを含むメッセージを受け入れます。

メッセージは次のようになります。

{
    "from": "John",
    "text": "Hello!"
}

テキストを伝えるメッセージをモデル化するために、 from および text プロパティを持つ単純な____ Javaオブジェクトを作成できます。

public class Message {

    private String from;
    private String text;

   //getters and setters
}

デフォルトでは、Springは Jackson ライブラリを使用してモデルオブジェクトをJSONとの間で変換します。

5メッセージ処理コントローラを作成する

これまで見てきたように、STOMPメッセージングを扱うためのSpringのアプローチは、設定されたエンドポイントにコントローラメソッドを関連付けることです。これは @ MessageMapping アノテーションによって可能になります。

エンドポイントとコントローラ間の関連付けにより、必要に応じてメッセージを処理することができます。

@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
    String time = new SimpleDateFormat("HH:mm").format(new Date());
    return new OutputMessage(message.getFrom(), message.getText(), time);
}

F.オブジェクトに送信者と受信メッセージから取得したメッセージテキストを入力し、タイムスタンプを付加します。

メッセージを処理した後、 @ SendTo アノテーションで定義された適切な宛先に送信します。 “ /topic/messages ”宛先へのすべての購読者はメッセージを受け取ります。

6. ブラウザクライアントを作成する

サーバーサイドで設定を行った後は、 sockjs-client ライブラリ を使用して、メッセージングシステムと対話する簡単なHTMLページを作成します。

まず、 sockjs および stomp Javascriptクライアントライブラリをインポートする必要があります。次に、エンドポイントとの通信を開くための connect() 関数、STOMPメッセージを送信するための sendMessage() 関数、および通信を閉じるための disconnect() 関数を作成します。

<html>
    <head>
        <title>Chat WebSocket</title>
        <script src="./js/sockjs-0.3.4.js"></script>
        <script src="./js/stomp.js"></script>
        <script type="text/javascript">
            var stompClient = null;

            function setConnected(connected) {
                document.getElementById('connect').disabled = connected;
                document.getElementById('disconnect').disabled = !connected;
                document.getElementById('conversationDiv').style.visibility
                  = connected ? 'visible' : 'hidden';
                document.getElementById('response').innerHTML = '';
            }

            function connect() {
                var socket = new SockJS('/spring-mvc-java/chat');
                stompClient = Stomp.over(socket);
                stompClient.connect({}, function(frame) {
                    setConnected(true);
                    console.log('Connected: ' + frame);
                    stompClient.subscribe('/topic/messages', function(messageOutput) {
                        showMessageOutput(JSON.parse(messageOutput.body));
                    });
                });
            }

            function disconnect() {
                if(stompClient != null) {
                    stompClient.disconnect();
                }
                setConnected(false);
                console.log("Disconnected");
            }

            function sendMessage() {
                var from = document.getElementById('from').value;
                var text = document.getElementById('text').value;
                stompClient.send("/app/chat", {},
                  JSON.stringify({'from':from, 'text':text}));
            }

            function showMessageOutput(messageOutput) {
                var response = document.getElementById('response');
                var p = document.createElement('p');
                p.style.wordWrap = 'break-word';
                p.appendChild(document.createTextNode(messageOutput.from + ": "
                  + messageOutput.text + " (" + messageOutput.time + ")"));
                response.appendChild(p);
            }
        </script>
    </head>
    <body onload="disconnect()">
        <div>
            <div>
                <input type="text" id="from" placeholder="Choose a nickname"/>
            </div>
            <br/>
            <div>
                <button id="connect" onclick="connect();">Connect</button>
                <button id="disconnect" disabled="disabled" onclick="disconnect();">
                    Disconnect
                </button>
            </div>
            <br/>
            <div id="conversationDiv">
                <input type="text" id="text" placeholder="Write a message..."/>
                <button id="sendMessage" onclick="sendMessage();">Send</button>
                <p id="response"></p>
            </div>
        </div>

    </body>
</html>

** 7. 例のテスト

**

この例をテストするために、ブラウザウィンドウをいくつか開いてチャットページにアクセスします。

http://localhost:8080/spring-mvc-java/resources/chat.html

リンク:/uploads/screenshot.png[]

8結論

このチュートリアルでは、SpringのWebSocketをサポートします。サーバーサイドの設定を見て、 sockjs stomp JavaScriptライブラリを使って簡単なクライアントサイドを構築しました。

サンプルコードはhttps://github.com/eugenp/tutorials/tree/master/spring-mvc-java[GitHubプロジェクト]にあります。