annotation」タグアーカイブ

WebSocketを使ってみる。その4

続けて@PathParamを試してみる。
APIによると
ServerEndpointに{パラメータ}で指定した値とバインドしてくれるらしい。

変数として使える型はStringかプリミティブOrそのボクシング型とのこと。

ただし、パラメータがデコードできなかったらエラーハンドラーを呼び出すらしい。

Java (zip:paramBindTest)

import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/hello/{hoge}/{fuga}")
public class HelloWorld {

    static Set<Session> sessions = Collections
            .synchronizedSet(new HashSet<Session>());

    @OnMessage
    public void onMessage(String message,
           @PathParam("hoge") Integer hoge,@PathParam("fuga") String fuga) {
        System.out.println("message[" + message + "]");
        System.out.println("hoge[" + hoge + "]");
        System.out.println("fuga[" + fuga + "]");
        for (Session s : sessions) {
            s.getAsyncRemote().sendText(message);
        }
    }

JS

            $(document).ready(function(){
                var host="ws://localhost:8080/first/hello/12/ああ";
                socket = new WebSocket(host);

                socket.onmessage = function(message){
                    $('#log').append(message.data + "<br/>");
                }

結果

情報: 開始します:8ca15fe1-a79d-420e-afc6-3c5ab18a75ec
情報: message[あsdf]
情報: hoge[12] <- ボクシング型にバインドされている
情報: fuga[ああ] <- 日本語も素直にバインドされる模様

日本語もそのままうまくバインドされた。

ためしにIntegerの部分を少数にしたらNumberFormatExceptionをチェーンしたDecodeExceptionが発生。このケースは切り分け難しくない。

情報: エラーです:9366a9f2-2b24-4be3-8897-40427563d56e
重大: javax.websocket.DecodeException: Decoding failed
    at org.glassfish.tyrus.core.PrimitiveDecoders$IntegerDecoder.decode(PrimitiveDecoders.java:165)
    at org.glassfish.tyrus.core.PrimitiveDecoders$IntegerDecoder.decode(PrimitiveDecoders.java:157)
    at org.glassfish.tyrus.core.AnnotatedEndpoint$2.value(AnnotatedEndpoint.java:362)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:428)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.access$100(AnnotatedEndpoint.java:83)
    at org.glassfish.tyrus.core.AnnotatedEndpoint$WholeHandler$1.onMessage(AnnotatedEndpoint.java:518)
    at org.glassfish.tyrus.core.SessionImpl.notifyMessageHandlers(SessionImpl.java:389)
    at org.glassfish.tyrus.core.EndpointWrapper.onMessage(EndpointWrapper.java:495)
    at org.glassfish.tyrus.server.TyrusEndpoint.onMessage(TyrusEndpoint.java:174)
    at org.glassfish.tyrus.websockets.DefaultWebSocket.onMessage(DefaultWebSocket.java:156)
    at org.glassfish.tyrus.websockets.frametypes.TextFrameType.respond(TextFrameType.java:66)
    at org.glassfish.tyrus.websockets.DataFrame.respond(DataFrame.java:102)
    at org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler.onDataAvailable(TyrusHttpUpgradeHandler.java:113)
    at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.processDataAvailable(InputBuffer.java:488)
    at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.onDataAvailable(InputBuffer.java:453)
    at org.glassfish.grizzly.http.io.InputBuffer.append(InputBuffer.java:855)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:222)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.NumberFormatException: For input string: "1.2"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:492)
    at java.lang.Integer.valueOf(Integer.java:582)
    at org.glassfish.tyrus.core.PrimitiveDecoders$IntegerDecoder.decode(PrimitiveDecoders.java:163)
    ... 30 more

 

nullとした場合も素直にエラー

var host="ws://localhost:8080/first/hello/null/ああ";
->
Caused by: java.lang.NumberFormatException: For input string: "null"

空文字だと、予想外に素直なエラー

var host="ws://localhost:8080/first/hello//ああ";
->
Caused by: java.lang.NumberFormatException: For input string: ""

 

最後の文字列が空文字だと空文字としてバインドされる。

var host="ws://localhost:8080/first/hello/12/";
->
情報: 開始します:75b4bc74-30e9-4a69-8af3-07ed67e6a296
情報: message[あ]
情報: hoge[12]
情報: fuga[]

