package org.sef4j.springmsg.websocket; import java.util.List; import java.util.Map; import org.sef4j.core.api.EventSender; import org.sef4j.core.api.def.ioevenchain.InputEventChainDef; import org.sef4j.core.api.session.ClientSessionInputEventChainSubscriptions; import org.sef4j.core.api.session.InOutEventsClientSession; import org.sef4j.core.api.session.InOutEventsClientSessionManager; import org.sef4j.core.api.session.SubscriptionCommandDTO; import org.sef4j.core.api.session.SubscriptionResponseDTO; import org.sef4j.core.util.CopyOnWriteUtils; import org.sef4j.core.util.Handle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.AbstractWebSocketHandler; import com.fasterxml.jackson.core.JsonFactory; import com.google.common.collect.ImmutableMap; /** * */ public class ClientSessionTransportWebSocketHandler extends AbstractWebSocketHandler { private static final Logger LOG = LoggerFactory.getLogger(ClientSessionTransportWebSocketHandler.class); public static final String ATTR_associatedClientSessionId = "associatedClientSessionId"; protected InOutEventsClientSessionManager clientSessionManager; private ImmutableMap<String,WebSocketEntry> webSocketEntries = ImmutableMap.of(); private Object lock = new Object(); private JsonFactory jsonFactory = new JsonFactory(); // ------------------------------------------------------------------------ public ClientSessionTransportWebSocketHandler(InOutEventsClientSessionManager clientSessionManager) { this.clientSessionManager = clientSessionManager; } // ------------------------------------------------------------------------ /** called from WebSocketHandler */ public void onWebSocketSessionCreated(WebSocketSession wsSession) { String wsId = wsSession.getId(); String associatedClientSessionId = (String) wsSession.getAttributes().get(ATTR_associatedClientSessionId); LOG.info("onWebSocketSessionCreated " + wsId + ((associatedClientSessionId != null? " associatedClientSessionId:" + associatedClientSessionId : ""))); InOutEventsClientSession clientSession = (associatedClientSessionId != null)? getClientSessionOrThrow(associatedClientSessionId) : null; JsonWSMessageEventSender jsonMessageSender = new JsonWSMessageEventSender(wsSession, jsonFactory); JSonMessageToEventSender<Object> jsonMessageReceiver = new JSonMessageToEventSender<Object>(null, jsonFactory, Object.class // TODO ???!!! class for json->object converter ); WebSocketEntry entry = new WebSocketEntry(wsSession, clientSession, jsonMessageSender, jsonMessageReceiver); synchronized (lock) { webSocketEntries = CopyOnWriteUtils.newWithPut(webSocketEntries, wsId, entry); if (clientSession != null) { doAttachWebSocketToClientSession(entry, clientSession); } } } /** called from WebSocketHandler */ public void onWebSocketSessionDeleted(WebSocketSession wsSession) { String wsId = wsSession.getId(); LOG.info("onWebSocketSessionDeleted " + wsId); WebSocketEntry entry = webSocketEntries.get(wsId); if (entry == null) { LOG.warn("webSocket entry '" + wsId + "' not found ... ignore!"); return; } // TODO if (entry.clientSession != null) { doDetachWebSocketFromClientSession(entry, entry.clientSession); } synchronized (lock) { webSocketEntries = CopyOnWriteUtils.newWithRemove(webSocketEntries, wsId); } } public void attachWebSocketToClientSession(WebSocketSession wsSession, String clientSessionId) { String wsId = wsSession.getId(); LOG.info("attachWebSocketToClientSession " + wsId + " " + clientSessionId); WebSocketEntry entry = webSocketEntries.get(wsId); InOutEventsClientSession clientSession = getClientSessionOrThrow(clientSessionId); doAttachWebSocketToClientSession(entry, clientSession); } public void detachWebSocketFromClientSession(WebSocketSession wsSession, String clientSessionId) { String wsId = wsSession.getId(); LOG.info("detachWebSocketToClientSession " + wsId + " " + clientSessionId); WebSocketEntry entry = webSocketEntries.get(wsId); InOutEventsClientSession clientSession = getClientSessionOrThrow(clientSessionId); doDetachWebSocketFromClientSession(entry, clientSession); } public void redispatchReceiveMessageToClientSession(WebSocketSession wsSession, TextMessage message) { String wsId = wsSession.getId(); WebSocketEntry entry = webSocketEntries.get(wsId); if (entry == null) { LOG.warn("webSocket entry '" + wsId + "' not found ... ignore message!"); return; } entry.messageReceiver.sendEvent(message); } public List<SubscriptionResponseDTO> handleSubscriptionCommands(WebSocketSession wsSession, List<SubscriptionCommandDTO> commands) { WebSocketEntry entry = sageGetWsSession(wsSession); ClientSessionInputEventChainSubscriptions inputEventChainSubscriptions = entry.clientSession.getInputEventChainSubscriptions(); List<SubscriptionResponseDTO> res = inputEventChainSubscriptions.handleSubscriptionCommands(commands); return res; } public SubscriptionResponseDTO handleSubscriptionCommand(WebSocketSession wsSession, SubscriptionCommandDTO command) { WebSocketEntry entry = sageGetWsSession(wsSession); ClientSessionInputEventChainSubscriptions inputEventChainSubscriptions = entry.clientSession.getInputEventChainSubscriptions(); SubscriptionResponseDTO res = inputEventChainSubscriptions.handleSubscriptionCommand(command); return res; } public Handle addSubscription(WebSocketSession wsSession, InputEventChainDef def, Object key, String displayName, Map<String,Object> options) { WebSocketEntry entry = sageGetWsSession(wsSession); return entry.clientSession.getInputEventChainSubscriptions().addSubscription(def, key, displayName, options); } public void removeSubscription(WebSocketSession wsSession, Handle handle) { WebSocketEntry entry = sageGetWsSession(wsSession); entry.clientSession.getInputEventChainSubscriptions().removeSubscription(handle); } // internal // ------------------------------------------------------------------------ private WebSocketEntry sageGetWsSession(WebSocketSession wsSession) { String wsId = wsSession.getId(); WebSocketEntry entry = webSocketEntries.get(wsId); if (entry == null) { LOG.warn("webSocket entry '" + wsId + "' not found ... ignore message!"); throw new IllegalStateException("webSocket entry not found for wsId: '" + wsId + "'"); } return entry; } private void doAttachWebSocketToClientSession(WebSocketEntry entry, InOutEventsClientSession clientSession) { if (entry.clientSession == clientSession) { return; // do nothing (should not occur however) } if (entry.clientSession != null) { throw new IllegalStateException("WebSocket already associated with another clientSession"); } entry.clientSession = clientSession; // attach event receiver: clientSession <-- WebSocket EventSender<Object> clientSessionMessageReceiver = clientSession.getSessionTransportService().getSessionTransportsEventReceiver(); entry.messageReceiver.setTarget(clientSessionMessageReceiver); // attach event sender: clientSession --> WebSocket clientSession.getSessionTransportService().addSessionTransportsEventListener(entry.webSocketEventSender); } private void doDetachWebSocketFromClientSession(WebSocketEntry entry, InOutEventsClientSession clientSession) { if (entry.clientSession == null) { return; // do nothing (should not occur however) } entry.clientSession = null; // deattach event receiver: clientSession <-- WebSocket entry.messageReceiver.setTarget(null); // detach event sender: clientSession --> WebSocket clientSession.getSessionTransportService().removeSessionTransportsEventListener(entry.webSocketEventSender); } private InOutEventsClientSession getClientSessionOrThrow(String associatedClientSessionId) { InOutEventsClientSession clientSession; clientSession = clientSessionManager.getClientSessionOrNull(associatedClientSessionId); if (clientSession == null) { throw new IllegalArgumentException("clientSessionId '" + associatedClientSessionId + "' not found"); } return clientSession; } // ------------------------------------------------------------------------ private static class WebSocketEntry { WebSocketSession wsSession; InOutEventsClientSession clientSession; EventSender<Object> webSocketEventSender; JSonMessageToEventSender<Object> messageReceiver; public WebSocketEntry(WebSocketSession wsSession, InOutEventsClientSession clientSession, EventSender<Object> webSocketEventSender, JSonMessageToEventSender<Object> messageReceiver) { this.wsSession = wsSession; this.clientSession = clientSession; this.webSocketEventSender = webSocketEventSender; this.messageReceiver = messageReceiver; } @Override public String toString() { return "WebSocketEntry [wsSession=" + ((wsSession != null)? wsSession.getId() : "") + ", clientSession=" + ((clientSession != null)? clientSession.getId() : "null") + "]"; } } }