2424import org .zkoss .zk .ui .Desktop ;
2525import tools .dynamia .commons .logger .Loggable ;
2626
27- import java .util . Collection ;
27+ import java .io . IOException ;
2828import java .util .Map ;
2929import java .util .concurrent .ConcurrentHashMap ;
3030
6262 */
6363public class WebSocketGlobalCommandHandler extends TextWebSocketHandler implements Loggable {
6464
65- private final Map < String , String > desktops = new ConcurrentHashMap <>();
66- private final Map <String , WebSocketSession > sessions = new ConcurrentHashMap <>();
65+ // Map of active WebSocket sessions by session ID
66+ private final Map <String , DeskstopWebSocketSession > sessions = new ConcurrentHashMap <>();
6767
6868 /**
6969 * Handles incoming text messages from WebSocket clients.
@@ -91,17 +91,18 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
9191 return ;
9292 }
9393
94+ if ("PONG" .equals (payload )) {
95+ // Ignore PONG messages
96+ return ;
97+ }
98+
9499 // Handle desktop ID registration
95- String desktopId = payload ;
96- String oldSessionId = desktops .get (desktopId );
97- if (oldSessionId != null ) {
98- WebSocketSession oldSession = sessions .get (oldSessionId );
99- if (oldSession != null ) {
100- oldSession .close (CloseStatus .NORMAL );
101- }
100+ var oldSession = sessions .get (payload );
101+ if (oldSession != null ) {
102+ oldSession .close (CloseStatus .NORMAL );
102103 }
103- log ("Associating desktop " + desktopId + " with ws session " + session .getId ());
104- desktops .put (desktopId , session .getId ());
104+ log ("Associating desktop " + payload + " with ws session " + session .getId ());
105+ sessions .put (session .getId (), new DeskstopWebSocketSession ( payload , session ));
105106 }
106107
107108 /**
@@ -114,8 +115,7 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
114115 */
115116 @ Override
116117 public void afterConnectionEstablished (WebSocketSession session ) {
117- log ("WebSocket connection established: " + session .getId ());
118- sessions .put (session .getId (), session );
118+ log ("New webSocket connection established: " + session .getId () + " waiting for desktop ID..." );
119119 }
120120
121121 /**
@@ -125,16 +125,11 @@ public void afterConnectionEstablished(WebSocketSession session) {
125125 * the internal maps. This ensures that closed connections don't accumulate in memory.</p>
126126 *
127127 * @param session the closed WebSocket session
128- * @param status the status code indicating why the connection was closed
128+ * @param status the status code indicating why the connection was closed
129129 */
130130 @ Override
131131 public void afterConnectionClosed (WebSocketSession session , CloseStatus status ) {
132132 log ("WebSocket connection closed: " + session .getId () + " with status " + status );
133-
134- String desktopId = desktops .entrySet ().stream ().filter (e -> e .getValue ().equals (session .getId ())).map (Map .Entry ::getKey ).findFirst ().orElse (null );
135- if (desktopId != null ) {
136- desktops .remove (desktopId );
137- }
138133 sessions .remove (session .getId ());
139134 }
140135
@@ -147,15 +142,11 @@ public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
147142 * @param desktop the ZK Desktop to find the session for
148143 * @return the associated WebSocket session, or {@code null} if not found or desktop is null
149144 */
150- WebSocketSession findSession (Desktop desktop ) {
151- WebSocketSession session = null ;
152- if (desktop != null && desktop .getId () != null ) {
153- String sessionID = desktops .get (desktop .getId ());
154- if (sessionID != null ) {
155- session = sessions .get (sessionID );
156- }
157- }
158- return session ;
145+ public DeskstopWebSocketSession findSession (Desktop desktop ) {
146+ return sessions .values ().stream ()
147+ .filter (ds -> ds .matchesDesktop (desktop ))
148+ .findFirst ()
149+ .orElse (null );
159150 }
160151
161152 /**
@@ -166,7 +157,61 @@ WebSocketSession findSession(Desktop desktop) {
166157 *
167158 * @return a collection of all active WebSocket sessions
168159 */
169- Collection <WebSocketSession > getAllSessions () {
170- return sessions .values ();
160+ public Map <String , DeskstopWebSocketSession > getSessions () {
161+ return sessions ;
162+ }
163+
164+ /**
165+ * Closes the WebSocket session associated with a specific ZK Desktop.
166+ *
167+ * <p>This method safely closes the session and removes it from the internal
168+ * session map to prevent resource leaks.</p>
169+ *
170+ * @param desktop the ZK Desktop whose session should be closed
171+ */
172+ public void closeSession (Desktop desktop ) {
173+ var session = findSession (desktop );
174+ if (session != null ) {
175+ try {
176+ session .close (CloseStatus .NORMAL );
177+ } finally {
178+ sessions .remove (session .session .getId ());
179+ }
180+ } else {
181+ log ("No websocket session found for desktop " + desktop .getId ());
182+ }
183+ }
184+
185+
186+ /**
187+ * Represents a WebSocket session associated with a specific ZK Desktop.
188+ *
189+ * <p>This record encapsulates the desktop ID and the WebSocket session,
190+ * providing utility methods for session management and message sending.</p>
191+ */
192+ public record DeskstopWebSocketSession (String desktopId , WebSocketSession session ) {
193+ boolean isOpen () {
194+ return session .isOpen ();
195+ }
196+
197+ boolean matchesDesktop (Desktop desktop ) {
198+ return desktop != null && desktopId .equals (desktop .getId ());
199+ }
200+
201+ boolean matchesSession (WebSocketSession otherSession ) {
202+ return session .getId ().equals (otherSession .getId ());
203+ }
204+
205+ void close (CloseStatus closeStatus ) {
206+ try {
207+ session .close (closeStatus );
208+ } catch (Exception e ) {
209+ //ignore
210+ }
211+ }
212+
213+ void sendMessage (String message ) throws IOException {
214+ session .sendMessage (new TextMessage (message ));
215+ }
171216 }
172217}
0 commit comments