2014年4月4日金曜日

java.lang.IllegalStateExceptionの対処法

JavaEE7のRemoteEndpoint.Async.sendText()を連続して呼び出すと、java.lang.IllegalStateExceptionがスローされることがあります。

これは、sendText()の処理が終わる前に、再度sendText()が呼び出されたためです。
sendText()の処理は非同期で行われるために、メソッドが戻ってきたとしてもメッセージ送信が終了している保証はありません。

これを防ぐためには、sendText()の戻り値であるFutre型のオブジェクトのget()を呼び出して、処理が終わるのを待つ必要があります。



 public void sendMessageToBrowser(String message) {
  for (Session s : sessions) {
   Future future = s.getAsyncRemote().sendText(message);
   try {
    future.get();
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (ExecutionException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }

2014年4月1日火曜日

websocketのサンプルコードをJavaEEで書いた

JavaEE7にはwebsocketを実装したパッケージ(javax.websocket)が提供されています。

これを使ったサンプルコードを書いてみました。
ブラウザから入力された数字の数だけ、適当な文字列(数字列)を接続している全ブラウザに表示するコードです。

まずはJava側。Servletクラスとかを継承しなくていいみたいです。
注意が必要なのは、RemoteEndpoint.Async.sendText()はFuture型のオブジェクトを返すということ。つまり、sendText()メソッドの処理は非同期で行われます。

sendText()はその仕様として処理が終わる前に、再び呼び出されるとjava.lang.IllegalStateExceptionを送出します。そのため、for文などを用いて繰り返し、メッセージを送りたい場合はFuture.get()などを用いて、処理が終わるのを待ってから次のメッセージを送るようにします。

import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/endpoint")
public class Main {
    static Set<session> sessions = Collections.synchronizedSet(new HashSet<session>());
    @OnMessage
    public void onMessage(String message) {
        int count = Integer.valueOf(message);
        run(count);
    }

    @OnOpen
    public void open(Session sess) {
        sessions.add(sess);
    }

    @OnClose
    public void close(Session sess) {
        sessions.remove(sess);
    }

    public void sendMessageToBrowser(String message) {
       for (Session s : sessions) {
           Future future = s.getAsyncRemote().sendText(message);
           try {
                   future.get();
           } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
           } catch (ExecutionException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
           }
       }
    }
 
    private void run(int count){
        Random rnd = new Random();
  
        for(int i = 0; i &lt count; i++){
                 String s = String.valueOf(rnd.nextInt());
                sendMessageToBrowser(s);
        }
    } 
}

続いてhtml。

<!DOCTYPE html>
<html>
    <head>
        <title>WebSocketテスト</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="http://code.jquery.com/jquery-1.9.0.js"></script>
        <script type="text/javascript">
            var socket;
            $(document).ready(function(){
             var server="ws://localhost:8080/temporary/endpoint";
                socket = new WebSocket(server);
                
                socket.onmessage = function(message){
                    $('#messageFromServer').append(message.data + "<br/>");
                };

                $('#send').click(function(){
                    var text = $('#msg').val();
                    socket.send(text);
                })

            });
        </script>        
    </head>
    <body>
        <div id="messageFromServer">
        input the integer</div>
        <input id="msg" type="text"/>
        <button id="send">go</button>
    </body>
</html>

参考文献
WebSocketをネタにJava EE 7正式版を試してみる