/{空文字}/{数字}の順でも動いた。(//となるので環境次第では不具合が出る??

@ServerEndpoint("/hello/{fuga}/{hoge}")と最初を文字列にする
->
var host="ws://localhost:8080/first/hello//12";
->
情報: 開始します:028c5db1-a60f-4d82-970f-8933058b2bfb
情報: message[あああ]
情報: hoge[12]
情報: fuga[]

{変数名}と@PathParamで誤記をするとバインドされない(null)。

@ServerEndpoint("/hello/{fuga}/{hoge}")
->
public void onMessage(String message,
  @PathParam("hoge") Integer hoge,
  @PathParam("fuga2") String fuga)
->
情報: 開始します:49d01de4-4a6b-4b81-8c71-d6d48d9f0600
情報: message[あ]
情報: hoge[12]
情報: fuga[null]

 

URLパラーンの違いによっては呼び出されない。

一つ前のnullがバインドされる事象はバインド対象が不整合でも動いていたけど、そっちが中途半端に動いているだけで、呼び出されないのが正しいのかな??

@ServerEndpoint("/hello/{hoge}") <-fuga2はない
->
public void onMessage(String message,
   @PathParam("hoge") Integer hoge,]
   @PathParam("fuga2") String fuga) {

->
23:27:25.244[23ms][total 23ms] Status: 404[Not Found]
GET http://localhost:8080/first/hello/12/nasi Load Flags[LOAD_BYPASS_CACHE  LOAD_BACKGROUND  INHIBIT_CACHING  ] Content Size[-1] Mime Type[application/x-unknown-content-type]
   Request Headers:
      Host[localhost:8080]
      User-Agent[Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0]
      Accept[text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
      Accept-Language[ja,en-us;q=0.7,en;q=0.3]
      Accept-Encoding[gzip, deflate]
      Sec-WebSocket-Version[13]
      Origin[http://localhost:8080]
      Sec-WebSocket-Key[gq8u2S2nXT2xpIagf7qPaQ==]
      Cookie[JSESSIONID=e1e94c1a637e58c7ccaa61c6f17e]
      Connection[keep-alive, Upgrade]
      Pragma[no-cache]
      Cache-Control[no-cache]
      Upgrade[websocket]
   Response Headers:
      X-Powered-By[Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)]
      Server[GlassFish Server Open Source Edition  4.0]
      Set-Cookie[JSESSIONID=e2121d0c30e627004af713703b3d; Path=/first; HttpOnly]

WebSocket(JSR 356)を使ってみる。その2

先日に続きOracleのサイトをみながらぼちぼち試していきます。

@OnMessageの戻り値

If the return type of the method annotated with @OnMessage is not void, the WebSocket implementation will send the return value to the other peer.

戻り値付のメソッドの場合、その値をpeerに返すというような事が書いてあるので試してみる。

ソース

@OnMessage
public String onMessage(String message) {
System.out.println(“message[” + message + “]”);
 return message;
}

実際にメッセージを送ったブラウザには戻り値が帰ってきたことを確認できた。

戻り値確認

ただし、開いていた他のブラウザには送信されていない。

別ブラウザへの反映状況

 

peerと書いているだけなので複数に返すのでなく単独のものに返すという動作という認識であっているのかな??

疑問は残るけど、複数ブラウザ(peer)に返すサンプルがあるgetAsyncRemotegetBasicRemote()を試してみる。APIによると非同期か同期かの違いらしい。

 @OnMessageのgetAsyncRemote/getBasicRemote

ソースを書き換えてみたけれども、getAsyncRemotegetBasicRemoteの両方とも同時にalert(“Calling”);まで進む。

RemoteEndpoint.Basicをみると下のように書かれているのでのでJSのAlert文で待ちが入るというわけではないらしい。

The point of completion of the send is defined when all the supplied data has been written to the underlying connection

送信失敗に備えたエラー処理をするためのもので、Endpoint間の同期をとる目的でないということでよいのかな。

(java)

static Set<Session> sessions = Collections
.synchronizedSet(new HashSet<Session>());

@OnMessage
public void onMessage(String message) {
System.out.println(“message[” + message + “]”);
for (Session s : sessions) {
s.getAsyncRemote().sendText(message);
}
}

@OnMessage
public void onMessage(String message) throws IOException {
System.out.println(“message[” + message + “]”);
for (Session s : sessions) {
s.getBasicRemote().sendText(message);
}
}

(JS)

socket.onmessage = function(message){
alert(“Calling”);
$(‘#log’).append(message.data + “<br/>”);
}