1年以上前、broadcast ALL での WebSocketBehavior の方法は確立していた。
Tomcat 8.x + Wicket8 で WebSocket native - Oboe吹きプログラマの黙示録
見直してみれば、、
org.apache.wicket.protocol.ws.api.WebSocketPushBroadcaster
の broadcastAll(Application application, IWebSocketPushMessage message) を使ってたわけだ。
→ 接続してる全てに送る方法だった。
接続しているもの中で任意に特定して送信するのは、
broadcast(ConnectedMessage connection, IWebSocketPushMessage message)
を使う。
org.apache.wicket.protocol.ws.api.message.ConnectedMessage
このコンストラクタは、ConnectedMessage(Application application, String sessionId, IKey key)
よくよく注意すれば、この IKey :org.apache.wicket.protocol.ws.api.registry.IKey は、
受信するページを接続した時のイベントメソッド onConnect から ConnectedMessage が渡されるので、
ConnectedMessage の getKey() メソッドで取得できる!
受信するページのセッションIDとこの IKey が管理できてればいいわけだ。
そこで安易で原始的な方法だが、、、以下シングルトンでの管理クラスを用意
接続したページを開いた時刻も認識できるようにしたいので、LocalDateTime値も保存
import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; import org.apache.wicket.protocol.ws.api.registry.IKey; public final class WebsocketManager{ private static WebsocketManager inst; private Map<String, LocalDateTime> connectedMap; private Map<String, IKey> keyMap; private WebsocketManager(){ connectedMap = new HashMap<>(); keyMap = new HashMap<>(); } public static synchronized WebsocketManager getInstance(){ if (inst==null) inst = new WebsocketManager(); return inst; } /** * 受信ページ接続 * @param message ConnectedMessage */ public void ready(ConnectedMessage message){ connectedMap.put(message.getSessionId(), LocalDateTime.now()); keyMap.put(message.getSessionId(), message.getKey()); } /** * sessionId → IKey参照. * @param sessionId * @return Optional<IKey> */ public Optional<IKey> getIKeyOption(String sessionId){ return Optional.ofNullable(keyMap.get(sessionId)); } /** * sessionId → IKey参照. * @param sessionId * @return Optional<IKey> */ public IKey getIKey(String sessionId){ return keyMap.get(sessionId); } public void remove(ClosedMessage message){ connectedMap.remove(message.getSessionId()); keyMap.remove(message.getSessionId()); } public void destroy(){ connectedMap.clear(); keyMap.clear(); } public int count(){ return connectedMap.size(); } /** * セッションID Stream 取得 * @return Stream<String> */ public Stream<String> idstream(){ return keyMap.keySet().stream(); } public List<String> idlist(){ return keyMap.keySet().stream().collect(Collectors.toList()); } }
extends WebApplication にて、、
@Override protected void init(){ WebsocketManager.getInstance(); // TODO } @Override protected void onDestroy(){ super.onDestroy(); WebsocketManager.getInstance().destroy(); }
受信するPageのコンストラクタで、、WebSocketBehavior を追加
( onMessage は動かなかった。→ なぜか不明)
add(new WebSocketBehavior(){ @Override protected void onConnect(ConnectedMessage message){ super.onConnect(message); WebsocketManager.getInstance().ready(message); } @Override protected void onClose(ClosedMessage message){ WebsocketManager.getInstance().remove(message); } @Override public void onException(Component component, RuntimeException exception){ } });
受信するPage として、onEvent をオーバーライド
@Override public void onEvent(IEvent<?> event){ if (event.getPayload() instanceof WebSocketPushPayload){ WebSocketPushPayload wsEvent = (WebSocketPushPayload)event.getPayload(); WebSocketRequestHandler handler = wsEvent.getHandler(); // FeedMessage という任意の受信データを受け取る FeedMessage feed = (FeedMessage)wsEvent.getMessage(); String message = Optional.ofNullable(feed.getMessage()).orElse(""); // TODO // ページに書いたコンテナ更新 handler.add(messageLabel); handler.appendJavaScript("alert('" + message + "');"); } }
送受信メッセージ を定義するクラス FeedMessage
import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; public class FeedMessage implements IWebSocketPushMessage{ public String message; public String getMessage(){ return message; } }
接続している受信Page 全てに送る場合、、、broadcastAll 実行
FeedMessage message = new FeedMessage(); message.message = text; WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(getApplication()); WebSocketPushBroadcaster broadcaster = new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry()); broadcaster.broadcastAll(getApplication(), message);
特定の受信Page だけに送る場合、、、broadcast 実行
String sessionId = // 予め管理している接続セッションID管理 WebsocketManager から特定する WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(getApplication()); WebSocketPushBroadcaster broadcaster = new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry()); broadcaster.broadcast(new ConnectedMessage(getApplication(), sessionId, WebsocketManager.getInstance().getIKey(sessionId)), message);
尚、これを動くようにした時、
org.apache.wicket.protocol.ws.javax.JavaxWebSocketFilter による WebFilter を web.xml には、
定義しないで動いたし、WebSocket EndPoint を定義しているわけでもない。
だから厳密には、WebSocket の通信とは言えないかもしれけど、
任意のPUSHを実行したいという要望には、充分、応えられる機能だ。