これまでに引き続きWebSocketを試してみる。
APIをみるとgetOpenSessions()というメソッドがあり、そのセッションに紐付くセッションが取れるとのこと。また、OnMessageアノテーションの説明によるとPOJOのメソッド引数にSessionをオプショナルな引数として渡せるとのこと。
下のように引数にセッションを追加してやって、そのセッションからgetOpenSessionsを呼んでやれば対向のエンドポイントにメッセージを送れた。
package hp.co.epea.first; import java.util.Set; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/hello") public class HelloWorld { /* * sessionOfEndPointとsessionOfClientEndPointという名前になっているけど * 勘違いしていたことによるよくない名前。 * 引数のSessionはリクエストを出したPeerと受け取ったPeer間のSessionなので * sessionOfCurrentReqみたいなもの。 * getOpenSessionsで取得できるのはそのsessionが持つendPointに紐付くやつなので * sessionOfRelatedみたいなものが本来 */ @OnMessage public void onMessage(String message,Session sessionOfSvEndPoint) { Set<Session> sessionsOfClientEndPoint = sessionOfSvEndPoint.getOpenSessions(); System.out.println("sessionsOfClientEndPoint.size():" +sessionsOfClientEndPoint.size()); System.out.println("message[" + message + "]"); for (Session s : sessionsOfClientEndPoint) { s.getAsyncRemote().sendText(message); } } @OnOpen public void open(Session sess) { System.out.println("開始します:" + sess.getId()); } @OnClose public void close(Session sess) { System.out.println("終了します:" + sess.getId()); } @OnError public void error(Session sess, Throwable t) { System.out.println("エラーです:" + sess.getId()); t.printStackTrace(); } }
※他の方が書いている記事を発見
対向のエンドポイントが閉じられた場合(こちらのOnCloseが終わったら?)そのエンドポイントはないものと扱われている。
(3つ開いている) 情報: 開始します:07d672db-af64-435c-a272-2698528927f1 情報: 開始します:91202881-63ac-45b8-a3a1-d53e7bdc2008 情報: 開始します:628fc0c0-9cde-4812-a12b-d2ce52cc146e 情報: sessionsOfClientEndPoint.size():3 <-開いている数 情報: message[あああ] (1つ閉じて2個にする) 情報: 終了します:628fc0c0-9cde-4812-a12b-d2ce52cc146e 情報: sessionsOfClientEndPoint.size():2 <-開いている数 情報: message[いいい]
ただこのやり方だとsessionOfSvEndPoint.getOpenSessions()してからfor文が回っている間にコネクションが閉じられるとコネクションクローズ済みエラーが飛んでしまう。前の書き方のほうがいいのかな。
情報: 開始します:68d4f179-7c99-4118-b829-f48f7fc9d670 情報: 開始します:83ccdfea-f0b8-4dc5-8cc3-3d9bddebc658 情報: sessionsOfClientEndPoint.size():2 <-セッションのコピーをとる 情報: message[a] 情報: 終了します:83ccdfea-f0b8-4dc5-8cc3-3d9bddebc658 <-for文の前に(or途中で)閉じる 情報: エラーです:68d4f179-7c99-4118-b829-f48f7fc9d670 <- OnErrorの引数で入ってくるのは実際に例外の原因となったものでなくてOnmessageを呼んだSessionの模様。 重大: java.lang.IllegalStateException: The connection has been closed. at org.glassfish.tyrus.core.SessionImpl.checkConnectionState(SessionImpl.java:343) at org.glassfish.tyrus.core.SessionImpl.getAsyncRemote(SessionImpl.java:180) at hp.co.epea.first.HelloWorld.onMessage(HelloWorld.java:22) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:431) 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)