diff --git a/actions/pom.xml b/actions/pom.xml
index 73487d32..bf87e72c 100644
--- a/actions/pom.xml
+++ b/actions/pom.xml
@@ -23,11 +23,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
tools.dynamia.actions
- 5.4.9
+ 5.4.10
DynamiaTools - Actions
https://dynamia.tools/docs/actions
@@ -65,12 +65,12 @@
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
diff --git a/app/pom.xml b/app/pom.xml
index 3db4b8dd..dd4c6603 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -23,11 +23,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
tools.dynamia.app
- 5.4.9
+ 5.4.10
DynamiaTools - App
https://dynamia.tools/docs/app
@@ -74,58 +74,58 @@
tools.dynamia
tools.dynamia.actions
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.crud
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.domain
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.io
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.navigation
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.reports
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.templates
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.viewers
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.web
- 5.4.9
+ 5.4.10
org.springframework.data
@@ -208,7 +208,7 @@
tools.dynamia
tools.dynamia.domain.jpa
- 5.4.9
+ 5.4.10
test
diff --git a/commons/pom.xml b/commons/pom.xml
index 1e0fa8d9..b863c77c 100644
--- a/commons/pom.xml
+++ b/commons/pom.xml
@@ -22,11 +22,11 @@
tools.dynamia.commons
jar
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Commons
https://dynamia.tools/docs/common
diff --git a/crud/pom.xml b/crud/pom.xml
index 9943e0d6..f1e7a4d0 100644
--- a/crud/pom.xml
+++ b/crud/pom.xml
@@ -23,11 +23,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
tools.dynamia.crud
- 5.4.9
+ 5.4.10
DynamiaTools - CRUD
https://dynamia.tools/docs/crud
@@ -62,23 +62,23 @@
tools.dynamia
tools.dynamia.actions
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.viewers
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.navigation
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.domain.jpa
- 5.4.9
+ 5.4.10
test
diff --git a/domain-jpa/pom.xml b/domain-jpa/pom.xml
index d66217d8..c337b910 100644
--- a/domain-jpa/pom.xml
+++ b/domain-jpa/pom.xml
@@ -23,13 +23,13 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Domain JPA
https://dynamia.tools/docs/domain
tools.dynamia.domain.jpa
- 5.4.9
+ 5.4.10
jar
diff --git a/domain/pom.xml b/domain/pom.xml
index cad08e8e..830d5095 100644
--- a/domain/pom.xml
+++ b/domain/pom.xml
@@ -21,13 +21,13 @@
4.0.0
tools.dynamia.domain
- 5.4.9
+ 5.4.10
jar
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Domain
https://dynamia.tools/docs/domain
diff --git a/integration/pom.xml b/integration/pom.xml
index 2e8e453a..e66b8b85 100644
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -27,12 +27,12 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Integration
A set of classes and interfaces that help integrate modules
- 5.4.9
+ 5.4.10
https://dynamia.tools/docs/integration
diff --git a/integration/src/main/java/tools/dynamia/integration/scheduling/SchedulerUtil.java b/integration/src/main/java/tools/dynamia/integration/scheduling/SchedulerUtil.java
index 6c4223e5..2c5de048 100644
--- a/integration/src/main/java/tools/dynamia/integration/scheduling/SchedulerUtil.java
+++ b/integration/src/main/java/tools/dynamia/integration/scheduling/SchedulerUtil.java
@@ -307,14 +307,14 @@ public static boolean isSchedulingEnabled() {
}
/**
- * Schedule a task with fixed delay using Spring TaskScheduler
+ * Schedule a task at fixed rate using Spring TaskScheduler for repeated execution.
*
- * @param delay the delay
- * @param task the task
+ * @param rate the rate
+ * @param task the task to repeat each period
* @return the scheduled future
*/
- public static ScheduledFuture> scheduleWithFixedDelay(Duration delay, Runnable task) {
- return getTaskScheduler().scheduleAtFixedRate(task, delay);
+ public static ScheduledFuture> scheduleAtFixedRate(Duration rate, Runnable task) {
+ return getTaskScheduler().scheduleAtFixedRate(task, rate);
}
/**
diff --git a/io/pom.xml b/io/pom.xml
index fe48fb56..ac80a13f 100644
--- a/io/pom.xml
+++ b/io/pom.xml
@@ -28,11 +28,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - IO
- 5.4.9
+ 5.4.10
A set of classes and interfaces that help in any kind io task
https://dynamia.tools/docs/io
diff --git a/navigation/pom.xml b/navigation/pom.xml
index 3bf25d84..96110c2f 100644
--- a/navigation/pom.xml
+++ b/navigation/pom.xml
@@ -23,11 +23,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
tools.dynamia.navigation
- 5.4.9
+ 5.4.10
DynamiaTools - Navigation
https://dynamia.tools/docs/navigation
@@ -63,17 +63,17 @@
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.actions
- 5.4.9
+ 5.4.10
diff --git a/pom.xml b/pom.xml
index eef41758..4b393f73 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
4.0.0
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
pom
Dynamia Soluciones IT SAS
diff --git a/reports/pom.xml b/reports/pom.xml
index cb33e98c..b6862dd7 100644
--- a/reports/pom.xml
+++ b/reports/pom.xml
@@ -26,11 +26,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Reports
- 5.4.9
+ 5.4.10
A set of classes and interfaces that help building Reports
https://dynamia.tools/docs/reports
diff --git a/starter/pom.xml b/starter/pom.xml
index 50e9251d..b1809e6a 100644
--- a/starter/pom.xml
+++ b/starter/pom.xml
@@ -4,7 +4,7 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
dynamia-tools-starter
DynamiaTools - Starter
@@ -26,17 +26,17 @@
tools.dynamia
tools.dynamia.app
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.zk
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.domain.jpa
- 5.4.9
+ 5.4.10
org.hibernate.validator
diff --git a/templates/pom.xml b/templates/pom.xml
index 91aaa5b2..19caddc3 100644
--- a/templates/pom.xml
+++ b/templates/pom.xml
@@ -23,12 +23,12 @@
tools.dynamia.parent
tools.dynamia
- 5.4.9
+ 5.4.10
tools.dynamia.templates
- 5.4.9
+ 5.4.10
DynamiaTools - Templates
https://dynamia.tools/docs/templates
@@ -64,12 +64,12 @@
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
diff --git a/ui/pom.xml b/ui/pom.xml
index 07b683ac..90ccb70e 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -23,11 +23,11 @@
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
tools.dynamia.ui
- 5.4.9
+ 5.4.10
DynamiaTools - UI
https://dynamia.tools/docs/ui
Helper classes for module integrations and messages
@@ -64,17 +64,17 @@
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.io
- 5.4.9
+ 5.4.10
diff --git a/viewers/pom.xml b/viewers/pom.xml
index 2c0b164a..898b0228 100644
--- a/viewers/pom.xml
+++ b/viewers/pom.xml
@@ -23,13 +23,13 @@
tools.dynamia.viewers
jar
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Viewers
@@ -67,27 +67,27 @@
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.io
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.domain
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.actions
- 5.4.9
+ 5.4.10
org.yaml
diff --git a/web/pom.xml b/web/pom.xml
index be5e838e..f298ba66 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -23,14 +23,14 @@
tools.dynamia.web
jar
- 5.4.9
+ 5.4.10
A set of common classes and interfaces for web application development
tools.dynamia
tools.dynamia.parent
- 5.4.9
+ 5.4.10
DynamiaTools - Web
@@ -88,27 +88,27 @@
tools.dynamia
tools.dynamia.commons
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.integration
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.navigation
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.viewers
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.crud
- 5.4.9
+ 5.4.10
org.springframework
diff --git a/zk/pom.xml b/zk/pom.xml
index 314a1825..3fb59f59 100644
--- a/zk/pom.xml
+++ b/zk/pom.xml
@@ -21,12 +21,12 @@
tools.dynamia.parent
tools.dynamia
- 5.4.9
+ 5.4.10
4.0.0
tools.dynamia.zk
- 5.4.9
+ 5.4.10
jar
DynamiaTools - ZK
https://dynamia.tools/docs/zk
@@ -99,31 +99,31 @@
tools.dynamia
tools.dynamia.web
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.navigation
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.ui
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.domain
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.viewers
- 5.4.9
+ 5.4.10
org.yaml
@@ -134,19 +134,19 @@
tools.dynamia
tools.dynamia.crud
- 5.4.9
+ 5.4.10
tools.dynamia
tools.dynamia.reports
- 5.4.9
+ 5.4.10
compile
tools.dynamia
tools.dynamia.templates
- 5.4.9
+ 5.4.10
compile
diff --git a/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketGlobalCommandHandler.java b/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketGlobalCommandHandler.java
index 9146f8ab..3aa34f52 100644
--- a/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketGlobalCommandHandler.java
+++ b/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketGlobalCommandHandler.java
@@ -24,7 +24,7 @@
import org.zkoss.zk.ui.Desktop;
import tools.dynamia.commons.logger.Loggable;
-import java.util.Collection;
+import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -62,8 +62,8 @@
*/
public class WebSocketGlobalCommandHandler extends TextWebSocketHandler implements Loggable {
- private final Map desktops = new ConcurrentHashMap<>();
- private final Map sessions = new ConcurrentHashMap<>();
+ // Map of active WebSocket sessions by session ID
+ private final Map sessions = new ConcurrentHashMap<>();
/**
* Handles incoming text messages from WebSocket clients.
@@ -91,17 +91,18 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
return;
}
+ if ("PONG".equals(payload)) {
+ // Ignore PONG messages
+ return;
+ }
+
// Handle desktop ID registration
- String desktopId = payload;
- String oldSessionId = desktops.get(desktopId);
- if (oldSessionId != null) {
- WebSocketSession oldSession = sessions.get(oldSessionId);
- if (oldSession != null) {
- oldSession.close(CloseStatus.NORMAL);
- }
+ var oldSession = sessions.get(payload);
+ if (oldSession != null) {
+ oldSession.close(CloseStatus.NORMAL);
}
- log("Associating desktop " + desktopId + " with ws session " + session.getId());
- desktops.put(desktopId, session.getId());
+ log("Associating desktop " + payload + " with ws session " + session.getId());
+ sessions.put(session.getId(), new DeskstopWebSocketSession(payload, session));
}
/**
@@ -114,8 +115,7 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) {
- log("WebSocket connection established: " + session.getId());
- sessions.put(session.getId(), session);
+ log("New webSocket connection established: " + session.getId() + " waiting for desktop ID...");
}
/**
@@ -125,16 +125,11 @@ public void afterConnectionEstablished(WebSocketSession session) {
* the internal maps. This ensures that closed connections don't accumulate in memory.
*
* @param session the closed WebSocket session
- * @param status the status code indicating why the connection was closed
+ * @param status the status code indicating why the connection was closed
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
log("WebSocket connection closed: " + session.getId() + " with status " + status);
-
- String desktopId = desktops.entrySet().stream().filter(e -> e.getValue().equals(session.getId())).map(Map.Entry::getKey).findFirst().orElse(null);
- if (desktopId != null) {
- desktops.remove(desktopId);
- }
sessions.remove(session.getId());
}
@@ -147,15 +142,11 @@ public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
* @param desktop the ZK Desktop to find the session for
* @return the associated WebSocket session, or {@code null} if not found or desktop is null
*/
- WebSocketSession findSession(Desktop desktop) {
- WebSocketSession session = null;
- if (desktop != null && desktop.getId() != null) {
- String sessionID = desktops.get(desktop.getId());
- if (sessionID != null) {
- session = sessions.get(sessionID);
- }
- }
- return session;
+ public DeskstopWebSocketSession findSession(Desktop desktop) {
+ return sessions.values().stream()
+ .filter(ds -> ds.matchesDesktop(desktop))
+ .findFirst()
+ .orElse(null);
}
/**
@@ -166,7 +157,61 @@ WebSocketSession findSession(Desktop desktop) {
*
* @return a collection of all active WebSocket sessions
*/
- Collection getAllSessions() {
- return sessions.values();
+ public Map getSessions() {
+ return sessions;
+ }
+
+ /**
+ * Closes the WebSocket session associated with a specific ZK Desktop.
+ *
+ * This method safely closes the session and removes it from the internal
+ * session map to prevent resource leaks.
+ *
+ * @param desktop the ZK Desktop whose session should be closed
+ */
+ public void closeSession(Desktop desktop) {
+ var session = findSession(desktop);
+ if (session != null) {
+ try {
+ session.close(CloseStatus.NORMAL);
+ } finally {
+ sessions.remove(session.session.getId());
+ }
+ } else {
+ log("No websocket session found for desktop " + desktop.getId());
+ }
+ }
+
+
+ /**
+ * Represents a WebSocket session associated with a specific ZK Desktop.
+ *
+ * This record encapsulates the desktop ID and the WebSocket session,
+ * providing utility methods for session management and message sending.
+ */
+ public record DeskstopWebSocketSession(String desktopId, WebSocketSession session) {
+ boolean isOpen() {
+ return session.isOpen();
+ }
+
+ boolean matchesDesktop(Desktop desktop) {
+ return desktop != null && desktopId.equals(desktop.getId());
+ }
+
+ boolean matchesSession(WebSocketSession otherSession) {
+ return session.getId().equals(otherSession.getId());
+ }
+
+ void close(CloseStatus closeStatus) {
+ try {
+ session.close(closeStatus);
+ } catch (Exception e) {
+ //ignore
+ }
+ }
+
+ void sendMessage(String message) throws IOException {
+ session.sendMessage(new TextMessage(message));
+ }
}
}
diff --git a/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketPushSender.java b/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketPushSender.java
index 93080fe6..f1c5df5a 100644
--- a/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketPushSender.java
+++ b/zk/src/main/java/tools/dynamia/zk/websocket/WebSocketPushSender.java
@@ -17,6 +17,7 @@
package tools.dynamia.zk.websocket;
+import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.zkoss.zk.ui.Desktop;
@@ -31,6 +32,7 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Helper class to send push command to desktop client using web socket
@@ -66,21 +68,23 @@ public static boolean sendPushCommand(Desktop desktop, String command, Map {
+ AtomicInteger count = new AtomicInteger();
+ handler.getSessions().values().forEach(s -> {
try {
- s.sendMessage(new TextMessage(command));
+ s.sendMessage(command);
+ count.incrementAndGet();
} catch (IOException e) {
- LOGGER.error("Error sending command " + command + " to WS Session: " + s);
+ LOGGER.error("IO Error sending command " + command + " to WS Session: " + s, e);
+ s.close(CloseStatus.NORMAL);
+ } catch (Exception e) {
+ LOGGER.error("Error sending command " + command + " to WS Session: " + s, e);
}
});
+ LOGGER.info("Broadcasted command '" + command + "' to " + count.get() + " WS sessions.");
}
}
- private static WebSocketGlobalCommandHandler getHandler() {
+ /**
+ * Broadcast a heartbeat PING command to all connected sessions
+ */
+ public static void broadcastHeartbeat() {
+ broadcastCommand("PING");
+ }
+
+
+ public static WebSocketGlobalCommandHandler getHandler() {
return Containers.get().findObject(WebSocketGlobalCommandHandler.class);
}
diff --git a/zk/src/main/resources/static/dynamia-tools/js/dynamia-tools-ws.js b/zk/src/main/resources/static/dynamia-tools/js/dynamia-tools-ws.js
index 8838a09f..ebde1ba4 100644
--- a/zk/src/main/resources/static/dynamia-tools/js/dynamia-tools-ws.js
+++ b/zk/src/main/resources/static/dynamia-tools/js/dynamia-tools-ws.js
@@ -118,7 +118,7 @@
// Keep-alive configuration
var keepAliveConfig = {
enabled: true,
- interval: 30000 // Send ping every 30 seconds
+ interval: 60000 // Send ping every 60 seconds
};
var wsManager = {
@@ -301,6 +301,17 @@
return;
}
+ if (e.data === 'PING') {
+ // Respond to server ping
+ try {
+ wsManager.socket.send('PONG');
+ console.debug('DynamiaTools WebSocket: pong sent in response to server ping');
+ } catch (error) {
+ console.warn('DynamiaTools WebSocket: error sending pong', error);
+ }
+ return;
+ }
+
console.debug('DynamiaTools command received:', e.data);
try {