From 21b5826488c2c9593305ca46d72c64505943fb45 Mon Sep 17 00:00:00 2001
From: georgweiss
Date: Thu, 22 Jan 2026 15:06:37 +0100
Subject: [PATCH 1/4] Align save&restore web socket implementation with Olog -
server side
---
.../application/Application.java | 4 +-
.../web/config/WebConfiguration.java | 16 +-
.../CompositeSnapshotController.java | 28 ++-
.../controllers/ConfigurationController.java | 26 +-
.../web/controllers/FilterController.java | 8 +-
.../web/controllers/NodeController.java | 11 +-
.../web/controllers/SnapshotController.java | 33 ++-
.../web/controllers/StructureController.java | 17 +-
.../web/controllers/TagController.java | 9 +-
.../saveandrestore/websocket/WebSocket.java | 212 ----------------
.../websocket/WebSocketConfig.java | 35 ++-
.../websocket/WebSocketController.java | 39 +++
.../websocket/WebSocketHandler.java | 227 ------------------
.../websocket/WebSocketService.java | 61 +++++
.../web/config/ControllersTestConfig.java | 26 +-
.../web/config/WebConfigTest.java | 2 +-
.../AppMetaDataControllerTest.java | 3 +-
.../controllers/ComparisonControllerTest.java | 4 +-
...positeSnapshotControllerPermitAllTest.java | 4 +-
.../CompositeSnapshotControllerTest.java | 37 +--
.../ConfigurationControllerPermitAllTest.java | 4 +-
.../ConfigurationControllerTest.java | 41 ++--
.../FilterControllerPermitAllTest.java | 4 +-
.../web/controllers/FilterControllerTest.java | 46 ++--
.../web/controllers/HelpResourceTest.java | 4 +-
.../NodeControllerPermitAllTest.java | 4 +-
.../web/controllers/NodeControllerTest.java | 42 ++--
.../web/controllers/SearchControllerTest.java | 4 +-
.../SnapshotControllerPermitAllTest.java | 4 +-
.../controllers/SnapshotControllerTest.java | 45 ++--
.../SnapshotRestorerControllerTest.java | 4 +-
.../StructureControllerPermitAllTest.java | 4 +-
.../controllers/StructureControllerTest.java | 34 +--
.../TagControllerPermitAllTest.java | 4 +-
.../web/controllers/TagControllerTest.java | 4 +-
.../TakeSnapshotControllerTest.java | 4 +-
36 files changed, 394 insertions(+), 660 deletions(-)
delete mode 100644 services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocket.java
create mode 100644 services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java
delete mode 100644 services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketHandler.java
create mode 100644 services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/application/Application.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/application/Application.java
index 5242d9bd27..907748ea15 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/application/Application.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/application/Application.java
@@ -24,6 +24,7 @@
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.ArrayList;
@@ -34,7 +35,8 @@
/**
* Save-and-restore service main class.
*/
-@SpringBootApplication(scanBasePackages = "org.phoebus.service.saveandrestore")
+@SpringBootApplication
+@ComponentScan(basePackages = {"org.phoebus.service.saveandrestore"})
@EnableScheduling
@EnableAutoConfiguration
public class Application {
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
index bb10c87ca2..2d20f7517f 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
@@ -20,15 +20,12 @@
import org.phoebus.saveandrestore.util.SnapshotUtil;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -44,7 +41,7 @@ public class WebConfiguration {
@SuppressWarnings("unused")
@Bean
- public long getConnectionTimeout(){
+ public long getConnectionTimeout() {
return connectionTimeout;
}
@@ -71,20 +68,13 @@ public AcceptHeaderResolver acceptHeaderResolver() {
@SuppressWarnings("unused")
@Bean
@Scope("singleton")
- public SnapshotUtil snapshotRestorer(){
+ public SnapshotUtil snapshotRestorer() {
return new SnapshotUtil();
}
@SuppressWarnings("unused")
@Bean
- public ExecutorService executorService(){
+ public ExecutorService executorService() {
return Executors.newCachedThreadPool();
}
-
- @SuppressWarnings("unused")
- @Bean(name = "sockets")
- @Scope("singleton")
- public List getSockets() {
- return new CopyOnWriteArrayList<>();
- }
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
index 5fd64d7e46..d2955b20ff 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
@@ -19,11 +19,15 @@
package org.phoebus.service.saveandrestore.web.controllers;
-import org.phoebus.applications.saveandrestore.model.*;
+import org.phoebus.applications.saveandrestore.model.CompositeSnapshot;
+import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData;
+import org.phoebus.applications.saveandrestore.model.Node;
+import org.phoebus.applications.saveandrestore.model.NodeType;
+import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
@@ -48,13 +52,16 @@ public class CompositeSnapshotController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
+
+
/**
* Creates a new {@link CompositeSnapshot} {@link Node}.
- * @param parentNodeId Valid id of the {@link Node}s intended parent.
+ *
+ * @param parentNodeId Valid id of the {@link Node}s intended parent.
* @param compositeSnapshot {@link CompositeSnapshot} data.
- * @param principal User {@link Principal} injected by Spring.
+ * @param principal User {@link Principal} injected by Spring.
* @return The new {@link CompositeSnapshot}.
*/
@PutMapping(value = "/composite-snapshot", produces = JSON)
@@ -62,31 +69,32 @@ public class CompositeSnapshotController extends BaseController {
public CompositeSnapshot createCompositeSnapshot(@RequestParam(value = "parentNodeId") String parentNodeId,
@RequestBody CompositeSnapshot compositeSnapshot,
Principal principal) {
- if(!compositeSnapshot.getCompositeSnapshotNode().getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)){
+ if (!compositeSnapshot.getCompositeSnapshotNode().getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)) {
throw new IllegalArgumentException("Composite snapshot node of wrong type");
}
compositeSnapshot.getCompositeSnapshotNode().setUserName(principal.getName());
CompositeSnapshot newCompositeSnapshot = nodeDAO.createCompositeSnapshot(parentNodeId, compositeSnapshot);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newCompositeSnapshot.getCompositeSnapshotNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newCompositeSnapshot.getCompositeSnapshotNode().getUniqueId()));
return newCompositeSnapshot;
}
/**
* Updates/overwrites a {@link CompositeSnapshot} {@link Node}.
+ *
* @param compositeSnapshot {@link CompositeSnapshot} data.
- * @param principal User {@link Principal} injected by Spring.
+ * @param principal User {@link Principal} injected by Spring.
* @return The new {@link CompositeSnapshot}.
*/
@PostMapping(value = "/composite-snapshot", produces = JSON)
@PreAuthorize("@authorizationHelper.mayUpdate(#compositeSnapshot, #root)")
public CompositeSnapshot updateCompositeSnapshot(@RequestBody CompositeSnapshot compositeSnapshot,
Principal principal) {
- if(!compositeSnapshot.getCompositeSnapshotNode().getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)){
+ if (!compositeSnapshot.getCompositeSnapshotNode().getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)) {
throw new IllegalArgumentException("Composite snapshot node of wrong type");
}
compositeSnapshot.getCompositeSnapshotNode().setUserName(principal.getName());
CompositeSnapshot updatedCompositeSnapshot = nodeDAO.updateCompositeSnapshot(compositeSnapshot);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedCompositeSnapshot.getCompositeSnapshotNode()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedCompositeSnapshot.getCompositeSnapshotNode()));
return updatedCompositeSnapshot;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
index 45a0bc52d3..830ecd2b26 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
@@ -24,7 +24,7 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
@@ -49,15 +49,15 @@ public class ConfigurationController extends BaseController {
@Autowired
private NodeDAO nodeDAO;
- @SuppressWarnings("unused")
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
/**
* Creates new {@link Configuration} {@link Node}.
- * @param parentNodeId Valid id of the {@link Node}s intended parent.
+ *
+ * @param parentNodeId Valid id of the {@link Node}s intended parent.
* @param configuration {@link Configuration} data.
- * @param principal User {@link Principal} injected by Spring.
+ * @param principal User {@link Principal} injected by Spring.
* @return The new {@link Configuration}.
*/
@SuppressWarnings("unused")
@@ -66,25 +66,26 @@ public class ConfigurationController extends BaseController {
public Configuration createConfiguration(@RequestParam(value = "parentNodeId") String parentNodeId,
@RequestBody Configuration configuration,
Principal principal) {
- for(ConfigPv configPv : configuration.getConfigurationData().getPvList()){
+ for (ConfigPv configPv : configuration.getConfigurationData().getPvList()) {
// Compare mode is set, verify tolerance is non-null
- if(configPv.getComparison() != null && (configPv.getComparison().getComparisonMode() == null || configPv.getComparison().getTolerance() == null)){
+ if (configPv.getComparison() != null && (configPv.getComparison().getComparisonMode() == null || configPv.getComparison().getTolerance() == null)) {
throw new IllegalArgumentException("PV item \"" + configPv.getPvName() + "\" specifies comparison but no comparison or tolerance value");
}
// Tolerance is set...
- if(configPv.getComparison() != null && configPv.getComparison().getTolerance() < 0){
+ if (configPv.getComparison() != null && configPv.getComparison().getTolerance() < 0) {
//Tolerance is less than zero, which does not make sense as comparison considers tolerance as upper and lower limit.
throw new IllegalArgumentException("PV item \"" + configPv.getPvName() + "\" specifies zero tolerance");
- }
+ }
}
configuration.getConfigurationNode().setUserName(principal.getName());
Configuration newConfiguration = nodeDAO.createConfiguration(parentNodeId, configuration);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newConfiguration.getConfigurationNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newConfiguration.getConfigurationNode().getUniqueId()));
return newConfiguration;
}
/**
* Retrieves data associated with a {@link Configuration} {@link Node}-.
+ *
* @param uniqueId unique {@link Node} id of a {@link Configuration}.
* @return A {@link ConfigurationData} object.
*/
@@ -96,8 +97,9 @@ public ConfigurationData getConfigurationData(@PathVariable String uniqueId) {
/**
* Updates/overwrites an existing {@link Configuration}
+ *
* @param configuration The {@link Configuration} subject to update.
- * @param principal User {@link Principal} injected by Spring.
+ * @param principal User {@link Principal} injected by Spring.
* @return The updated {@link Configuration}.
*/
@SuppressWarnings("unused")
@@ -107,7 +109,7 @@ public Configuration updateConfiguration(@RequestBody Configuration configuratio
Principal principal) {
configuration.getConfigurationNode().setUserName(principal.getName());
Configuration updatedConfiguration = nodeDAO.updateConfiguration(configuration);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedConfiguration.getConfigurationNode()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedConfiguration.getConfigurationNode()));
return updatedConfiguration;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
index d06e120eef..eee74041f4 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
@@ -23,7 +23,7 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -47,7 +47,7 @@ public class FilterController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
/**
* Saves a new or updated {@link Filter}.
@@ -63,7 +63,7 @@ public Filter saveFilter(@RequestBody final Filter filter,
Principal principal) {
filter.setUser(principal.getName());
Filter savedFilter = nodeDAO.saveFilter(filter);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_ADDED_OR_UPDATED, filter));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_ADDED_OR_UPDATED, filter));
return savedFilter;
}
@@ -87,6 +87,6 @@ public List getAllFilters() {
@PreAuthorize("@authorizationHelper.maySaveOrDeleteFilter(#name, #root)")
public void deleteFilter(@PathVariable final String name, Principal principal) {
nodeDAO.deleteFilter(name);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_REMOVED, name));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_REMOVED, name));
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
index 41005b1185..8ccf7378e4 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
@@ -23,7 +23,7 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -51,8 +51,7 @@ public class NodeController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
-
+ private WebSocketService webSocketService;
/**
* Create a new folder in the tree structure.
@@ -84,7 +83,7 @@ public Node createNode(@RequestParam(name = "parentNodeId") String parentsUnique
}
node.setUserName(principal.getName());
Node savedNode = nodeDAO.createNode(parentsUniqueId, node);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED,
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED,
savedNode.getUniqueId()));
return savedNode;
}
@@ -159,7 +158,7 @@ public List getChildNodes(@PathVariable final String uniqueNodeId) {
public void deleteNodes(@RequestBody List nodeIds) {
nodeDAO.deleteNodes(nodeIds);
nodeIds.forEach(id ->
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, id)));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, id)));
}
/**
@@ -221,7 +220,7 @@ public Node updateNode(@RequestParam(value = "customTimeForMigration", required
}
nodeToUpdate.setUserName(principal.getName());
Node updatedNode = nodeDAO.updateNode(nodeToUpdate, Boolean.parseBoolean(customTimeForMigration));
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedNode));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedNode));
return updatedNode;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
index 5f9ed63217..3c48680ae2 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
@@ -24,10 +24,16 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
import java.util.List;
@@ -43,7 +49,8 @@ public class SnapshotController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
+
/**
*
* @param uniqueId Unique {@link Node} id of a snapshot.
@@ -65,28 +72,30 @@ public List getAllSnapshots() {
/**
* Creates a new {@link Snapshot}
+ *
* @param parentNodeId Unique {@link Node} id of the new {@link Snapshot}.
- * @param snapshot {@link Snapshot} data.
- * @param principal User {@link Principal} as injected by Spring.
+ * @param snapshot {@link Snapshot} data.
+ * @param principal User {@link Principal} as injected by Spring.
* @return The new {@link Snapshot}.
*/
@PutMapping(value = "/snapshot", produces = JSON)
@PreAuthorize("@authorizationHelper.mayCreate(#root)")
public Snapshot createSnapshot(@RequestParam(value = "parentNodeId") String parentNodeId,
- @RequestBody Snapshot snapshot,
- Principal principal) {
- if(!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)){
+ @RequestBody Snapshot snapshot,
+ Principal principal) {
+ if (!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)) {
throw new IllegalArgumentException("Snapshot node of wrong type");
}
snapshot.getSnapshotNode().setUserName(principal.getName());
Snapshot newSnapshot = nodeDAO.createSnapshot(parentNodeId, snapshot);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newSnapshot.getSnapshotNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newSnapshot.getSnapshotNode().getUniqueId()));
return newSnapshot;
}
/**
* Updates a {@link Snapshot}.
- * @param snapshot The {@link Snapshot} subject to update.
+ *
+ * @param snapshot The {@link Snapshot} subject to update.
* @param principal User {@link Principal} as injected by Spring.
* @return The updated {@link Snapshot}
*/
@@ -94,12 +103,12 @@ public Snapshot createSnapshot(@RequestParam(value = "parentNodeId") String pare
@PreAuthorize("@authorizationHelper.mayUpdate(#snapshot, #root)")
public Snapshot updateSnapshot(@RequestBody Snapshot snapshot,
Principal principal) {
- if(!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)){
+ if (!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)) {
throw new IllegalArgumentException("Snapshot node of wrong type");
}
snapshot.getSnapshotNode().setUserName(principal.getName());
Snapshot updatedSnapshot = nodeDAO.updateSnapshot(snapshot);
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedSnapshot.getSnapshotNode()));
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedSnapshot.getSnapshotNode()));
return updatedSnapshot;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
index cf90c4714a..597e4c82c5 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
@@ -21,11 +21,16 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.security.Principal;
@@ -45,7 +50,7 @@ public class StructureController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
/**
* Moves a list of source nodes to a new target (parent) node.
@@ -71,9 +76,9 @@ public Node moveNodes(@RequestParam(value = "to") String to,
Logger.getLogger(StructureController.class.getName()).info(Thread.currentThread().getName() + " " + (new Date()) + " move");
Node targetNode = nodeDAO.moveNodes(nodes, to, principal.getName());
// Update clients
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
targetNode));
- webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
+ webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
sourceParentNode));
return targetNode;
}
@@ -101,7 +106,7 @@ public Node copyNodes(@RequestParam(value = "to") String to,
Logger.getLogger(StructureController.class.getName()).info(Thread.currentThread().getName() + " " + (new Date()) + " copy");
Node targetNode = nodeDAO.getNode(to);
List newNodes = nodeDAO.copyNodes(nodes, to, principal.getName());
- newNodes.forEach(n -> webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED, n.getUniqueId())));
+ newNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED, n.getUniqueId())));
return targetNode;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
index cb4b306a20..015d1004cd 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
@@ -27,7 +27,7 @@
import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -35,7 +35,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.socket.WebSocketMessage;
import java.security.Principal;
import java.util.List;
@@ -54,7 +53,7 @@ public class TagController extends BaseController {
private NodeDAO nodeDAO;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
/**
* @return A {@link List} of all {@link Tag}s.
@@ -78,7 +77,7 @@ public List addTag(@RequestBody TagData tagData,
Principal principal) {
tagData.getTag().setUserName(principal.getName());
List taggedNodes = nodeDAO.addTag(tagData);
- taggedNodes.forEach(n -> webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
+ taggedNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
return taggedNodes;
}
@@ -93,7 +92,7 @@ public List addTag(@RequestBody TagData tagData,
@PreAuthorize("@authorizationHelper.mayAddOrDeleteTag(#tagData, #root)")
public List deleteTag(@RequestBody TagData tagData) {
List untaggedNodes = nodeDAO.deleteTag(tagData);
- untaggedNodes.forEach(n -> webSocketHandler.sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
+ untaggedNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
return untaggedNodes;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocket.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocket.java
deleted file mode 100644
index c97e944c16..0000000000
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocket.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2019-2022 UT-Battelle, LLC.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the LICENSE
- * which accompanies this distribution
- ******************************************************************************/
-package org.phoebus.service.saveandrestore.websocket;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.web.socket.PingMessage;
-import org.springframework.web.socket.TextMessage;
-import org.springframework.web.socket.WebSocketSession;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.time.Instant;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Utility class for handling web socket messages.
- */
-@SuppressWarnings("nls")
-public class WebSocket {
-
- /**
- * Is the queue full?
- */
- private final AtomicBoolean stuffed = new AtomicBoolean();
-
- /**
- * Queue of messages for the client.
- *
- * Multiple threads concurrently writing to the socket results in
- * IllegalStateException "remote endpoint was in state [TEXT_FULL_WRITING]"
- * All writes are thus performed by just one thread off this queue.
- */
- private final ArrayBlockingQueue writeQueue = new ArrayBlockingQueue<>(2048);
-
- private static final String EXIT_MESSAGE = "EXIT";
-
- private final WebSocketSession session;
- private final String id;
- private final String description;
- private final Logger logger = Logger.getLogger(WebSocket.class.getName());
- private final ObjectMapper objectMapper;
-
- /**
- * Keeps track of when this session was used for a ping/pong exchange. Should be set to non-null value ONLY
- * when an actual pong was received by {@link WebSocketHandler}.
- */
- private Instant lastPinged;
-
- /**
- * Constructor
- */
- public WebSocket(ObjectMapper objectMapper, WebSocketSession webSocketSession) {
- this.session = webSocketSession;
- logger.log(Level.INFO, () -> "Creating web socket " + session.getUri() + " ID " + session.getId());
- this.objectMapper = objectMapper;
- this.id = webSocketSession.getId();
- Thread writeThread = new Thread(this::writeQueuedMessages, "Web Socket Write Thread");
- writeThread.setName("Web Socket Write Thread " + this.id);
- writeThread.setDaemon(true);
- writeThread.start();
- InetSocketAddress inetSocketAddress = webSocketSession.getRemoteAddress();
- this.description = this.id + "/" + (inetSocketAddress != null ? inetSocketAddress.getAddress().toString() : "IP address unknown");
- }
-
- /**
- * @return Session ID
- */
- public String getId() {
- if (session == null)
- return "(" + id + ")";
- else
- return id;
- }
-
- /**
- *
- * @return A description containing the session ID and - if available - the associated IP address.
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * @param message Potentially long message
- * @return Message shorted to 200 chars
- */
- private String shorten(final String message) {
- if (message == null || message.length() < 200)
- return message;
- return message.substring(0, 200) + " ...";
- }
-
- public void queueMessage(final String message) {
- // Ignore messages after 'dispose'
- if (session == null)
- return;
-
- if (writeQueue.offer(message)) { // Queued OK. Is this a recovery from stuffed queue?
- if (stuffed.getAndSet(false))
- logger.log(Level.WARNING, () -> "Un-stuffed message queue for " + id);
- } else { // Log, but only for the first message to prevent flooding the log
- if (!stuffed.getAndSet(true))
- logger.log(Level.WARNING, () -> "Cannot queue message '" + shorten(message) + "' for " + id);
- }
- }
-
- private void writeQueuedMessages() {
- try {
- while (true) {
- final String message;
- try {
- message = writeQueue.take();
- } catch (final InterruptedException ex) {
- return;
- }
-
- // Check if we should exit the thread
- if (message.equals(EXIT_MESSAGE)) {
- logger.log(Level.FINE, () -> "Exiting write thread " + id);
- return;
- }
-
- final WebSocketSession safeSession = session;
- try {
- if (safeSession == null)
- throw new Exception("No session");
- if (!safeSession.isOpen())
- throw new Exception("Session closed");
- safeSession.sendMessage(new TextMessage(message));
- } catch (final Exception ex) {
- logger.log(Level.WARNING, ex, () -> "Cannot write '" + shorten(message) + "' for " + id);
-
- // Clear queue
- String drop = writeQueue.take();
- while (drop != null) {
- if (drop.equals(EXIT_MESSAGE)) {
- logger.log(Level.FINE, () -> "Exiting write thread " + id);
- return;
- }
- drop = writeQueue.take();
- }
- }
- }
- } catch (Throwable ex) {
- logger.log(Level.WARNING, "Write thread error for " + id, ex);
- }
- }
-
- /**
- * Called when client sends a generic message
- *
- * @param message {@link TextMessage}, its payload is expected to be JSON.
- */
- public void handleTextMessage(TextMessage message) throws Exception {
- final JsonNode json = objectMapper.readTree(message.getPayload());
- final JsonNode node = json.path("type");
- if (node.isMissingNode())
- throw new Exception("Missing 'type' in " + shorten(message.getPayload()));
- final String type = node.asText();
- logger.log(Level.INFO, "Client message type: " + type);
- }
-
- public void dispose() {
- // Exit write thread
- try {
- // Drop queued messages (which might be stuffed):
- // We're closing and just need the EXIT_MESSAGE
- writeQueue.clear();
- queueMessage(EXIT_MESSAGE);
- // TODO: is this needed?
- session.close();
- } catch (Throwable ex) {
- logger.log(Level.WARNING, "Error disposing " + description, ex);
- }
- logger.log(Level.INFO, () -> "Web socket " + description + " closed");
- }
-
- /**
- * Sets the time of last received pong message.
- * @param instant Time of last received pong message.
- */
- public synchronized void setLastPinged(Instant instant) {
- this.lastPinged = instant;
- }
-
- /**
- *
- * @return The time of last received pong message.
- */
- public synchronized Instant getLastPinged() {
- return lastPinged;
- }
-
- /**
- * Sends a {@link PingMessage} to peer.
- */
- public void sendPing() {
- try {
- session.sendMessage(new PingMessage());
- } catch (IOException e) {
- logger.log(Level.WARNING, "Failed to send ping message", e);
- }
- }
-}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
index f0e89df46d..46485fce04 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
@@ -20,31 +20,42 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
-import org.springframework.web.socket.config.annotation.EnableWebSocket;
-import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
-import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
-import java.util.List;
import java.util.logging.Logger;
@SuppressWarnings("unused")
@Configuration
-@EnableWebSocket
-public class WebSocketConfig implements WebSocketConfigurer {
+@EnableWebSocketMessageBroker
+public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
public ObjectMapper objectMapper;
- @Autowired
- private List sockets;
+ private final Logger logger = Logger.getLogger(WebSocketConfig.class.getName());
+
+ private TaskScheduler messageBrokerTaskScheduler;
@Autowired
- private WebSocketHandler webSocketHandler;
+ public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
+ this.messageBrokerTaskScheduler = taskScheduler;
+ }
- private final Logger logger = Logger.getLogger(WebSocketConfig.class.getName());
+ @Override
+ public void configureMessageBroker(MessageBrokerRegistry config) {
+ config.enableSimpleBroker("/web-socket/messages")
+ .setHeartbeatValue(new long[]{30000, 30000})
+ .setTaskScheduler(this.messageBrokerTaskScheduler);
+ config.setApplicationDestinationPrefixes("/web-socket");
+ }
@Override
- public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(webSocketHandler, "/web-socket");
+ public void registerStompEndpoints(StompEndpointRegistry registry) {
+ registry.addEndpoint("/web-socket");
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java
new file mode 100644
index 0000000000..389fd33bee
--- /dev/null
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package org.phoebus.service.saveandrestore.websocket;
+
+
+import org.springframework.messaging.handler.annotation.MessageMapping;
+import org.springframework.messaging.handler.annotation.SendTo;
+import org.springframework.stereotype.Controller;
+
+/**
+ * This {@link Controller} defines an echo endpoint, i.e. for testing or health check purposes...
+ */
+@Controller
+@SuppressWarnings("unused")
+public class WebSocketController {
+
+
+ @MessageMapping("/echo")
+ @SendTo("web-socket/messages")
+ public String echo(String message) {
+ return message;
+ }
+}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketHandler.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketHandler.java
deleted file mode 100644
index a539f573f7..0000000000
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketHandler.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- *
- */
-
-package org.phoebus.service.saveandrestore.websocket;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.lang.NonNull;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.web.socket.CloseStatus;
-import org.springframework.web.socket.PongMessage;
-import org.springframework.web.socket.TextMessage;
-import org.springframework.web.socket.WebSocketSession;
-import org.springframework.web.socket.handler.TextWebSocketHandler;
-
-import javax.annotation.PreDestroy;
-import java.io.EOFException;
-import java.net.InetSocketAddress;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Single web socket end-point routing messages to active {@link WebSocket} instances.
- *
- *
- * In some cases web socket clients may become stale/disconnected for various reasons, e.g. network issues. The
- * {@link #afterConnectionClosed(WebSocketSession, CloseStatus)} is not necessarily called in those case.
- * To make sure the {@link #sockets} collection does not contain stale clients, a scheduled job runs once per hour to
- * ping all clients, and set the time when the pong response was received. Another scheduled job will check
- * the last received pong message timestamp and - if older than 70 minutes - consider the client session dead
- * and dispose of it.
- *
- */
-@Component
-public class WebSocketHandler extends TextWebSocketHandler {
-
- /**
- * List of active {@link WebSocket}
- */
- @SuppressWarnings("unused")
- private List sockets = Collections.synchronizedList(new ArrayList<>());
-
- @SuppressWarnings("unused")
- @Autowired
- private ObjectMapper objectMapper;
-
- @SuppressWarnings("unused")
-
- private final Logger logger = Logger.getLogger(WebSocketHandler.class.getName());
-
- /**
- * Handles text message from web socket client
- *
- * @param session The {@link WebSocketSession} associated with the remote client.
- * @param message Message sent by client
- */
- @Override
- public void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
- try {
- // Find the WebSocket instance associated with the WebSocketSession
- Optional webSocketOptional;
- synchronized (sockets){
- webSocketOptional =
- sockets.stream().filter(webSocket -> webSocket.getId().equals(session.getId())).findFirst();
- }
- if (webSocketOptional.isEmpty()) {
- return; // Should only happen in case of timing issues?
- }
- webSocketOptional.get().handleTextMessage(message);
- } catch (final Exception ex) {
- logger.log(Level.WARNING, ex, () -> "Error for message " + shorten(message.getPayload()));
- }
- }
-
- /**
- * Called when client connects.
- *
- * @param session Associated {@link WebSocketSession}
- */
- @Override
- public void afterConnectionEstablished(@NonNull WebSocketSession session) {
- InetSocketAddress inetSocketAddress = session.getRemoteAddress();
- logger.log(Level.INFO, "Opening web socket session from remote " + (inetSocketAddress != null ? inetSocketAddress.getAddress().toString() : ""));
- WebSocket webSocket = new WebSocket(objectMapper, session);
- sockets.add(webSocket);
- }
-
- /**
- * Called when web socket is closed. Depending on the web browser, {@link #handleTransportError(WebSocketSession, Throwable)}
- * may be called first.
- *
- * @param session Associated {@link WebSocketSession}
- * @param status See {@link CloseStatus}
- */
- @Override
- public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) {
- Optional webSocketOptional;
- synchronized (sockets){
- webSocketOptional = sockets.stream().filter(webSocket -> webSocket.getId().equals(session.getId())).findFirst();
- }
- if (webSocketOptional.isPresent()) {
- logger.log(Level.INFO, "Closing web socket session " + webSocketOptional.get().getDescription());
- webSocketOptional.get().dispose();
- sockets.remove(webSocketOptional.get());
- }
- }
-
- /**
- * Depending on the web browser, this is called before {@link #afterConnectionClosed(WebSocketSession, CloseStatus)}
- * when tab or browser is closes.
- *
- * @param session Associated {@link WebSocketSession}
- * @param ex {@link Throwable} that should indicate reason
- */
- @Override
- public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable ex) {
- if (ex instanceof EOFException)
- logger.log(Level.FINE, "Web Socket closed", ex);
- else
- logger.log(Level.WARNING, "Web Socket error", ex);
- }
-
- /**
- * Called when client sends ping message, i.e. a pong message is sent and time for last pong response message
- * in the {@link WebSocket} instance is refreshed.
- *
- * @param session Associated {@link WebSocketSession}
- * @param message See {@link PongMessage}
- */
- @Override
- protected void handlePongMessage(@NonNull WebSocketSession session, @NonNull PongMessage message) {
- logger.log(Level.FINE, "Got pong for session " + session.getId());
- // Find the WebSocket instance associated with this WebSocketSession
- Optional webSocketOptional;
- synchronized (sockets) {
- webSocketOptional = sockets.stream().filter(webSocket -> webSocket.getId().equals(session.getId())).findFirst();
- }
- if (webSocketOptional.isPresent()) {
- webSocketOptional.get().setLastPinged(Instant.now());
- }
- }
-
- /**
- * @param message Potentially long message
- * @return Message shorted to 200 chars
- */
- private String shorten(final String message) {
- if (message == null || message.length() < 200)
- return message;
- return message.substring(0, 200) + " ...";
- }
-
- @PreDestroy
- public void cleanup() {
- synchronized (sockets) {
- sockets.forEach(s -> {
- logger.log(Level.INFO, "Disposing socket " + s.getDescription());
- s.dispose();
- });
- }
- }
-
- public void sendMessage(SaveAndRestoreWebSocketMessage webSocketMessage) {
- synchronized (sockets) {
- sockets.forEach(ws -> {
- try {
- ws.queueMessage(objectMapper.writeValueAsString(webSocketMessage));
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- });
- }
- }
-
- /**
- * Sends a ping message to all clients contained in {@link #sockets}.
- *
- * This is scheduled to run at the top of each hour, i.e. 00.00, 01.00...23.00
- *
- *
- */
- @SuppressWarnings("unused")
- @Scheduled(cron = "* 0 * * * *")
- public void pingClients(){
- synchronized (sockets) {
- sockets.forEach(WebSocket::sendPing);
- }
- }
-
- /**
- * For each client in {@link #sockets}, checks the timestamp of last received pong message. If this is older
- * than 70 minutes, the socket is considered dead, and then disposed.
- *
- * This is scheduled to run 5 minutes past each hour, i.e. 00.05, 01.05...23.05
- *
- *
- */
- @SuppressWarnings("unused")
- @Scheduled(cron = "* 5 * * * *")
- public void cleanUpDeadSockets(){
- List deadSockets = new ArrayList<>();
- Instant now = Instant.now();
- synchronized (sockets) {
- sockets.forEach(s -> {
- Instant lastPinged = s.getLastPinged();
- if (lastPinged != null && lastPinged.isBefore(now.minus(70, ChronoUnit.MINUTES))) {
- deadSockets.add(s);
- }
- });
- deadSockets.forEach(d -> {
- sockets.remove(d);
- d.dispose();
- });
- }
- }
-}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
new file mode 100644
index 0000000000..7b9c534ade
--- /dev/null
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package org.phoebus.service.saveandrestore.websocket;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.NonNull;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.messaging.simp.user.SimpUserRegistry;
+import org.springframework.stereotype.Service;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+@Service
+public class WebSocketService {
+
+ @SuppressWarnings("unused")
+ @Autowired
+ private SimpMessagingTemplate simpMessagingTemplate;
+
+ @SuppressWarnings("unused")
+ @Autowired
+ private SimpUserRegistry simpUserRegistry;
+
+ @SuppressWarnings("unused")
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ private static final Logger logger = Logger.getLogger(WebSocketService.class.getName());
+
+ /**
+ * @param webSocketMessage Non-null {@link SaveAndRestoreWebSocketMessage}, will be converted to a JSON string before it is dispatched.
+ */
+ public void sendMessageToClients(@NonNull SaveAndRestoreWebSocketMessage webSocketMessage) {
+ try {
+ String message = objectMapper.writeValueAsString(webSocketMessage);
+ simpMessagingTemplate.convertAndSend("/web-socket/messages", message);
+ } catch (JsonProcessingException e) {
+ logger.log(Level.WARNING, "Failed to write web socket message to json string", e);
+ }
+ }
+}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
index cfb5058ecd..b895a7ead7 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
@@ -27,22 +27,24 @@
import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.FilterRepository;
import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.SnapshotDataRepository;
import org.phoebus.service.saveandrestore.search.SearchUtil;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.util.Base64Utils;
import org.springframework.web.socket.WebSocketSession;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-@SpringBootConfiguration
+@TestConfiguration
@ComponentScan(basePackages = "org.phoebus.service.saveandrestore.web.controllers")
-@Import(WebSecurityConfig.class)
@SuppressWarnings("unused")
@Profile("!IT")
public class ControllersTestConfig {
@@ -133,17 +135,27 @@ public SnapshotUtil snapshotUtil() {
}
@Bean
- public WebSocketSession webSocketSession(){
+ public WebSocketSession webSocketSession() {
return Mockito.mock(WebSocketSession.class);
}
@Bean
- public WebSocketHandler webSocketHandler(){
- return Mockito.mock(WebSocketHandler.class);
+ public WebSocketService webSocketService() {
+ return Mockito.mock(WebSocketService.class);
}
@Bean
- public long connectionTimeout(){
+ public SimpMessagingTemplate simpMessagingTemplate(){
+ return Mockito.mock(SimpMessagingTemplate.class);
+ }
+
+ @Bean
+ public SimpUserRegistry simpUserRegistry(){
+ return Mockito.mock(SimpUserRegistry.class);
+ }
+
+ @Bean
+ public long connectionTimeout() {
return 5000;
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/WebConfigTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/WebConfigTest.java
index da1005650f..67633bc4df 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/WebConfigTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/WebConfigTest.java
@@ -29,7 +29,7 @@
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
-@ContextHierarchy({@ContextConfiguration(classes = {WebConfiguration.class, ControllersTestConfig.class})})
+@ContextHierarchy({@ContextConfiguration(classes = {WebConfiguration.class, ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
@SuppressWarnings("unused")
public class WebConfigTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/AppMetaDataControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/AppMetaDataControllerTest.java
index d49f0912cf..142d9e90da 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/AppMetaDataControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/AppMetaDataControllerTest.java
@@ -21,6 +21,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@@ -40,7 +41,7 @@
* @author georgweiss
* Created 16 May 2019
*/
-@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class})})
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(AppMetaDataControllerTest.class)
@ExtendWith(SpringExtension.class)
@TestPropertySource(locations = "classpath:test_application.properties")
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonControllerTest.java
index 5afdf55862..99f8a7298c 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ComparisonControllerTest.java
@@ -22,9 +22,11 @@
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -43,7 +45,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
@WebMvcTest(NodeController.class)
public class ComparisonControllerTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java
index 19cec3db71..eacf21be4c 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java
@@ -30,10 +30,12 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -51,7 +53,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(CompositeSnapshotController.class)
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
public class CompositeSnapshotControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
index 1eb9fdfd21..eb75fcae17 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
@@ -21,7 +21,6 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -35,11 +34,13 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -60,7 +61,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(CompositeSnapshotController.class)
@TestPropertySource(locations = "classpath:test_application.properties")
public class CompositeSnapshotControllerTest {
@@ -84,7 +85,7 @@ public class CompositeSnapshotControllerTest {
private MockMvc mockMvc;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
private final ObjectMapper objectMapper = new ObjectMapper();
@@ -101,8 +102,8 @@ public static void init() {
}
@AfterEach
- public void resetMocks(){
- reset(nodeDAO, webSocketHandler);
+ public void resetMocks() {
+ reset(nodeDAO, webSocketService);
}
@Test
@@ -124,7 +125,7 @@ public void testCreateCompositeSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, CompositeSnapshot.class);
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -141,7 +142,7 @@ public void testCreateCompositeSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -156,7 +157,7 @@ public void testCreateCompositeSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -170,12 +171,12 @@ public void testCreateCompositeSnapshot4() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testCreateCompositeSnapshotWrongNodeType() throws Exception{
+ public void testCreateCompositeSnapshotWrongNodeType() throws Exception {
Node node = Node.builder().uniqueId("c").nodeType(NodeType.SNAPSHOT).build();
CompositeSnapshot compositeSnapshot1 = new CompositeSnapshot();
compositeSnapshot1.setCompositeSnapshotNode(node);
@@ -186,7 +187,7 @@ public void testCreateCompositeSnapshotWrongNodeType() throws Exception{
.content(objectMapper.writeValueAsString(compositeSnapshot1));
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -213,7 +214,7 @@ public void testUpdateCompositeSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, CompositeSnapshot.class);
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -234,7 +235,7 @@ public void testUpdateCompositeSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -261,7 +262,7 @@ public void testUpdateCompositeSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -284,11 +285,11 @@ public void testUpdateCompositeSnapshot4() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testUpdateCompositeSnapshotWrongNodeType() throws Exception{
+ public void testUpdateCompositeSnapshotWrongNodeType() throws Exception {
Node node = Node.builder().uniqueId("c").userName(demoUser).nodeType(NodeType.SNAPSHOT).build();
CompositeSnapshot compositeSnapshot1 = new CompositeSnapshot();
compositeSnapshot1.setCompositeSnapshotNode(node);
@@ -301,7 +302,7 @@ public void testUpdateCompositeSnapshotWrongNodeType() throws Exception{
.content(objectMapper.writeValueAsString(compositeSnapshot1));
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java
index ebcdbf45ef..d6d6e64b9b 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java
@@ -30,10 +30,12 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -49,7 +51,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(ConfigurationController.class)
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
public class ConfigurationControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
index ecfc9de044..8db4fc24e0 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
@@ -23,27 +23,24 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-
import org.mockito.Mockito;
import org.phoebus.applications.saveandrestore.model.Comparison;
+import org.phoebus.applications.saveandrestore.model.ComparisonMode;
import org.phoebus.applications.saveandrestore.model.ConfigPv;
-
import org.phoebus.applications.saveandrestore.model.Configuration;
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
-
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
-
-import org.phoebus.applications.saveandrestore.model.ComparisonMode;
-
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -62,7 +59,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(ConfigurationController.class)
@TestPropertySource(locations = "classpath:test_application.properties")
public class ConfigurationControllerTest {
@@ -89,11 +86,11 @@ public class ConfigurationControllerTest {
private String demoUser;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
@AfterEach
- public void resetMocks(){
- reset(nodeDAO, webSocketHandler);
+ public void resetMocks() {
+ reset(nodeDAO, webSocketService);
}
@Test
@@ -114,7 +111,7 @@ public void testCreateConfiguration1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -135,7 +132,7 @@ public void testCreateConfiguration2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -151,11 +148,11 @@ public void testCreateConfiguration3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testCreateConfiguration4() throws Exception{
+ public void testCreateConfiguration4() throws Exception {
Configuration configuration = new Configuration();
configuration.setConfigurationNode(Node.builder().build());
@@ -165,7 +162,7 @@ public void testCreateConfiguration4() throws Exception{
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -186,7 +183,7 @@ public void testUpdateConfiguration1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -207,7 +204,7 @@ public void tesUpdateConfiguration2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -227,7 +224,7 @@ public void testUpdateConfiguration3() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -259,11 +256,11 @@ public void testUpdateConfiguration5() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testUpdateConfiguration6() throws Exception{
+ public void testUpdateConfiguration6() throws Exception {
Configuration configuration = new Configuration();
Node configurationNode = Node.builder().uniqueId("uniqueId").nodeType(NodeType.CONFIGURATION).userName("someUser").build();
@@ -274,7 +271,7 @@ public void testUpdateConfiguration6() throws Exception{
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
mockMvc.perform(request).andExpect(status().isUnauthorized());
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java
index 0864cf5f2a..37746e5b8c 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java
@@ -26,10 +26,12 @@
import org.phoebus.applications.saveandrestore.model.search.Filter;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -47,7 +49,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(FilterController.class)
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
public class FilterControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
index 3d268e201b..7c19d5b52b 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
@@ -28,11 +28,13 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -47,12 +49,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(FilterController.class)
@TestPropertySource(locations = "classpath:test_application.properties")
public class FilterControllerTest {
@@ -79,11 +83,11 @@ public class FilterControllerTest {
private String demoUser;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
@AfterEach
- public void resetMocks(){
- reset(webSocketHandler,nodeDAO);
+ public void resetMocks() {
+ reset(webSocketService, nodeDAO);
}
@Test
@@ -110,7 +114,7 @@ public void testSaveFilter1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, Filter.class);
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -130,7 +134,7 @@ public void testSaveFilter2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@@ -150,12 +154,12 @@ public void testSaveFiliter3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testSaveFiliter4() throws Exception{
+ public void testSaveFiliter4() throws Exception {
Filter filter = new Filter();
filter.setName("name");
@@ -170,7 +174,7 @@ public void testSaveFiliter4() throws Exception{
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@@ -188,7 +192,7 @@ public void testDeleteFilter() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@@ -200,19 +204,19 @@ public void testDeleteFilter2() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
public void testDeleteFilter3() throws Exception {
- MockHttpServletRequestBuilder request = delete("/filter/name")
- .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization)
- .contentType(JSON);
- mockMvc.perform(request).andExpect(status().isForbidden());
+ MockHttpServletRequestBuilder request = delete("/filter/name")
+ .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization)
+ .contentType(JSON);
+ mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@@ -222,12 +226,12 @@ public void testDeleteFilter4() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testDeleteFilter5() throws Exception{
+ public void testDeleteFilter5() throws Exception {
Filter filter = new Filter();
filter.setName("name");
filter.setQueryString("query");
@@ -239,7 +243,7 @@ public void testDeleteFilter5() throws Exception{
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/HelpResourceTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/HelpResourceTest.java
index 3843053690..995e84570a 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/HelpResourceTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/HelpResourceTest.java
@@ -22,9 +22,11 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -35,7 +37,7 @@
@ExtendWith(SpringExtension.class)
@WebMvcTest(HelpResource.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
public class HelpResourceTest{
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java
index 1f591c298e..d646607194 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java
@@ -26,10 +26,12 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -46,7 +48,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
@WebMvcTest(NodeController.class)
/**
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
index 677225fd74..615fac0159 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
@@ -39,11 +39,13 @@
import org.phoebus.service.saveandrestore.NodeNotFoundException;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -53,7 +55,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.reset;
@@ -61,7 +62,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -73,7 +77,7 @@
* @author Georg Weiss, European Spallation Source
*/
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
@WebMvcTest(NodeController.class)
public class NodeControllerTest {
@@ -85,7 +89,7 @@ public class NodeControllerTest {
private MockMvc mockMvc;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
private static Node folderFromClient;
@@ -118,8 +122,8 @@ public static void setUp() {
}
@AfterEach
- public void resetMocks(){
- reset(webSocketHandler, nodeDAO);
+ public void resetMocks() {
+ reset(webSocketService, nodeDAO);
}
@Test
@@ -254,7 +258,7 @@ public void testCreateConfigWithBadToleranceData1() throws Exception {
.contentType(JSON)
.content(objectMapper.writeValueAsString(configuration));
- mockMvc.perform(request).andExpect(status().isBadRequest());
+ mockMvc.perform(request).andExpect(status().isBadRequest());
}
@Test
@@ -436,7 +440,7 @@ public void testDeleteFolder() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -450,7 +454,7 @@ public void testDeleteForbiddenAccess() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
}
@Test
@@ -465,12 +469,12 @@ public void testDeleteForbiddenAccess2() throws Exception {
.header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.CONFIGURATION).userName(demoUser).build());
when(nodeDAO.getChildNodes("a")).thenReturn(Collections.emptyList());
- verify(webSocketHandler, times(0)).sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
}
@@ -487,7 +491,7 @@ public void testDeleteFolder2() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@@ -504,11 +508,11 @@ public void testDeleteFolder3() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testDeleteForbidden3() throws Exception{
+ public void testDeleteForbidden3() throws Exception {
when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.CONFIGURATION).userName(demoUser).build());
when(nodeDAO.getChildNodes("a")).thenReturn(List.of(Node.builder().build()));
@@ -519,11 +523,11 @@ public void testDeleteForbidden3() throws Exception{
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
}
@Test
- public void testDeleteForbidden4() throws Exception{
+ public void testDeleteForbidden4() throws Exception {
when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.FOLDER).userName(demoUser).build());
when(nodeDAO.getChildNodes("a")).thenReturn(List.of(Node.builder().build()));
@@ -534,12 +538,12 @@ public void testDeleteForbidden4() throws Exception{
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
}
@Test
- public void testDeleteFolder5() throws Exception{
+ public void testDeleteFolder5() throws Exception {
when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.CONFIGURATION).userName(demoUser).build());
when(nodeDAO.getChildNodes("a")).thenReturn(List.of(Node.builder().build()));
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java
index 07ee27333e..05113d1fb4 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java
@@ -30,10 +30,12 @@
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.search.SearchUtil;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -53,7 +55,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(SearchController.class)
@TestPropertySource(locations = "classpath:test_application.properties")
public class SearchControllerTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java
index a56b005c04..b1ef17fa87 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java
@@ -29,10 +29,12 @@
import org.phoebus.applications.saveandrestore.model.Snapshot;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -49,7 +51,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
@WebMvcTest(SnapshotController.class)
public class SnapshotControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
index 8aaddec51c..f5c8225923 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
@@ -31,11 +31,13 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -43,7 +45,6 @@
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import java.util.List;
-import java.util.Set;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -57,7 +58,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
@WebMvcTest(SnapshotController.class)
public class SnapshotControllerTest {
@@ -83,11 +84,11 @@ public class SnapshotControllerTest {
private String demoUser;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
@AfterEach
- public void resetMocks(){
- reset(webSocketHandler, nodeDAO);
+ public void resetMocks() {
+ reset(webSocketService, nodeDAO);
}
@@ -108,7 +109,7 @@ public void testSaveSnapshotWrongNodeType() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -123,7 +124,7 @@ public void testSaveSnapshotNoParentNodeId() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -149,7 +150,7 @@ public void testCreateSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class);
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -172,11 +173,11 @@ public void testCreateSnapshot2() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testCreateSnapshot3() throws Exception{
+ public void testCreateSnapshot3() throws Exception {
Node node = Node.builder().uniqueId("uniqueId").nodeType(NodeType.SNAPSHOT).userName(demoUser).build();
Snapshot snapshot = new Snapshot();
@@ -193,7 +194,7 @@ public void testCreateSnapshot3() throws Exception{
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -219,7 +220,7 @@ public void testUpdateSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class);
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -236,7 +237,7 @@ public void testUpdateSnapshot2() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -254,11 +255,11 @@ public void testUpdateSnapshot3() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testUpdateSnapshot4() throws Exception{
+ public void testUpdateSnapshot4() throws Exception {
Node node = Node.builder().uniqueId("s").nodeType(NodeType.SNAPSHOT).userName(demoUser).build();
Snapshot snapshot = new Snapshot();
@@ -275,7 +276,7 @@ public void testUpdateSnapshot4() throws Exception{
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -296,7 +297,7 @@ public void testDeleteSnapshot1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -309,7 +310,7 @@ public void testDeleteSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -329,11 +330,11 @@ public void testDeleteSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketHandler, times(1)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testDeleteSnapshot4() throws Exception{
+ public void testDeleteSnapshot4() throws Exception {
MockHttpServletRequestBuilder request =
delete("/node")
@@ -341,6 +342,6 @@ public void testDeleteSnapshot4() throws Exception{
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerControllerTest.java
index 5b15bacf46..1d7485b9da 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerControllerTest.java
@@ -15,10 +15,12 @@
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -34,7 +36,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
@WebMvcTest(SnapshotRestoreController.class)
public class SnapshotRestorerControllerTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java
index c82b44e5ae..02344101a7 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java
@@ -25,10 +25,12 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -44,7 +46,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
@WebMvcTest(StructureController.class)
public class StructureControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
index 67eeb88386..7a11c10e02 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
@@ -28,11 +28,13 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
-import org.phoebus.service.saveandrestore.websocket.WebSocketHandler;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
+import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -52,7 +54,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application.properties")
@WebMvcTest(StructureController.class)
public class StructureControllerTest {
@@ -81,11 +83,11 @@ public class StructureControllerTest {
private String readOnlyAuthorization;
@Autowired
- private WebSocketHandler webSocketHandler;
+ private WebSocketService webSocketService;
@AfterEach
- public void resetMocks(){
- reset(webSocketHandler, nodeDAO);
+ public void resetMocks() {
+ reset(webSocketService, nodeDAO);
}
@Test
@@ -106,11 +108,11 @@ public void testMoveNode1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Node.class);
- verify(webSocketHandler, times(2)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(2)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testMoveNode2() throws Exception{
+ public void testMoveNode2() throws Exception {
when(nodeDAO.moveNodes(List.of("a"), "b", demoUser))
.thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoUser).build());
@@ -124,11 +126,11 @@ public void testMoveNode2() throws Exception{
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testMoveNode3() throws Exception{
+ public void testMoveNode3() throws Exception {
MockHttpServletRequestBuilder request = post("/move")
.header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization)
@@ -147,7 +149,7 @@ public void testMoveNode3() throws Exception{
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -161,7 +163,7 @@ public void testMoveNodeSourceNodeListEmpty() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -175,7 +177,7 @@ public void testMoveNodeTargetIdEmpty() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -194,11 +196,11 @@ public void testCopyNodes1() throws Exception {
.param("to", "target");
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
- public void testCopyNodes2() throws Exception{
+ public void testCopyNodes2() throws Exception {
MockHttpServletRequestBuilder request = post("/copy")
.contentType(JSON)
@@ -217,7 +219,7 @@ public void testCopyNodesBadRequest1() throws Exception {
.param("to", "");
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
@Test
@@ -241,6 +243,6 @@ public void testCopyNodesBadRequest3() throws Exception {
.param("to", "target");
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketHandler, times(0)).sendMessage(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
}
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java
index bc11cfc137..df0f938952 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java
@@ -27,10 +27,12 @@
import org.phoebus.applications.saveandrestore.model.TagData;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -47,7 +49,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(TagController.class)
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
public class TagControllerPermitAllTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java
index b7f1afb71c..f3dbd629de 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java
@@ -27,10 +27,12 @@
import org.phoebus.applications.saveandrestore.model.TagData;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -46,7 +48,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@WebMvcTest(TagController.class)
@TestPropertySource(locations = "classpath:test_application.properties")
public class TagControllerTest {
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TakeSnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TakeSnapshotControllerTest.java
index 06c6f3d8b2..c8aef1b267 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TakeSnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TakeSnapshotControllerTest.java
@@ -13,9 +13,11 @@
import org.phoebus.service.saveandrestore.NodeNotFoundException;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
+import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
@@ -32,7 +34,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = ControllersTestConfig.class)
+@ContextHierarchy({@ContextConfiguration(classes = {ControllersTestConfig.class, WebSecurityConfig.class})})
@TestPropertySource(locations = "classpath:test_application_permit_all.properties")
@WebMvcTest(TakeSnapshotController.class)
public class TakeSnapshotControllerTest {
From 684e74ff2c4ea08dd229ba1f1397076ca49f4b21 Mon Sep 17 00:00:00 2001
From: georgweiss
Date: Fri, 23 Jan 2026 13:27:21 +0100
Subject: [PATCH 2/4] Refactoring a bit to simplify web socket client setup
(logbook, save&restore)
---
.../olog/ui/LogbookSearchController.java | 44 +++++------
core/websocket/pom.xml | 7 ++
.../WebSocketClientService.java | 75 +++++++++++++++----
.../WebSocketClientServiceTest.java | 49 ++++++++++++
4 files changed, 136 insertions(+), 39 deletions(-)
create mode 100644 core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
index 1f5a4ac0ee..12cd051fc3 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
@@ -20,7 +20,6 @@
import org.phoebus.logbook.olog.ui.websocket.WebSocketMessage;
import org.phoebus.olog.es.api.Preferences;
-import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -57,8 +56,6 @@ public abstract class LogbookSearchController implements WebSocketMessageHandler
new SimpleObjectProperty<>(ConnectivityMode.NOT_CONNECTED);
protected WebSocketClientService webSocketClientService;
- private final String webSocketConnectUrl;
- private final String subscriptionEndpoint;
protected final CountDownLatch connectivityCheckerCountDownLatch = new CountDownLatch(1);
@SuppressWarnings("unused")
@@ -69,36 +66,29 @@ public abstract class LogbookSearchController implements WebSocketMessageHandler
@FXML
private GridPane viewSearchPane;
- public LogbookSearchController() {
- String baseUrl = Preferences.olog_url;
- URI uri = URI.create(baseUrl);
- String scheme = uri.getScheme();
- String host = uri.getHost();
- int port = uri.getPort();
- String path = uri.getPath();
- if (path.endsWith("/")) {
- path = path.substring(0, path.length() - 1);
- }
- String webSocketScheme = scheme.toLowerCase().startsWith("https") ? "wss" : "ws";
- this.webSocketConnectUrl = webSocketScheme + "://" + host + (port > -1 ? (":" + port) : "") + path + "/web-socket";
- this.subscriptionEndpoint = path + "/web-socket/messages";
- }
-
/**
* Determines how the client may connect to the remote service. The service info endpoint is called to establish
* availability of the service. If available, then a single web socket connection is attempted to determine
* if the service supports web sockets.
+ *
* @param consumer {@link Consumer} called when the connectivity mode has been determined.
*/
- protected void determineConnectivity(Consumer consumer){
-
+ protected void determineConnectivity(Consumer consumer) {
+ String webSocketConnectUrl;
+ try {
+ webSocketConnectUrl = WebSocketClientService.getWebsocketConnectUrl(Preferences.olog_url);
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to determine websocket connect URL", e);
+ consumer.accept(ConnectivityMode.NOT_CONNECTED);
+ return;
+ }
// Try to determine the connection mode: is the remote service available at all?
// If so, does it accept web socket connections?
JobManager.schedule("Connection mode probe", monitor -> {
ConnectivityMode connectivityMode = ConnectivityMode.NOT_CONNECTED;
String serviceInfo = client.serviceInfo();
if (serviceInfo != null && !serviceInfo.isEmpty()) { // service online, check web socket availability
- if (WebSocketClientService.checkAvailability(this.webSocketConnectUrl)) {
+ if (WebSocketClientService.checkAvailability(webSocketConnectUrl)) {
connectivityMode = ConnectivityMode.WEB_SOCKETS_SUPPORTED;
} else {
connectivityMode = ConnectivityMode.HTTP_ONLY;
@@ -184,8 +174,7 @@ public void shutdown() {
Logger.getLogger(LogbookSearchController.class.getName()).log(Level.INFO, "Shutting down web socket");
webSocketClientService.removeWebSocketMessageHandler(this);
webSocketClientService.shutdown();
- }
- else if(connectivityModeObjectProperty.get().equals(ConnectivityMode.HTTP_ONLY)){
+ } else if (connectivityModeObjectProperty.get().equals(ConnectivityMode.HTTP_ONLY)) {
cancelPeriodSearch();
}
}
@@ -193,6 +182,13 @@ else if(connectivityModeObjectProperty.get().equals(ConnectivityMode.HTTP_ONLY))
protected abstract void search();
protected void connectWebSocket() {
+ String webSocketConnectUrl;
+ try {
+ webSocketConnectUrl = WebSocketClientService.getWebsocketConnectUrl(Preferences.olog_url);
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to get websocket connection parameters", e);
+ return;
+ }
webSocketClientService = new WebSocketClientService(() -> {
logger.log(Level.INFO, "Connected to web socket on " + webSocketConnectUrl);
webSocketConnected.setValue(true);
@@ -204,7 +200,7 @@ protected void connectWebSocket() {
webSocketConnected.set(false);
viewSearchPane.visibleProperty().set(false);
errorPane.visibleProperty().set(true);
- }, webSocketConnectUrl, subscriptionEndpoint, null);
+ }, webSocketConnectUrl);
webSocketClientService.addWebSocketMessageHandler(this);
webSocketClientService.connect();
}
diff --git a/core/websocket/pom.xml b/core/websocket/pom.xml
index ab45ff67b8..2cddf9f45a 100644
--- a/core/websocket/pom.xml
+++ b/core/websocket/pom.xml
@@ -28,6 +28,13 @@
spring-messaging
${spring.framework.version}
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.version}
+ test
+
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java b/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
index 5b83cb5bb6..826dd8b241 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
+++ b/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
@@ -5,6 +5,7 @@
package org.phoebus.core.websocket.springframework;
import org.phoebus.core.websocket.WebSocketMessageHandler;
+import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.simp.stomp.ConnectionLostException;
@@ -19,6 +20,10 @@
import org.springframework.web.socket.messaging.WebSocketStompClient;
import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,8 +47,8 @@
*
* Since web socket URL paths are currently hard coded, a remote peer (e.g. Spring Framework STOMP web socket service) must:
*
- * - Publish a connect URL like ws(s)://host:port/path/web-socket, where path is optional.
- * - Publish a topic named /path/web-socket/messages, where path is optional.
+ * - Publish a connect URL like
ws(s)://host:port/path/web-socket, where path is optional.
+ * - Publish a topic named
/path/web-socket/messages, where path is optional.
*
*
*
@@ -76,24 +81,31 @@ public class WebSocketClientService {
/**
* Constructor if connect/disconnect callbacks are not needed.
+ *
+ * @param connectUrl URL to the service web socket, e.g. ws://localhost:8080/Olog/web.socket.
+ * The subscription and echo endpoints are computed from the connectUrl.
*/
@SuppressWarnings("unused")
- public WebSocketClientService(String connectUrl, String subscriptionEndpoint, String echoEndpoint) {
+ public WebSocketClientService(@NonNull String connectUrl) {
this.connectUrl = connectUrl;
- this.subscriptionEndpoint = subscriptionEndpoint;
- this.echoEndpoint = echoEndpoint;
+ URI uri = URI.create(connectUrl);
+ String path = uri.getPath();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ this.subscriptionEndpoint = path + "/messages";
+ this.echoEndpoint = path + "/echo";
}
/**
- * @param connectCallback The non-null method called when connection to the remote web socket has been successfully established.
- * @param disconnectCallback The non-null method called when connection to the remote web socket has been lost, e.g.
- * remote peer has been shut down.
- * @param connectUrl URL to the service web socket, e.g. ws://localhost:8080/Olog/web.socket
- * @param subscriptionEndpoint E.g. /Olog/web-socket/messages
- * @param echoEndpoint E.g. /Olog/web-socket/echo. May be null if client has no need for echo messages.
+ * @param connectCallback The non-null method called when connection to the remote web socket has been successfully established.
+ * @param disconnectCallback The non-null method called when connection to the remote web socket has been lost, e.g.
+ * remote peer has been shut down.
+ * @param connectUrl URL to the service web socket, e.g. ws://localhost:8080/Olog/web.socket.
+ * The subscription and echo endpoints are computed from the connectUrl.
*/
- public WebSocketClientService(Runnable connectCallback, Runnable disconnectCallback, String connectUrl, String subscriptionEndpoint, String echoEndpoint) {
- this(connectUrl, subscriptionEndpoint, echoEndpoint);
+ public WebSocketClientService(Runnable connectCallback, Runnable disconnectCallback, @NonNull String connectUrl) {
+ this(connectUrl);
this.connectCallback = connectCallback;
this.disconnectCallback = disconnectCallback;
}
@@ -155,9 +167,9 @@ public void connect() {
logger.log(Level.INFO, "Attempting web socket connection to " + connectUrl);
new Thread(() -> {
while (true) {
- try{
+ try {
synchronized (WebSocketClientService.this) {
- if(attemptReconnect.get()) {
+ if (attemptReconnect.get()) {
stompSession = stompClient.connect(connectUrl, sessionHandler).get();
stompSession.subscribe(this.subscriptionEndpoint, new StompFrameHandler() {
@Override
@@ -284,4 +296,37 @@ public Type getPayloadType(StompHeaders headers) {
}
return false;
}
+
+ /**
+ * Determines the web socket URL from the REST URL. This is a convenience method for clients connecting
+ * to a standard http(s) REST API, but that also need to maintain a client connecting to the
+ * same service over web sockets.
+ *
+ * @param restUrl The URL clients use for REST API calls to some service.
+ * @return The web socket connection URL derived from the REST URL.
+ * @throws Exception if restUrl is invalid or cannot be parsed as a valid URI (e.g. due to
+ * non-URL encoded chars like space).
+ */
+ public static String getWebsocketConnectUrl(String restUrl) throws Exception {
+ URI uri;
+ try {
+ URL url = new URL(restUrl);
+ uri = url.toURI();
+ } catch (MalformedURLException e) {
+ logger.log(Level.WARNING, "Invalid REST url: " + restUrl, e);
+ throw new Exception(e);
+ } catch (URISyntaxException e) {
+ logger.log(Level.WARNING, "REST url " + restUrl + " cannot be parsed as URI", e);
+ throw new Exception(e);
+ }
+ String scheme = uri.getScheme();
+ String host = uri.getHost();
+ int port = uri.getPort();
+ String path = uri.getPath();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ String webSocketScheme = scheme.toLowerCase().startsWith("https") ? "wss" : "ws";
+ return webSocketScheme + "://" + host + (port > -1 ? (":" + port) : "") + path + "/web-socket";
+ }
}
diff --git a/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java b/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
new file mode 100644
index 0000000000..d91609d98e
--- /dev/null
+++ b/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.core.websocket.springframework;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class WebSocketClientServiceTest {
+
+ @Test
+ public void testGetWebsocketConnectionParameters_1() throws Exception {
+ String restUrl = "http://localhost:8080";
+ assertEquals("ws://localhost:8080/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+
+ @Test
+ public void testGetWebsocketConnectionParameters_2() throws Exception {
+ String restUrl = "https://localhost:8080";
+ assertEquals("wss://localhost:8080/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+
+ @Test
+ public void testGetWebsocketConnectionParameters_3() throws Exception {
+ String restUrl = "https://localhost:8080/path-element";
+ assertEquals("wss://localhost:8080/path-element/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+
+ @Test
+ public void testGetWebsocketConnectionParameters_4() throws Exception {
+ String restUrl = "https://localhost/path-element";
+ assertEquals("wss://localhost/path-element/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+
+ @Test
+ public void testGetWebsocketConnectionParameters_5(){
+ String restUrl = "https://localhost:8080/path element";
+ assertThrows(Exception.class, () -> WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+
+ @Test
+ public void testGetWebsocketConnectionParameters_7(){
+ String restUrl = "invalid";
+ assertThrows(Exception.class, () -> WebSocketClientService.getWebsocketConnectUrl(restUrl));
+ }
+}
From f4af25286b41ec430ee8c5a0b18ac578562c4ba1 Mon Sep 17 00:00:00 2001
From: georgweiss
Date: Sun, 1 Feb 2026 18:42:47 +0100
Subject: [PATCH 3/4] Common web socket setup between Olog and save&restore
---
.../phoebus/olog/es/api/OlogHttpClient.java | 35 +--
app/logbook/olog/ui/pom.xml | 6 +
.../ui/LogEntryCalenderViewController.java | 1 +
.../olog/ui/LogEntryTableViewController.java | 1 +
.../olog/ui/LogbookSearchController.java | 39 +--
...ssageType.java => LogbookMessageType.java} | 7 +-
.../LogbookWebSocketMessageDeserializer.java | 54 +++++
.../olog/ui/websocket/WebSocketMessage.java | 22 --
...gbookWebSocketMessageDeserializerTest.java | 46 ++++
.../src/test/resources/websocketexample1.json | 3 +
.../src/test/resources/websocketexample2.json | 4 +
.../src/test/resources/websocketexample3.json | 3 +
.../ui/SaveAndRestoreBaseController.java | 12 +-
.../ui/SaveAndRestoreController.java | 53 ++--
.../ui/SaveAndRestoreService.java | 97 +++++++-
.../saveandrestore/ui/SaveAndRestoreTab.java | 5 +-
.../ui/WebSocketClientService.java | 90 -------
.../ui/WebSocketMessageHandler.java | 12 -
.../ConfigurationController.java | 21 +-
.../ui/configuration/ConfigurationTab.java | 11 +-
.../search/SearchAndFilterViewController.java | 17 +-
.../SearchResultTableViewController.java | 17 +-
.../snapshot/CompositeSnapshotController.java | 21 +-
.../ui/snapshot/CompositeSnapshotTab.java | 12 +-
.../ui/snapshot/SnapshotController.java | 44 ++--
.../ui/snapshot/SnapshotTab.java | 11 +-
app/save-and-restore/model/pom.xml | 6 +
...pe.java => SaveAndRestoreMessageType.java} | 4 +-
.../SaveAndRestoreWebSocketMessage.java | 13 -
...ndRestoreWebSocketMessageDeserializer.java | 62 +++++
...SaveAndRestoreWebSocketMessageHandler.java | 15 ++
.../websocket/WebMessageDeserializer.java | 65 -----
...storeWebSocketMessageDeserializerTest.java | 24 +-
.../phoebus/core/websocket/MessageType.java | 8 +
.../core/websocket/WebSocketClient.java | 228 ------------------
.../core/websocket/WebSocketMessage.java | 27 +--
.../websocket/WebSocketMessageHandler.java | 3 +
.../WebSocketClientService.java | 55 +----
.../WebSocketClientServiceTest.java | 49 ----
services/save-and-restore/pom.xml | 1 -
.../web/config/WebConfiguration.java | 11 +
.../CompositeSnapshotController.java | 8 +-
.../controllers/ConfigurationController.java | 8 +-
.../web/controllers/FilterController.java | 8 +-
.../web/controllers/NodeController.java | 10 +-
.../web/controllers/SnapshotController.java | 8 +-
.../web/controllers/StructureController.java | 10 +-
.../web/controllers/TagController.java | 8 +-
.../websocket/WebSocketConfig.java | 8 +-
.../websocket/WebSocketService.java | 15 +-
.../CompositeSnapshotControllerTest.java | 22 +-
.../ConfigurationControllerTest.java | 20 +-
.../web/controllers/FilterControllerTest.java | 24 +-
.../web/controllers/NodeControllerTest.java | 20 +-
.../controllers/SnapshotControllerTest.java | 28 +--
.../controllers/StructureControllerTest.java | 18 +-
56 files changed, 624 insertions(+), 806 deletions(-)
rename app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/{MessageType.java => LogbookMessageType.java} (84%)
create mode 100644 app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
delete mode 100644 app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/WebSocketMessage.java
create mode 100644 app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
create mode 100644 app/logbook/olog/ui/src/test/resources/websocketexample1.json
create mode 100644 app/logbook/olog/ui/src/test/resources/websocketexample2.json
create mode 100644 app/logbook/olog/ui/src/test/resources/websocketexample3.json
delete mode 100644 app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketClientService.java
delete mode 100644 app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketMessageHandler.java
rename app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/{MessageType.java => SaveAndRestoreMessageType.java} (74%)
delete mode 100644 app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessage.java
create mode 100644 app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
create mode 100644 app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
delete mode 100644 app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/WebMessageDeserializer.java
create mode 100644 core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java
delete mode 100644 core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketClient.java
rename services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java => core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java (56%)
delete mode 100644 core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
diff --git a/app/logbook/olog/client-es/src/main/java/org/phoebus/olog/es/api/OlogHttpClient.java b/app/logbook/olog/client-es/src/main/java/org/phoebus/olog/es/api/OlogHttpClient.java
index 308d4f2c13..c29ebf886f 100644
--- a/app/logbook/olog/client-es/src/main/java/org/phoebus/olog/es/api/OlogHttpClient.java
+++ b/app/logbook/olog/client-es/src/main/java/org/phoebus/olog/es/api/OlogHttpClient.java
@@ -67,6 +67,11 @@ public class OlogHttpClient implements LogClient {
private static final String OLOG_CLIENT_INFO_HEADER = "X-Olog-Client-Info";
private static final String CLIENT_INFO =
"CS Studio " + Messages.AppVersion + " on " + System.getProperty("os.name");
+ /**
+ * Each endpoint needs this as by default the service's context path is /, but all
+ * API endpoints are prefixed with Olog.
+ */
+ public static final String OLOG_PREFIX = "/Olog";
private static final Logger LOGGER = Logger.getLogger(OlogHttpClient.class.getName());
private final List changeHandlers = new ArrayList<>();
@@ -182,7 +187,7 @@ private LogEntry save(LogEntry log, LogEntry inReplyTo) throws LogbookException
}
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logs/multipart?" + QueryParamsHelper.mapToQueryParams(queryParams)))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logs/multipart?" + QueryParamsHelper.mapToQueryParams(queryParams)))
.header("Content-Type", httpRequestMultipartBody.getContentType())
.header(OLOG_CLIENT_INFO_HEADER, CLIENT_INFO)
.header("Authorization", basicAuthenticationHeader)
@@ -225,7 +230,7 @@ public LogEntry getLog(Long logId) {
@Override
public LogEntry findLogById(Long logId) {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logs/" + logId))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logs/" + logId))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
.build();
@@ -252,7 +257,7 @@ public List findLogs(Map map) {
*/
private SearchResult findLogs(MultivaluedMap searchParams) throws RuntimeException {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url +
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX +
"/logs/search?" + QueryParamsHelper.mapToQueryParams(searchParams)))
.header(OLOG_CLIENT_INFO_HEADER, CLIENT_INFO)
.header("Content-Type", CONTENT_TYPE_JSON)
@@ -311,7 +316,7 @@ public LogEntry update(LogEntry logEntry) {
try {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logs/" + logEntry.getId() + "?markup=commonmark"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logs/" + logEntry.getId() + "?markup=commonmark"))
.header("Content-Type", CONTENT_TYPE_JSON)
.header("Authorization", basicAuthenticationHeader)
.POST(HttpRequest.BodyPublishers.ofString(OlogObjectMappers.logEntrySerializer.writeValueAsString(logEntry)))
@@ -345,7 +350,7 @@ public SearchResult search(Map map) {
public void groupLogEntries(List logEntryIds) throws LogbookException {
try {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logs/group"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logs/group"))
.header("Content-Type", CONTENT_TYPE_JSON)
.header("Authorization", basicAuthenticationHeader)
.POST(HttpRequest.BodyPublishers.ofString(OlogObjectMappers.logEntrySerializer.writeValueAsString(logEntryIds)))
@@ -371,7 +376,7 @@ public void groupLogEntries(List logEntryIds) throws LogbookException {
* @return An {@link AuthenticationStatus} to indicate the outcome of the login attempt.
*/
public AuthenticationStatus authenticate(String userName, String password) {
- String stringBuilder = Preferences.olog_url +
+ String stringBuilder = Preferences.olog_url + OLOG_PREFIX +
"/login";
try {
HttpRequest request = HttpRequest.newBuilder()
@@ -400,7 +405,7 @@ public AuthenticationStatus authenticate(String userName, String password) {
@Override
public String serviceInfo() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
.build();
@@ -428,7 +433,7 @@ public Collection listLogs() {
@Override
public Collection listLogbooks() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logbooks"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logbooks"))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
.build();
@@ -445,7 +450,7 @@ public Collection listLogbooks() {
@Override
public Collection listTags() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/tags"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/tags"))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
.build();
@@ -462,7 +467,7 @@ public Collection listTags() {
@Override
public Collection listProperties() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/properties"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/properties"))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
.build();
@@ -481,7 +486,7 @@ public Collection listProperties() {
public InputStream getAttachment(Long logId, String attachmentName) {
try {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/logs/attachments/" + logId + "/" +
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/logs/attachments/" + logId + "/" +
URLEncoder.encode(attachmentName, StandardCharsets.UTF_8).replace("+", "%20"))) // + char does not work in path element!
.GET()
.build();
@@ -506,7 +511,7 @@ public InputStream getAttachment(Long logId, String attachmentName) {
public SearchResult getArchivedEntries(long id) {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url +
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX +
"/logs/archived/" + id))
.header(OLOG_CLIENT_INFO_HEADER, CLIENT_INFO)
.header("Content-Type", CONTENT_TYPE_JSON)
@@ -527,7 +532,7 @@ public SearchResult getArchivedEntries(long id) {
public Collection getTemplates() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url +
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX +
"/templates"))
.header("Content-Type", CONTENT_TYPE_JSON)
.GET()
@@ -554,7 +559,7 @@ public LogTemplate saveTemplate(LogTemplate template) throws LogbookException {
try {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/templates"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/templates"))
.header("Content-Type", CONTENT_TYPE_JSON)
.header("Authorization", basicAuthenticationHeader)
.PUT(HttpRequest.BodyPublishers.ofString(OlogObjectMappers.logEntrySerializer.writeValueAsString(template)))
@@ -575,7 +580,7 @@ public LogTemplate saveTemplate(LogTemplate template) throws LogbookException {
@Override
public Collection listLevels() {
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(Preferences.olog_url + "/levels"))
+ .uri(URI.create(Preferences.olog_url + OLOG_PREFIX + "/levels"))
.GET()
.build();
diff --git a/app/logbook/olog/ui/pom.xml b/app/logbook/olog/ui/pom.xml
index 9b26eb88b8..d2ecdcf8fa 100644
--- a/app/logbook/olog/ui/pom.xml
+++ b/app/logbook/olog/ui/pom.xml
@@ -60,6 +60,12 @@
9.0-r1
+
+ javax.json
+ javax.json-api
+ 1.1.2
+
+
com.atlassian.commonmark
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCalenderViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCalenderViewController.java
index 89d64c8815..924ef05bd6 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCalenderViewController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCalenderViewController.java
@@ -99,6 +99,7 @@ public LogEntryCalenderViewController(LogClient logClient, OlogQueryManager olog
@FXML
public void initialize() {
+ super.initialize();
advancedSearchViewController.setSearchCallback(this::search);
configureComboBox();
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
index 4a058ad0cb..bfd958f7e3 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
@@ -176,6 +176,7 @@ protected void setGoBackAndGoForwardActions(LogEntryTable.GoBackAndGoForwardActi
@FXML
public void initialize() {
+ super.initialize();
logEntryDisplayController.setLogEntryTableViewController(this);
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
index 12cd051fc3..1122d6d37a 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
@@ -1,7 +1,9 @@
package org.phoebus.logbook.olog.ui;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
@@ -9,6 +11,7 @@
import javafx.fxml.FXML;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.core.websocket.springframework.WebSocketClientService;
import org.phoebus.framework.jobs.Job;
@@ -16,8 +19,8 @@
import org.phoebus.logbook.LogClient;
import org.phoebus.logbook.LogEntry;
import org.phoebus.logbook.SearchResult;
-import org.phoebus.logbook.olog.ui.websocket.MessageType;
-import org.phoebus.logbook.olog.ui.websocket.WebSocketMessage;
+import org.phoebus.logbook.olog.ui.websocket.LogbookMessageType;
+import org.phoebus.logbook.olog.ui.websocket.LogbookWebSocketMessageDeserializer;
import org.phoebus.olog.es.api.Preferences;
import java.util.List;
@@ -57,6 +60,7 @@ public abstract class LogbookSearchController implements WebSocketMessageHandler
protected WebSocketClientService webSocketClientService;
protected final CountDownLatch connectivityCheckerCountDownLatch = new CountDownLatch(1);
+ private String webSocketConnectUrl;
@SuppressWarnings("unused")
@FXML
@@ -66,6 +70,17 @@ public abstract class LogbookSearchController implements WebSocketMessageHandler
@FXML
private GridPane viewSearchPane;
+ @FXML
+ public void initialize() {
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(WebSocketMessage.class, new LogbookWebSocketMessageDeserializer(WebSocketMessage.class));
+ objectMapper.registerModule(module);
+ webSocketConnectUrl = Preferences.olog_url.trim().toLowerCase().startsWith("https://") ?
+ Preferences.olog_url.trim().replace("https", "wss") :
+ Preferences.olog_url.trim().replace("http", "ws");
+ webSocketConnectUrl += "/Olog/web-socket";
+ }
+
/**
* Determines how the client may connect to the remote service. The service info endpoint is called to establish
* availability of the service. If available, then a single web socket connection is attempted to determine
@@ -74,14 +89,6 @@ public abstract class LogbookSearchController implements WebSocketMessageHandler
* @param consumer {@link Consumer} called when the connectivity mode has been determined.
*/
protected void determineConnectivity(Consumer consumer) {
- String webSocketConnectUrl;
- try {
- webSocketConnectUrl = WebSocketClientService.getWebsocketConnectUrl(Preferences.olog_url);
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to determine websocket connect URL", e);
- consumer.accept(ConnectivityMode.NOT_CONNECTED);
- return;
- }
// Try to determine the connection mode: is the remote service available at all?
// If so, does it accept web socket connections?
JobManager.schedule("Connection mode probe", monitor -> {
@@ -182,13 +189,6 @@ public void shutdown() {
protected abstract void search();
protected void connectWebSocket() {
- String webSocketConnectUrl;
- try {
- webSocketConnectUrl = WebSocketClientService.getWebsocketConnectUrl(Preferences.olog_url);
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to get websocket connection parameters", e);
- return;
- }
webSocketClientService = new WebSocketClientService(() -> {
logger.log(Level.INFO, "Connected to web socket on " + webSocketConnectUrl);
webSocketConnected.setValue(true);
@@ -208,8 +208,9 @@ protected void connectWebSocket() {
@Override
public void handleWebSocketMessage(String message) {
try {
- WebSocketMessage webSocketMessage = objectMapper.readValue(message, WebSocketMessage.class);
- if (webSocketMessage.messageType().equals(MessageType.NEW_LOG_ENTRY)) {
+ WebSocketMessage> webSocketMessage = objectMapper.readValue(message, new TypeReference<>() {
+ });
+ if (webSocketMessage.messageType().equals(LogbookMessageType.NEW_LOG_ENTRY)) {
// Add a random sleep 0 - 5 seconds to avoid an avalanche of search requests on the service.
long randomSleepTime = Math.round(5000 * Math.random());
try {
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/MessageType.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
similarity index 84%
rename from app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/MessageType.java
rename to app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
index b7406c1ff1..2cdfbe3c86 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/MessageType.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
@@ -18,7 +18,12 @@
package org.phoebus.logbook.olog.ui.websocket;
-public enum MessageType {
+import org.phoebus.core.websocket.MessageType;
+
+/**
+ * Web socket message types particular to the logbook
+ */
+public enum LogbookMessageType implements MessageType {
NEW_LOG_ENTRY,
LOG_ENTRY_UPDATED,
SHOW_BANNER
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
new file mode 100644
index 0000000000..fea7585789
--- /dev/null
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.logbook.olog.ui.websocket;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.phoebus.core.websocket.WebSocketMessage;
+
+/**
+ * Custom JSON deserializer of {@link WebSocketMessage}s particular to the logbook app.
+ */
+public class LogbookWebSocketMessageDeserializer extends StdDeserializer> {
+
+ public LogbookWebSocketMessageDeserializer(Class> clazz) {
+ super(clazz);
+ }
+
+ /**
+ * Deserializes a save-and-restore {@link WebSocketMessage}.
+ *
+ * @param jsonParser Parsed used for reading JSON content
+ * @param context Context that can be used to access information about
+ * this deserialization activity.
+ * @return A {@link WebSocketMessage} object, or null if deserialization fails, e.g. due to
+ * unknown/invalid {@link org.phoebus.core.websocket.MessageType} or null payload.
+ */
+ @Override
+ public WebSocketMessage> deserialize(JsonParser jsonParser, DeserializationContext context) {
+ try {
+ JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser);
+ LogbookMessageType logbookMessageType = LogbookMessageType.valueOf(rootNode.get("messageType").asText());
+ JsonNode payload = rootNode.get("payload");
+ switch (logbookMessageType) {
+ case NEW_LOG_ENTRY -> {
+ return new WebSocketMessage<>(logbookMessageType, null);
+ }
+ case LOG_ENTRY_UPDATED -> {
+ return new WebSocketMessage<>(logbookMessageType, payload.textValue());
+ }
+ case SHOW_BANNER -> {
+ throw new RuntimeException("SHOW_BANNER not yet implemented");
+ }
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return null;
+ }
+
+}
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/WebSocketMessage.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/WebSocketMessage.java
deleted file mode 100644
index 299304bcf8..0000000000
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/WebSocketMessage.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 European Spallation Source ERIC.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package org.phoebus.logbook.olog.ui.websocket;
-
-public record WebSocketMessage(MessageType messageType, String payload) {
-}
diff --git a/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java b/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
new file mode 100644
index 0000000000..875e6044e3
--- /dev/null
+++ b/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.logbook.olog.ui.websocket;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.junit.jupiter.api.Test;
+import org.phoebus.core.websocket.WebSocketMessage;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LogbookWebSocketMessageDeserializerTest {
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ public LogbookWebSocketMessageDeserializerTest() {
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(WebSocketMessage.class, new LogbookWebSocketMessageDeserializer(WebSocketMessage.class));
+ objectMapper.registerModule(module);
+ }
+
+ @Test
+ public void testDeserialize_1() throws Exception{
+ WebSocketMessage webSocketMessage = objectMapper.readValue(getClass().getResourceAsStream("/websocketexample1.json"), new TypeReference<>() {
+ });
+ assertEquals(LogbookMessageType.NEW_LOG_ENTRY, webSocketMessage.messageType());
+ }
+
+ @Test
+ public void testDeserialize_2() throws Exception{
+ WebSocketMessage webSocketMessage = objectMapper.readValue(getClass().getResourceAsStream("/websocketexample2.json"), new TypeReference<>() {
+ });
+ assertEquals(LogbookMessageType.LOG_ENTRY_UPDATED, webSocketMessage.messageType());
+ assertEquals("logEntryId", webSocketMessage.payload());
+ }
+
+ @Test
+ public void testDeserialize_3() throws Exception{
+ WebSocketMessage webSocketMessage = objectMapper.readValue(getClass().getResourceAsStream("/websocketexample3.json"), new TypeReference<>() {
+ });
+ assertNull(webSocketMessage);
+ }
+}
diff --git a/app/logbook/olog/ui/src/test/resources/websocketexample1.json b/app/logbook/olog/ui/src/test/resources/websocketexample1.json
new file mode 100644
index 0000000000..5eeb172715
--- /dev/null
+++ b/app/logbook/olog/ui/src/test/resources/websocketexample1.json
@@ -0,0 +1,3 @@
+{
+ "messageType": "NEW_LOG_ENTRY"
+}
\ No newline at end of file
diff --git a/app/logbook/olog/ui/src/test/resources/websocketexample2.json b/app/logbook/olog/ui/src/test/resources/websocketexample2.json
new file mode 100644
index 0000000000..1cb8559541
--- /dev/null
+++ b/app/logbook/olog/ui/src/test/resources/websocketexample2.json
@@ -0,0 +1,4 @@
+{
+ "messageType": "LOG_ENTRY_UPDATED",
+ "payload" : "logEntryId"
+}
\ No newline at end of file
diff --git a/app/logbook/olog/ui/src/test/resources/websocketexample3.json b/app/logbook/olog/ui/src/test/resources/websocketexample3.json
new file mode 100644
index 0000000000..0a0274351a
--- /dev/null
+++ b/app/logbook/olog/ui/src/test/resources/websocketexample3.json
@@ -0,0 +1,3 @@
+{
+ "messageType": "INVALID"
+}
\ No newline at end of file
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
index e1e55911f0..b5daadd145 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
@@ -21,7 +21,7 @@
import javafx.beans.property.SimpleStringProperty;
import org.phoebus.applications.saveandrestore.authentication.SaveAndRestoreAuthenticationScope;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.security.store.SecureStore;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
@@ -33,11 +33,11 @@
public abstract class SaveAndRestoreBaseController {
protected final SimpleStringProperty userIdentity = new SimpleStringProperty();
- protected final WebSocketClientService webSocketClientService;
+ //protected final WebSocketClientService webSocketClientService;
protected final SaveAndRestoreService saveAndRestoreService;
public SaveAndRestoreBaseController() {
- this.webSocketClientService = WebSocketClientService.getInstance();
+ //this.webSocketClientService = WebSocketClientService.getInstance();
this.saveAndRestoreService = SaveAndRestoreService.getInstance();
try {
SecureStore secureStore = new SecureStore();
@@ -70,10 +70,10 @@ public SimpleStringProperty getUserIdentity() {
}
/**
- * Default no-op implementation of a handler for {@link SaveAndRestoreWebSocketMessage}s.
- * @param webSocketMessage See {@link SaveAndRestoreWebSocketMessage}
+ * Default no-op implementation of a handler for {@link WebSocketMessage}s.
+ * @param webSocketMessage See {@link WebSocketMessage}
*/
- protected void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> webSocketMessage){
+ protected void handleWebSocketMessage(WebSocketMessage> webSocketMessage){
}
/**
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
index b658453f9f..8a37b08313 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
@@ -80,7 +80,8 @@
import org.phoebus.applications.saveandrestore.model.search.SearchQueryUtil;
import org.phoebus.applications.saveandrestore.model.search.SearchQueryUtil.Keys;
import org.phoebus.applications.saveandrestore.model.search.SearchResult;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.configuration.ConfigurationTab;
import org.phoebus.applications.saveandrestore.ui.contextmenu.CopyUniqueIdToClipboardMenuItem;
import org.phoebus.applications.saveandrestore.ui.contextmenu.CreateSnapshotMenuItem;
@@ -101,6 +102,7 @@
import org.phoebus.applications.saveandrestore.ui.snapshot.SnapshotTab;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagUtil;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagWidget;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.framework.selection.SelectionService;
@@ -132,7 +134,6 @@
import java.util.ServiceLoader;
import java.util.Stack;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -141,7 +142,7 @@
* Main controller for the save and restore UI.
*/
public class SaveAndRestoreController extends SaveAndRestoreBaseController
- implements Initializable, WebSocketMessageHandler {
+ implements Initializable, SaveAndRestoreWebSocketMessageHandler {
@FXML
protected TreeView treeView;
@@ -252,7 +253,6 @@ public class SaveAndRestoreController extends SaveAndRestoreBaseController
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
-
// Tree items are first compared on type, then on name (case-insensitive).
treeNodeComparator = Comparator.comparing(TreeItem::getValue);
@@ -368,10 +368,10 @@ public Filter fromString(String s) {
treeView.visibleProperty().bind(serviceConnected);
errorPane.visibleProperty().bind(serviceConnected.not());
- webSocketClientService.addWebSocketMessageHandler(this);
- webSocketClientService.setConnectCallback(this::handleWebSocketConnected);
- webSocketClientService.setDisconnectCallback(this::handleWebSocketDisconnected);
- webSocketClientService.connect();
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
+ saveAndRestoreService.setConnectCallback(this::handleWebSocketConnected);
+ saveAndRestoreService.setDisconnectCallback(this::handleWebSocketDisconnected);
+ saveAndRestoreService.connectWebSocket();
}
@@ -861,7 +861,7 @@ public void locateNode(Stack nodeStack) {
while (!nodeStack.isEmpty()) {
Node currentNode = nodeStack.pop();
TreeItem currentTreeItem = recursiveSearch(currentNode.getUniqueId(), parentTreeItem);
- if(!currentTreeItem.isExpanded()){
+ if (!currentTreeItem.isExpanded()) {
expandTreeNode(currentTreeItem);
}
parentTreeItem = currentTreeItem;
@@ -942,9 +942,10 @@ public void saveLocalState() {
@Override
public void handleTabClosed() {
- tabPane.getTabs().forEach(t -> ((SaveAndRestoreTab)t).handleTabClosed());
+ tabPane.getTabs().forEach(t -> ((SaveAndRestoreTab) t).handleTabClosed());
saveLocalState();
- webSocketClientService.closeWebSocket();
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
+ saveAndRestoreService.closeWebSocket();
filterActivators.forEach(FilterActivator::stop);
}
@@ -1125,11 +1126,11 @@ public boolean matchesFilter(Node node) {
* the {@link Filter}, then the {@link TreeView} is updated based on the search result.
*
* @param filter {@link Filter} selected by user or through business logic. If null, then the
- * no filter {@link Filter} is applied.
+ * no filter {@link Filter} is applied.
*/
private void applyFilter(Filter filter) {
treeView.getSelectionModel().clearSelection();
- if(filter == null){
+ if (filter == null) {
return;
}
Map searchParams =
@@ -1176,7 +1177,7 @@ private void filterAddedOrUpdated(Filter filter) {
filtersComboBox.valueProperty().set(filter);
// If this is the current filter, update the tree view
if (filter.equals(filtersComboBox.getSelectionModel().getSelectedItem())) {
- currentFilterProperty.set(filter);
+ currentFilterProperty.set(filter);
}
}
}
@@ -1438,13 +1439,14 @@ private void addOptionalLoggingMenuItem() {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- switch (saveAndRestoreWebSocketMessage.messageType()) {
- case NODE_ADDED -> nodeAdded((String) saveAndRestoreWebSocketMessage.payload());
- case NODE_REMOVED -> nodeRemoved((String) saveAndRestoreWebSocketMessage.payload());
- case NODE_UPDATED -> nodeChanged((Node) saveAndRestoreWebSocketMessage.payload());
- case FILTER_ADDED_OR_UPDATED -> filterAddedOrUpdated((Filter) saveAndRestoreWebSocketMessage.payload());
- case FILTER_REMOVED -> filterRemoved((String) saveAndRestoreWebSocketMessage.payload());
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage> webSocketMessage) {
+ SaveAndRestoreMessageType messageType = (SaveAndRestoreMessageType) webSocketMessage.messageType();
+ switch (messageType) {
+ case NODE_ADDED -> nodeAdded((String) webSocketMessage.payload());
+ case NODE_REMOVED -> nodeRemoved((String) webSocketMessage.payload());
+ case NODE_UPDATED -> nodeChanged((Node) webSocketMessage.payload());
+ case FILTER_ADDED_OR_UPDATED -> filterAddedOrUpdated((Filter) webSocketMessage.payload());
+ case FILTER_REMOVED -> filterRemoved((String) webSocketMessage.payload());
}
}
@@ -1478,6 +1480,7 @@ public void activateFilter(String filterName) {
/**
* If auto {@link Filter} activation is enabled and the active filter matches filterName, then
* this method will switch to no filter but maintain auto activation.
+ *
* @param filterName Name/id of the de-activated filter.
*/
public void deactivateFilter(String filterName) {
@@ -1500,16 +1503,16 @@ private void handleWebSocketDisconnected() {
}
@Override
- public boolean doCloseCheck(){
- for(Tab tab : tabPane.getTabs()){
- if(!((SaveAndRestoreTab)tab).doCloseCheck()){
+ public boolean doCloseCheck() {
+ for (Tab tab : tabPane.getTabs()) {
+ if (!((SaveAndRestoreTab) tab).doCloseCheck()) {
return false;
}
}
return true;
}
- private void findReferences(){
+ private void findReferences() {
SearchAndFilterTab searchAndFilterTab = openSearchWindow();
searchAndFilterTab.getController().findReferencesForSnapshot(treeView.getSelectionModel().getSelectedItems().get(0).getValue().getUniqueId());
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
index 1203fe1d7f..d75e9c4d61 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
@@ -18,7 +18,12 @@
package org.phoebus.applications.saveandrestore.ui;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
import org.epics.vtype.VType;
+import org.phoebus.applications.saveandrestore.client.Preferences;
import org.phoebus.applications.saveandrestore.client.SaveAndRestoreClient;
import org.phoebus.applications.saveandrestore.client.SaveAndRestoreClientImpl;
import org.phoebus.applications.saveandrestore.model.CompositeSnapshot;
@@ -35,7 +40,12 @@
import org.phoebus.applications.saveandrestore.model.TagData;
import org.phoebus.applications.saveandrestore.model.search.Filter;
import org.phoebus.applications.saveandrestore.model.search.SearchResult;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageDeserializer;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.core.vtypes.VDisconnectedData;
+import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessageHandler;
+import org.phoebus.core.websocket.springframework.WebSocketClientService;
import org.phoebus.pv.PV;
import org.phoebus.pv.PVPool;
import org.phoebus.saveandrestore.util.VNoData;
@@ -45,6 +55,7 @@
import javax.ws.rs.core.MultivaluedMap;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -55,7 +66,12 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;
-public class SaveAndRestoreService {
+/**
+ * Single to class providing an API to interact with the remote Save-and-Restore service. It also
+ * manages web socket communication such that API clients may be notified when service dispatches
+ * messages, e.g. when data has changed.
+ */
+public class SaveAndRestoreService implements WebSocketMessageHandler {
private final ExecutorService executor;
@@ -64,10 +80,23 @@ public class SaveAndRestoreService {
private static SaveAndRestoreService instance;
private final SaveAndRestoreClient saveAndRestoreClient;
+ private final WebSocketClientService webSocketClientService;
+ private final List saveAndRestoreWebSocketMessageHandlers =
+ Collections.synchronizedList(new ArrayList<>());
+ private final ObjectMapper objectMapper = new ObjectMapper();
private SaveAndRestoreService() {
saveAndRestoreClient = new SaveAndRestoreClientImpl();
+ String webSocketConnectUrl = Preferences.jmasarServiceUrl.trim().toLowerCase().startsWith("https://") ?
+ Preferences.jmasarServiceUrl.trim().replace("https", "wss") :
+ Preferences.jmasarServiceUrl.trim().replace("http", "ws");
+ webSocketConnectUrl += "/web-socket";
+ webSocketClientService = new WebSocketClientService(webSocketConnectUrl);
+ webSocketClientService.addWebSocketMessageHandler(this);
executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+ SimpleModule simpleModule = new SimpleModule();
+ simpleModule.addDeserializer(WebSocketMessage.class, new SaveAndRestoreWebSocketMessageDeserializer(WebSocketMessage.class));
+ objectMapper.registerModule(simpleModule);
}
public static SaveAndRestoreService getInstance() {
@@ -421,4 +450,70 @@ private VType readFromArchiver(String pvName, Instant time) {
}
return pvValue == null ? VDisconnectedData.INSTANCE : pvValue;
}
+
+ /**
+ * Connects to the web socket
+ */
+ public void connectWebSocket() {
+ webSocketClientService.connect();
+ }
+
+ /**
+ * Registers a handler for {@link WebSocketMessage}s.
+ *
+ * @param saveAndRestoreWebSocketMessageHandler A {@link SaveAndRestoreWebSocketMessageHandler} instance.
+ */
+ public void addSaveAndRestoreWebSocketMessageHandler(SaveAndRestoreWebSocketMessageHandler saveAndRestoreWebSocketMessageHandler) {
+ saveAndRestoreWebSocketMessageHandlers.add(saveAndRestoreWebSocketMessageHandler);
+ }
+
+ /**
+ * Unregisters a handler for {@link WebSocketMessage}s
+ *
+ * @param saveAndRestoreWebSocketMessageHandler A {@link SaveAndRestoreWebSocketMessageHandler} instance.
+ */
+ public void removeSaveAndRestoreWebSocketMessageHandler(SaveAndRestoreWebSocketMessageHandler saveAndRestoreWebSocketMessageHandler) {
+ saveAndRestoreWebSocketMessageHandlers.remove(saveAndRestoreWebSocketMessageHandler);
+ }
+
+ /**
+ * @param callback A {@link Runnable} called when the web socket has been connected successfully.
+ */
+ public void setConnectCallback(Runnable callback) {
+ webSocketClientService.setConnectCallback(callback);
+ }
+
+ /**
+ * @param callback A {@link Runnable} called when the web socket has been disconnected for whatever reason,
+ * e.g. remote service becomes unavailable.
+ */
+ public void setDisconnectCallback(Runnable callback) {
+ webSocketClientService.setDisconnectCallback(callback);
+ }
+
+ /**
+ * Closes the web socket
+ */
+ public void closeWebSocket() {
+ webSocketClientService.removeWebSocketMessageHandler(this);
+ webSocketClientService.shutdown();
+ }
+
+ /**
+ * Handler for raw web socket messages.
+ *
+ * @param message A raw message as dispatched by the web socket peer.
+ */
+ @Override
+ public void handleWebSocketMessage(String message) {
+ try {
+ WebSocketMessage webSocketMessage = objectMapper.readValue(message, new TypeReference<>() {
+ });
+ saveAndRestoreWebSocketMessageHandlers.forEach(h ->
+ h.handleSaveAndRestoreWebSocketMessage(webSocketMessage));
+ } catch (Exception e) {
+ LOG.log(Level.WARNING, "Failed to deserialize web socket message " + message, e);
+ }
+ }
+
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
index 7edab3004d..887ad37aa6 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
@@ -24,8 +24,9 @@
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.image.ImageView;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.applications.saveandrestore.ui.snapshot.SnapshotTab;
+import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
import org.phoebus.ui.javafx.ImageCache;
@@ -81,7 +82,7 @@ public void secureStoreChanged(List validTokens) {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
+ public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketClientService.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketClientService.java
deleted file mode 100644
index 0deddbd10d..0000000000
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketClientService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- */
-
-package org.phoebus.applications.saveandrestore.ui;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import org.phoebus.applications.saveandrestore.client.Preferences;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
-import org.phoebus.applications.saveandrestore.model.websocket.WebMessageDeserializer;
-import org.phoebus.core.websocket.WebSocketClient;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class WebSocketClientService {
-
- private final List webSocketMessageHandlers = Collections.synchronizedList(new ArrayList<>());
-
- private static WebSocketClientService instance;
- private final WebSocketClient webSocketClient;
-
- private final ObjectMapper objectMapper;
-
- private WebSocketClientService() {
- String baseUrl = Preferences.jmasarServiceUrl;
- String schema = baseUrl.startsWith("https") ? "wss" : "ws";
- String webSocketUrl = schema + baseUrl.substring(baseUrl.indexOf("://")) + "/web-socket";
- URI webSocketUri = URI.create(webSocketUrl);
- webSocketClient = new WebSocketClient(webSocketUri, this::handleWebSocketMessage);
- objectMapper = new ObjectMapper();
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- objectMapper.registerModule(new JavaTimeModule());
- objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
- SimpleModule module = new SimpleModule();
- module.addDeserializer(SaveAndRestoreWebSocketMessage.class,
- new WebMessageDeserializer(SaveAndRestoreWebSocketMessage.class));
- objectMapper.registerModule(module);
- }
-
- public static WebSocketClientService getInstance() {
- if (instance == null) {
- instance = new WebSocketClientService();
- }
- return instance;
- }
-
- public void connect() {
- webSocketClient.connect();
- }
-
- public void closeWebSocket() {
- webSocketMessageHandlers.clear();
- webSocketClient.close("Application shutdown");
- }
-
- public void addWebSocketMessageHandler(WebSocketMessageHandler webSocketMessageHandler) {
- webSocketMessageHandlers.add(webSocketMessageHandler);
- }
-
- public void removeWebSocketMessageHandler(WebSocketMessageHandler webSocketMessageHandler) {
- webSocketMessageHandlers.remove(webSocketMessageHandler);
- }
-
- private void handleWebSocketMessage(CharSequence charSequence) {
- try {
- SaveAndRestoreWebSocketMessage saveAndRestoreWebSocketMessage =
- objectMapper.readValue(charSequence.toString(), SaveAndRestoreWebSocketMessage.class);
- webSocketMessageHandlers.forEach(w -> w.handleWebSocketMessage(saveAndRestoreWebSocketMessage));
-
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void setConnectCallback(Runnable connectCallback) {
- webSocketClient.setConnectCallback(connectCallback);
- }
-
- public void setDisconnectCallback(Runnable disconnectCallback) {
- webSocketClient.setDisconnectCallback(disconnectCallback);
- }
-}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketMessageHandler.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketMessageHandler.java
deleted file mode 100644
index 3e202fb660..0000000000
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/WebSocketMessageHandler.java
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- */
-
-package org.phoebus.applications.saveandrestore.ui;
-
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
-
-public interface WebSocketMessageHandler {
-
- void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage);
-}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
index 9e252b8738..ecb4fd96b8 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
@@ -59,11 +59,11 @@
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
import org.phoebus.core.types.ProcessVariable;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.selection.SelectionService;
import org.phoebus.ui.application.ContextMenuHelper;
@@ -83,7 +83,8 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;
-public class ConfigurationController extends SaveAndRestoreBaseController implements WebSocketMessageHandler {
+public class ConfigurationController extends SaveAndRestoreBaseController
+ implements SaveAndRestoreWebSocketMessageHandler {
@FXML
@SuppressWarnings("unused")
@@ -365,7 +366,7 @@ public void commitEdit(Double value) {
addPVsPane.disableProperty().bind(userIdentity.isNull());
- webSocketClientService.addWebSocketMessageHandler(this);
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
}
@FXML
@@ -534,14 +535,14 @@ public boolean doCloseCheck() {
}
@Override
- public void handleTabClosed(){
- webSocketClientService.removeWebSocketMessageHandler(this);
+ public void handleTabClosed() {
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_UPDATED)) {
- Node node = (Node) saveAndRestoreWebSocketMessage.payload();
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage> webSocketMessage) {
+ if (webSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_UPDATED)) {
+ Node node = (Node) webSocketMessage.payload();
if (tabIdProperty.get() != null && node.getUniqueId().equals(tabIdProperty.get())) {
loadConfiguration(node);
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
index dcbb9cb9ca..249f4138e6 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
@@ -23,11 +23,9 @@
import javafx.scene.image.ImageView;
import org.phoebus.applications.saveandrestore.Messages;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
+import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import java.util.ResourceBundle;
@@ -82,12 +80,15 @@ public void configureForNewConfiguration(Node parentNode) {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_REMOVED)) {
+ public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
+ /*
+ if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
if (getId() != null && nodeId.equals(getId())) {
Platform.runLater(() -> getTabPane().getTabs().remove(this));
}
}
+
+ */
}
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
index 2f4fd38bac..909352e85f 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
@@ -50,12 +50,13 @@
import org.phoebus.applications.saveandrestore.model.search.Filter;
import org.phoebus.applications.saveandrestore.model.search.SearchQueryUtil;
import org.phoebus.applications.saveandrestore.model.search.SearchQueryUtil.Keys;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.HelpViewer;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
import org.phoebus.ui.autocomplete.PVAutocompleteMenu;
@@ -79,7 +80,7 @@
import java.util.stream.Collectors;
public class SearchAndFilterViewController extends SaveAndRestoreBaseController
- implements Initializable, WebSocketMessageHandler {
+ implements Initializable, SaveAndRestoreWebSocketMessageHandler {
private final SaveAndRestoreController saveAndRestoreController;
@@ -380,7 +381,7 @@ public void initialize(URL url, ResourceBundle resourceBundle) {
loadFilters();
- webSocketClientService.addWebSocketMessageHandler(this);
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
progressIndicator.visibleProperty().bind(disableUi);
disableUi.addListener((observable, oldValue, newValue) -> mainUi.setDisable(newValue));
@@ -636,7 +637,7 @@ private void updatedQueryEditor() {
}
public void handleSaveAndFilterTabClosed() {
- webSocketClientService.removeWebSocketMessageHandler(this);
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
searchResultTableViewController.handleTabClosed();
}
@@ -647,15 +648,15 @@ public void secureStoreChanged(List validTokens) {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- switch (saveAndRestoreWebSocketMessage.messageType()) {
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage> webSocketMessage) {
+ switch ((SaveAndRestoreMessageType) webSocketMessage.messageType()) {
case FILTER_REMOVED, FILTER_ADDED_OR_UPDATED -> loadFilters();
}
}
@Override
public void handleTabClosed() {
- webSocketClientService.removeWebSocketMessageHandler(this);
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
}
@Override
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
index 661c564843..3515514b0b 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
@@ -41,18 +41,19 @@
import org.phoebus.applications.saveandrestore.model.search.Filter;
import org.phoebus.applications.saveandrestore.model.search.SearchQueryUtil;
import org.phoebus.applications.saveandrestore.model.search.SearchResult;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.RestoreMode;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.contextmenu.LoginMenuItem;
import org.phoebus.applications.saveandrestore.ui.contextmenu.RestoreFromClientMenuItem;
import org.phoebus.applications.saveandrestore.ui.contextmenu.RestoreFromServiceMenuItem;
import org.phoebus.applications.saveandrestore.ui.contextmenu.TagGoldenMenuItem;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagUtil;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagWidget;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.workbench.ApplicationService;
import org.phoebus.ui.dialog.DialogHelper;
@@ -80,7 +81,7 @@
* Controller for the search result table.
*/
public class SearchResultTableViewController extends SaveAndRestoreBaseController
- implements Initializable, WebSocketMessageHandler {
+ implements Initializable, SaveAndRestoreWebSocketMessageHandler {
@SuppressWarnings("unused")
@FXML
@@ -288,7 +289,7 @@ protected void updateItem(Node node, boolean empty) {
}
});
- webSocketClientService.addWebSocketMessageHandler(this);
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
}
private ImageView getImageView(Node node) {
@@ -413,19 +414,19 @@ public void loadFilter(String filterId) {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- switch (saveAndRestoreWebSocketMessage.messageType()) {
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage webSocketMessage) {
+ switch ((SaveAndRestoreMessageType) webSocketMessage.messageType()) {
case NODE_UPDATED, NODE_REMOVED, NODE_ADDED -> search();
}
}
@Override
public void handleTabClosed() {
- webSocketClientService.removeWebSocketMessageHandler(this);
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
}
@Override
- public boolean doCloseCheck(){
+ public boolean doCloseCheck() {
return true;
}
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
index 733f6fef2c..edb75f32c7 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
@@ -59,12 +59,12 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Tag;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.ui.dialog.DialogHelper;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
@@ -82,7 +82,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
-public class CompositeSnapshotController extends SaveAndRestoreBaseController implements WebSocketMessageHandler {
+public class CompositeSnapshotController extends SaveAndRestoreBaseController
+ implements SaveAndRestoreWebSocketMessageHandler {
@SuppressWarnings("unused")
@FXML
@@ -347,7 +348,7 @@ public void updateItem(Node item, boolean empty) {
}
});
- webSocketClientService.addWebSocketMessageHandler(this);
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
}
@FXML
@@ -443,8 +444,8 @@ public boolean doCloseCheck() {
}
@Override
- public void handleTabClosed(){
- webSocketClientService.removeWebSocketMessageHandler(this);
+ public void handleTabClosed() {
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
}
/**
@@ -554,9 +555,9 @@ private void removeListeners() {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_UPDATED)) {
- Node node = (Node) saveAndRestoreWebSocketMessage.payload();
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage webSocketMessage) {
+ if (webSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_UPDATED)) {
+ Node node = (Node) webSocketMessage.payload();
if (tabIdProperty.get() != null && node.getUniqueId().equals(tabIdProperty.get())) {
loadCompositeSnapshot(node);
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
index 4068204013..cc9aeed295 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
@@ -24,12 +24,11 @@
import javafx.scene.image.ImageView;
import org.phoebus.applications.saveandrestore.Messages;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
+import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
@@ -113,13 +112,16 @@ public void addToCompositeSnapshot(List snapshotNodes) {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_REMOVED)) {
+ public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
+ /*
+ if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
if (getId() != null && nodeId.equals(getId())) {
Platform.runLater(() -> getTabPane().getTabs().remove(this));
}
}
+
+ */
}
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
index 0e5781836f..5f84935e4c 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
@@ -76,17 +76,17 @@
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.applications.saveandrestore.model.Tag;
import org.phoebus.applications.saveandrestore.model.event.SaveAndRestoreEventReceiver;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.RestoreMode;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
import org.phoebus.applications.saveandrestore.ui.SnapshotMode;
import org.phoebus.applications.saveandrestore.ui.VTypePair;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
import org.phoebus.core.types.TimeStampedProcessVariable;
import org.phoebus.core.vtypes.VDisconnectedData;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.selection.SelectionService;
import org.phoebus.saveandrestore.util.SnapshotUtil;
@@ -124,7 +124,8 @@
* Once the snapshot has been saved, this controller calls the {@link SnapshotTab} API to load
* the view associated with restore actions.
*/
-public class SnapshotController extends SaveAndRestoreBaseController implements WebSocketMessageHandler {
+public class SnapshotController extends SaveAndRestoreBaseController
+ implements SaveAndRestoreWebSocketMessageHandler {
@SuppressWarnings("unused")
@@ -271,9 +272,11 @@ public class SnapshotController extends SaveAndRestoreBaseController implements
@FXML
protected TableColumn baseSnapshotColumn;
+ @SuppressWarnings("unused")
@FXML
private TableColumn storedSeverityColumn;
+ @SuppressWarnings("unused")
@FXML
private TableColumn liveSeverityColumn;
@@ -357,8 +360,8 @@ public class SnapshotController extends SaveAndRestoreBaseController implements
* determine which elements in the {@link List} to actually represent.
*
*
- * Note that the list is cleared and recreated whenever snapshot data has changed, i.e.
- * when retrieved from service or when taking a snapshot.
+ * Note that the list is cleared and recreated whenever snapshot data has changed, i.e.
+ * when retrieved from service or when taking a snapshot.
*
*/
protected final List tableEntryItems = new ArrayList<>();
@@ -670,7 +673,7 @@ protected void updateItem(TableEntry item, boolean empty) {
snapshotUtil = new SnapshotUtil();
- webSocketClientService.addWebSocketMessageHandler(this);
+ saveAndRestoreService.addSaveAndRestoreWebSocketMessageHandler(this);
}
private void updateUi() {
@@ -870,8 +873,8 @@ public boolean doCloseCheck() {
}
@Override
- public void handleTabClosed(){
- webSocketClientService.removeWebSocketMessageHandler(this);
+ public void handleTabClosed() {
+ saveAndRestoreService.removeSaveAndRestoreWebSocketMessageHandler(this);
dispose();
}
@@ -1001,9 +1004,10 @@ private Snapshot getSnapshotFromService(Node snapshotNode) throws Exception {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_UPDATED)) {
- Node node = (Node) saveAndRestoreWebSocketMessage.payload();
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage webSocketMessage) {
+
+ if (webSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_UPDATED)) {
+ Node node = (Node) webSocketMessage.payload();
if (tabIdProperty.get() != null && node.getUniqueId().equals(tabIdProperty.get())) {
loadSnapshot(node);
}
@@ -1110,11 +1114,11 @@ private void takeSnapshotReadPVs(Consumer> consumer) {
Snapshot snapshot = new Snapshot();
snapshot.setSnapshotNode(
Node.builder()
- .nodeType(NodeType.SNAPSHOT)
- // set name and description to preserve the name / comment fields
- .name(snapshotNameProperty.getValue())
- .description(snapshotCommentProperty.getValue())
- .build()
+ .nodeType(NodeType.SNAPSHOT)
+ // set name and description to preserve the name / comment fields
+ .name(snapshotNameProperty.getValue())
+ .description(snapshotCommentProperty.getValue())
+ .build()
);
SnapshotData snapshotData = new SnapshotData();
snapshotData.setSnapshotItems(snapshotItems);
@@ -1145,7 +1149,7 @@ private void showTakeSnapshotResult(List snapshotItems) {
if (snapshotItem.getValue().equals(VDisconnectedData.INSTANCE)) {
disconnectedPvEncountered.set(true);
Platform.runLater(() ->
- actionResultColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/error.png"))));
+ actionResultColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/error.png"))));
break;
}
}
@@ -1154,7 +1158,7 @@ private void showTakeSnapshotResult(List snapshotItems) {
snapshotItem.getReadbackValue().equals(VDisconnectedData.INSTANCE)) {
disconnectedReadbackPvEncountered.set(true);
Platform.runLater(() ->
- actionResultReadbackColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/error.png"))));
+ actionResultReadbackColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/error.png"))));
break;
}
@@ -1492,7 +1496,7 @@ private void showSnapshotInTable() {
});
JobManager.schedule("Connect to PVs", monitor ->
- tableEntryItems.forEach(TableEntry::connect));
+ tableEntryItems.forEach(TableEntry::connect));
updateTable();
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
index eb8d77565e..66be135b29 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
@@ -27,12 +27,10 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Snapshot;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.applications.saveandrestore.ui.WebSocketMessageHandler;
+import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
import org.phoebus.ui.javafx.ImageCache;
@@ -141,12 +139,15 @@ public Node getConfigNode() {
}
@Override
- public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage> saveAndRestoreWebSocketMessage) {
- if (saveAndRestoreWebSocketMessage.messageType().equals(MessageType.NODE_REMOVED)) {
+ public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
+ /*
+ if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
if (getId() != null && nodeId.equals(getId())) {
Platform.runLater(() -> getTabPane().getTabs().remove(this));
}
}
+
+ */
}
}
diff --git a/app/save-and-restore/model/pom.xml b/app/save-and-restore/model/pom.xml
index 37f2ad21c0..e0e6f67890 100644
--- a/app/save-and-restore/model/pom.xml
+++ b/app/save-and-restore/model/pom.xml
@@ -17,6 +17,12 @@
5.0.3-SNAPSHOT
+
+ org.phoebus
+ core-websocket
+ 5.0.3-SNAPSHOT
+
+
javax.json
javax.json-api
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/MessageType.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
similarity index 74%
rename from app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/MessageType.java
rename to app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
index 9cc99875bd..58fb4b7fd5 100644
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/MessageType.java
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
@@ -4,10 +4,12 @@
package org.phoebus.applications.saveandrestore.model.websocket;
+import org.phoebus.core.websocket.MessageType;
+
/**
* Enum to indicate what type of web socket message the service is sending to clients.
*/
-public enum MessageType {
+public enum SaveAndRestoreMessageType implements MessageType {
NODE_ADDED,
NODE_UPDATED,
NODE_REMOVED,
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessage.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessage.java
deleted file mode 100644
index 30e6a7d6d7..0000000000
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessage.java
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (C) 2024 European Spallation Source ERIC.
- */
-
-package org.phoebus.applications.saveandrestore.model.websocket;
-
-/**
- * Record encapsulating a {@link MessageType} and a payload.
- * @param messageType The {@link MessageType} of a web socket message
- * @param payload The payload, e.g. {@link String} or {@link org.phoebus.applications.saveandrestore.model.Node}
- */
-public record SaveAndRestoreWebSocketMessage(MessageType messageType, T payload) {
-}
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
new file mode 100644
index 0000000000..23ffae4f0d
--- /dev/null
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.applications.saveandrestore.model.websocket;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.phoebus.applications.saveandrestore.model.Node;
+import org.phoebus.applications.saveandrestore.model.search.Filter;
+import org.phoebus.core.websocket.WebSocketMessage;
+
+/**
+ * Custom JSON deserializer of {@link WebSocketMessage}s particular to save-and-restore.
+ */
+public class SaveAndRestoreWebSocketMessageDeserializer extends StdDeserializer> {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ public SaveAndRestoreWebSocketMessageDeserializer(Class> clazz) {
+ super(clazz);
+ }
+
+ /**
+ * Deserializes a save-and-restore {@link WebSocketMessage}.
+ *
+ * @param jsonParser Parsed used for reading JSON content
+ * @param context Context that can be used to access information about
+ * this deserialization activity.
+ * @return A {@link WebSocketMessage} object, or null if deserialization fails, e.g. due to
+ * unknown/invalid {@link org.phoebus.core.websocket.MessageType} or null payload.
+ */
+ @Override
+ public WebSocketMessage> deserialize(JsonParser jsonParser, DeserializationContext context) {
+ try {
+ JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser);
+ SaveAndRestoreMessageType saveAndRestoreMessageType = SaveAndRestoreMessageType.valueOf(rootNode.get("messageType").asText());
+ JsonNode payload = rootNode.get("payload");
+ switch (saveAndRestoreMessageType) {
+ case NODE_ADDED, NODE_REMOVED, FILTER_REMOVED-> {
+ return new WebSocketMessage<>(saveAndRestoreMessageType, payload.textValue());
+ }
+ case NODE_UPDATED -> {
+ Node node = objectMapper.readValue(payload.toString(), Node.class);
+ return new WebSocketMessage<>(saveAndRestoreMessageType, node);
+ }
+ case FILTER_ADDED_OR_UPDATED -> {
+ Filter filter = objectMapper.readValue(payload.toString(), Filter.class);
+ return new WebSocketMessage<>(saveAndRestoreMessageType, filter);
+ }
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return null;
+ }
+
+}
+
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
new file mode 100644
index 0000000000..5f8a26b037
--- /dev/null
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.applications.saveandrestore.model.websocket;
+
+import org.phoebus.core.websocket.WebSocketMessage;
+
+/**
+ * Handler for web socket messages that have already been deserialized
+ * to a {@link WebSocketMessage}.
+ */
+public interface SaveAndRestoreWebSocketMessageHandler {
+ void handleSaveAndRestoreWebSocketMessage(WebSocketMessage> webSocketMessage);
+}
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/WebMessageDeserializer.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/WebMessageDeserializer.java
deleted file mode 100644
index 42c4807a74..0000000000
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/WebMessageDeserializer.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- */
-
-package org.phoebus.applications.saveandrestore.model.websocket;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.search.Filter;
-
-/**
- * Custom JSON deserializer of {@link SaveAndRestoreWebSocketMessage}s.
- */
-public class WebMessageDeserializer extends StdDeserializer {
-
- private final ObjectMapper objectMapper = new ObjectMapper();
-
- public WebMessageDeserializer(Class> clazz) {
- super(clazz);
- }
-
- /**
- * Deserializes a {@link SaveAndRestoreWebSocketMessage}-
- *
- * @param jsonParser Parsed used for reading JSON content
- * @param context Context that can be used to access information about
- * this deserialization activity.
- * @return A {@link SaveAndRestoreWebSocketMessage} object, or null if deserialization fails.
- */
- @Override
- public SaveAndRestoreWebSocketMessage> deserialize(JsonParser jsonParser,
- DeserializationContext context) {
- try {
- JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser);
- String messageType = rootNode.get("messageType").asText();
- switch (MessageType.valueOf(messageType)) {
- case NODE_ADDED, NODE_REMOVED, FILTER_REMOVED-> {
- SaveAndRestoreWebSocketMessage saveAndRestoreWebSocketMessage =
- objectMapper.readValue(rootNode.toString(), SaveAndRestoreWebSocketMessage.class);
- return saveAndRestoreWebSocketMessage;
- }
- case NODE_UPDATED -> {
- SaveAndRestoreWebSocketMessage saveAndRestoreWebSocketMessage = objectMapper.readValue(rootNode.toString(), new TypeReference<>() {
- });
- return saveAndRestoreWebSocketMessage;
- }
- case FILTER_ADDED_OR_UPDATED -> {
- SaveAndRestoreWebSocketMessage saveAndRestoreWebSocketMessage = objectMapper.readValue(rootNode.toString(), new TypeReference<>() {
- });
- return saveAndRestoreWebSocketMessage;
- }
- }
- } catch (Exception e) {
- return null;
- }
- return null;
- }
-
-}
-
diff --git a/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java b/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
index 0e60a5eb07..cf2e833ad3 100644
--- a/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
+++ b/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
@@ -10,9 +10,11 @@
import org.junit.jupiter.api.Test;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.search.Filter;
+import org.phoebus.core.websocket.WebSocketMessage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class SaveAndRestoreWebSocketMessageDeserializerTest {
@@ -20,32 +22,30 @@ public class SaveAndRestoreWebSocketMessageDeserializerTest {
public SaveAndRestoreWebSocketMessageDeserializerTest(){
SimpleModule module = new SimpleModule();
- module.addDeserializer(SaveAndRestoreWebSocketMessage.class,
- new WebMessageDeserializer(SaveAndRestoreWebSocketMessage.class));
+ module.addDeserializer(WebSocketMessage.class, new SaveAndRestoreWebSocketMessageDeserializer(WebSocketMessage.class));
mapper.registerModule(module);
}
@Test
public void test1() {
try {
- SaveAndRestoreWebSocketMessage webSocketMessage =
+ WebSocketMessage webSocketMessage =
mapper.readValue(getClass().getResourceAsStream("/websocketexample2.json"), new TypeReference<>() {
});
- assertEquals(MessageType.NODE_UPDATED, webSocketMessage.messageType());
+ assertEquals(SaveAndRestoreMessageType.NODE_UPDATED, webSocketMessage.messageType());
assertEquals("a", webSocketMessage.payload().getUniqueId());
} catch (Exception e) {
throw new RuntimeException(e);
}
-
}
@Test
public void test2() {
try {
- SaveAndRestoreWebSocketMessage webSocketMessage =
+ WebSocketMessage webSocketMessage =
mapper.readValue(getClass().getResourceAsStream("/websocketexample1.json"), new TypeReference<>() {
});
- assertEquals(MessageType.NODE_ADDED, webSocketMessage.messageType());
+ assertEquals(SaveAndRestoreMessageType.NODE_ADDED, webSocketMessage.messageType());
assertEquals("parentNodeId", webSocketMessage.payload());
} catch (Exception e) {
throw new RuntimeException(e);
@@ -55,10 +55,10 @@ public void test2() {
@Test
public void test3() {
try {
- SaveAndRestoreWebSocketMessage webSocketMessage =
+ WebSocketMessage webSocketMessage =
mapper.readValue(getClass().getResourceAsStream("/websocketexample3.json"), new TypeReference<>() {
});
- assertEquals(MessageType.FILTER_ADDED_OR_UPDATED, webSocketMessage.messageType());
+ assertEquals(SaveAndRestoreMessageType.FILTER_ADDED_OR_UPDATED, webSocketMessage.messageType());
assertEquals("myFilter", webSocketMessage.payload().getName());
} catch (Exception e) {
throw new RuntimeException(e);
@@ -68,7 +68,7 @@ public void test3() {
@Test
public void test4() {
try {
- SaveAndRestoreWebSocketMessage webSocketMessage =
+ WebSocketMessage webSocketMessage =
mapper.readValue(getClass().getResourceAsStream("/websocketexample4.json"), new TypeReference<>() {
});
assertNull(webSocketMessage);
@@ -81,10 +81,10 @@ public void test4() {
@Test
public void test5() {
try {
- SaveAndRestoreWebSocketMessage webSocketMessage =
+ WebSocketMessage webSocketMessage =
mapper.readValue(getClass().getResourceAsStream("/websocketexample5.json"), new TypeReference<>() {
});
- assertNull(webSocketMessage.payload());
+ assertNull(webSocketMessage);
}
catch (Exception e) {
throw new RuntimeException(e);
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java b/core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java
new file mode 100644
index 0000000000..27b93238ef
--- /dev/null
+++ b/core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java
@@ -0,0 +1,8 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.core.websocket;
+
+public interface MessageType {
+}
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketClient.java b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketClient.java
deleted file mode 100644
index 5d7d8242db..0000000000
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketClient.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- */
-
-package org.phoebus.core.websocket;
-
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.WebSocket;
-import java.nio.ByteBuffer;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A web socket client implementation supporting pong and text messages.
- *
- *
- * Once connection is established, a ping/pong thread is set up to check peer availability. This should be
- * able to handle both remote peer being shut down and network issues. Ping messages are dispatched once
- * per minute. A reconnection loop is started if a pong message is not received from peer within three seconds.
- *
- */
-public class WebSocketClient implements WebSocket.Listener {
-
- private WebSocket webSocket;
- private final Logger logger = Logger.getLogger(WebSocketClient.class.getName());
- private Runnable connectCallback;
- private Runnable disconnectCallback;
- private final URI uri;
- private final Consumer onTextCallback;
-
- private final AtomicBoolean attemptReconnect = new AtomicBoolean();
- private final AtomicBoolean keepPinging = new AtomicBoolean();
- private CountDownLatch pingCountdownLatch;
-
- /**
- * @param uri The URI of the web socket peer.
- * @param onTextCallback A callback method the API client will use to process web socket messages.
- */
- public WebSocketClient(URI uri, Consumer onTextCallback) {
- this.uri = uri;
- this.onTextCallback = onTextCallback;
- }
-
- /**
- * Attempts to connect to the remote web socket.
- */
- public void connect() {
- doConnect();
- }
-
- /**
- * Internal connect implementation. This is done in a loop with 10 s intervals until
- * connection is established.
- */
- private void doConnect() {
- attemptReconnect.set(true);
- new Thread(() -> {
- while (attemptReconnect.get()) {
- logger.log(Level.INFO, "Attempting web socket connection to " + uri);
- HttpClient.newBuilder()
- .build()
- .newWebSocketBuilder()
- .buildAsync(uri, this);
- try {
- Thread.sleep(10000);
- } catch (InterruptedException e) {
- logger.log(Level.WARNING, "Got interrupted exception");
- }
- }
- }).start();
- }
-
- /**
- * Called when connection has been established. An API client may optionally register a
- * {@link #connectCallback} which is called when connection is opened.
- *
- * @param webSocket the WebSocket that has been connected
- */
- @Override
- public void onOpen(WebSocket webSocket) {
- WebSocket.Listener.super.onOpen(webSocket);
- attemptReconnect.set(false);
- this.webSocket = webSocket;
- if (connectCallback != null) {
- connectCallback.run();
- }
- logger.log(Level.INFO, "Connected to " + uri);
- keepPinging.set(true);
- new Thread(new PingRunnable()).start();
- }
-
- /**
- * Send a text message to peer.
- *
- * @param message The actual message. In practice a JSON formatted string that peer can evaluate
- * to take proper action.
- */
- public void sendText(String message) {
- try {
- webSocket.sendText(message, true).get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-
- /**
- * Called when connection has been closed, e.g. by remote peer. An API client may optionally register a
- * {@link #disconnectCallback} which is called when connection is opened.
- *
- *
- * Note that reconnection will be attempted immediately.
- *
- *
- * @param webSocket the WebSocket that has been connected
- */
- @Override
- public CompletionStage> onClose(WebSocket webSocket,
- int statusCode,
- String reason) {
- logger.log(Level.INFO, "Web socket closed, status code=" + statusCode + ", reason: " + reason);
- if (disconnectCallback != null) {
- disconnectCallback.run();
- }
- return null;
- }
-
- /**
- * Utility method to check connectivity. Peer should respond such that {@link #onPong(WebSocket, ByteBuffer)}
- * is called.
- */
- public void sendPing() {
- logger.log(Level.FINE, Thread.currentThread().getName() + " Sending ping");
- webSocket.sendPing(ByteBuffer.allocate(0));
- }
-
- @Override
- public CompletionStage> onPong(WebSocket webSocket, ByteBuffer message) {
- pingCountdownLatch.countDown();
- logger.log(Level.FINE, "Got pong");
- return WebSocket.Listener.super.onPong(webSocket, message);
- }
-
- @Override
- public void onError(WebSocket webSocket, Throwable error) {
- logger.log(Level.WARNING, "Got web socket error", error);
- WebSocket.Listener.super.onError(webSocket, error);
- }
-
- @Override
- public CompletionStage> onText(WebSocket webSocket,
- CharSequence data,
- boolean last) {
- webSocket.request(1);
- if (onTextCallback != null) {
- onTextCallback.accept(data);
- }
- return WebSocket.Listener.super.onText(webSocket, data, last);
- }
-
- /**
- * NOTE: this must be called by the API client when web socket messages are no longer
- * needed, otherwise reconnect attempts will continue as these run on a separate thread.
- *
- *
- * The status code 1000 is used when calling the {@link WebSocket#sendClose(int, String)} method. See
- * list of common web socket status codes
- * here.
- *
- *
- * @param reason Custom reason text.
- */
- public void close(String reason) {
- keepPinging.set(false);
- attemptReconnect.set(false);
- // webSocket is null if never connected
- if(webSocket != null){
- webSocket.sendClose(1000, reason);
- }
- }
-
- /**
- * @param connectCallback A {@link Runnable} invoked when web socket connects successfully.
- */
- public void setConnectCallback(Runnable connectCallback) {
- this.connectCallback = connectCallback;
- }
-
- /**
- * @param disconnectCallback A {@link Runnable} invoked when web socket disconnects, either
- * when closed explicitly, or if remote peer goes away.
- */
- public void setDisconnectCallback(Runnable disconnectCallback) {
- this.disconnectCallback = disconnectCallback;
- }
-
- private class PingRunnable implements Runnable {
-
- @Override
- public void run() {
- while (keepPinging.get()) {
- pingCountdownLatch = new CountDownLatch(1);
- sendPing();
- try {
- if (!pingCountdownLatch.await(3, TimeUnit.SECONDS)) {
- if (disconnectCallback != null) {
- disconnectCallback.run();
- }
- logger.log(Level.WARNING, "No pong response within three seconds");
- doConnect();
- return;
- } else {
- Thread.sleep(60000);
- }
- } catch (InterruptedException e) {
- logger.log(Level.WARNING, "Got interrupted exception");
- return;
- }
- }
- }
- }
-}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
similarity index 56%
rename from services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java
rename to core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
index 389fd33bee..7cb366197a 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketController.java
+++ b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
@@ -16,24 +16,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-package org.phoebus.service.saveandrestore.websocket;
-
-
-import org.springframework.messaging.handler.annotation.MessageMapping;
-import org.springframework.messaging.handler.annotation.SendTo;
-import org.springframework.stereotype.Controller;
+package org.phoebus.core.websocket;
/**
- * This {@link Controller} defines an echo endpoint, i.e. for testing or health check purposes...
+ * Record encapsulating a {@link MessageType} and a payload of arbitrary type.
+ *
+ * The deserialization process of a web socket message into a concrete {@link WebSocketMessage} must be
+ * delegated to a custom deserializer.
+ *
+ * @param messageType The {@link MessageType} of a web socket message. Apps can implement as needed.
+ * @param payload The payload like a {@link String}, or something more specific for the actual use case, e.g. a
+ * logbook entry or save-and-restore object.
*/
-@Controller
-@SuppressWarnings("unused")
-public class WebSocketController {
-
-
- @MessageMapping("/echo")
- @SendTo("web-socket/messages")
- public String echo(String message) {
- return message;
- }
+public record WebSocketMessage(MessageType messageType, T payload) {
}
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java
index 7299c2a2fd..b94acc9745 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java
+++ b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java
@@ -4,6 +4,9 @@
package org.phoebus.core.websocket;
+/**
+ * Handler for raw web socket string messages.
+ */
public interface WebSocketMessageHandler {
void handleWebSocketMessage(String message);
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java b/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
index 826dd8b241..2397309935 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
+++ b/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
@@ -20,10 +20,7 @@
import org.springframework.web.socket.messaging.WebSocketStompClient;
import java.lang.reflect.Type;
-import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -72,10 +69,6 @@ public class WebSocketClientService {
* Subscription endpoint, e.g. /Olog/web-socket/messages
*/
private final String subscriptionEndpoint;
- /**
- * Echo endpoint /Olog/web-socket/echo
- */
- private final String echoEndpoint;
private static final Logger logger = Logger.getLogger(WebSocketClientService.class.getName());
@@ -94,7 +87,6 @@ public WebSocketClientService(@NonNull String connectUrl) {
path = path.substring(0, path.length() - 1);
}
this.subscriptionEndpoint = path + "/messages";
- this.echoEndpoint = path + "/echo";
}
/**
@@ -128,18 +120,6 @@ public void removeWebSocketMessageHandler(WebSocketMessageHandler webSocketMessa
webSocketMessageHandlers.remove(webSocketMessageHandler);
}
- /**
- * For debugging purposes: peer should just echo back the message on the subscribed topic.
- *
- * @param message Message for the service to echo
- */
- @SuppressWarnings("unused")
- public void sendEcho(String message) {
- if (stompSession != null && stompSession.isConnected() && echoEndpoint != null) {
- stompSession.send(echoEndpoint, message);
- }
- }
-
/**
* Disconnects the socket if connected and terminates connection thread.
*/
@@ -193,7 +173,7 @@ public void handleFrame(StompHeaders headers, Object payload) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
- logger.log(Level.WARNING, "Got exception when trying to connect", e);
+ logger.log(Level.WARNING, "Got exception when putting thread to sleep", e);
}
}
}).start();
@@ -296,37 +276,4 @@ public Type getPayloadType(StompHeaders headers) {
}
return false;
}
-
- /**
- * Determines the web socket URL from the REST URL. This is a convenience method for clients connecting
- * to a standard http(s) REST API, but that also need to maintain a client connecting to the
- * same service over web sockets.
- *
- * @param restUrl The URL clients use for REST API calls to some service.
- * @return The web socket connection URL derived from the REST URL.
- * @throws Exception if restUrl is invalid or cannot be parsed as a valid URI (e.g. due to
- * non-URL encoded chars like space).
- */
- public static String getWebsocketConnectUrl(String restUrl) throws Exception {
- URI uri;
- try {
- URL url = new URL(restUrl);
- uri = url.toURI();
- } catch (MalformedURLException e) {
- logger.log(Level.WARNING, "Invalid REST url: " + restUrl, e);
- throw new Exception(e);
- } catch (URISyntaxException e) {
- logger.log(Level.WARNING, "REST url " + restUrl + " cannot be parsed as URI", e);
- throw new Exception(e);
- }
- String scheme = uri.getScheme();
- String host = uri.getHost();
- int port = uri.getPort();
- String path = uri.getPath();
- if (path.endsWith("/")) {
- path = path.substring(0, path.length() - 1);
- }
- String webSocketScheme = scheme.toLowerCase().startsWith("https") ? "wss" : "ws";
- return webSocketScheme + "://" + host + (port > -1 ? (":" + port) : "") + path + "/web-socket";
- }
}
diff --git a/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java b/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
deleted file mode 100644
index d91609d98e..0000000000
--- a/core/websocket/src/test/java/org/phoebus/core/websocket/springframework/WebSocketClientServiceTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2025 European Spallation Source ERIC.
- */
-
-package org.phoebus.core.websocket.springframework;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class WebSocketClientServiceTest {
-
- @Test
- public void testGetWebsocketConnectionParameters_1() throws Exception {
- String restUrl = "http://localhost:8080";
- assertEquals("ws://localhost:8080/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-
- @Test
- public void testGetWebsocketConnectionParameters_2() throws Exception {
- String restUrl = "https://localhost:8080";
- assertEquals("wss://localhost:8080/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-
- @Test
- public void testGetWebsocketConnectionParameters_3() throws Exception {
- String restUrl = "https://localhost:8080/path-element";
- assertEquals("wss://localhost:8080/path-element/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-
- @Test
- public void testGetWebsocketConnectionParameters_4() throws Exception {
- String restUrl = "https://localhost/path-element";
- assertEquals("wss://localhost/path-element/web-socket", WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-
- @Test
- public void testGetWebsocketConnectionParameters_5(){
- String restUrl = "https://localhost:8080/path element";
- assertThrows(Exception.class, () -> WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-
- @Test
- public void testGetWebsocketConnectionParameters_7(){
- String restUrl = "invalid";
- assertThrows(Exception.class, () -> WebSocketClientService.getWebsocketConnectUrl(restUrl));
- }
-}
diff --git a/services/save-and-restore/pom.xml b/services/save-and-restore/pom.xml
index d0e5a2360e..c951110900 100644
--- a/services/save-and-restore/pom.xml
+++ b/services/save-and-restore/pom.xml
@@ -90,7 +90,6 @@
org.phoebus
core-websocket
5.0.3-SNAPSHOT
- test
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
index 2d20f7517f..3645f194a2 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
@@ -20,12 +20,15 @@
import org.phoebus.saveandrestore.util.SnapshotUtil;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchDAO;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
+import javax.servlet.ServletContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -39,6 +42,9 @@ public class WebConfiguration {
@Value("${connection.timeout:5000}")
public long connectionTimeout;
+ @Autowired
+ private ServletContext servletContext;
+
@SuppressWarnings("unused")
@Bean
public long getConnectionTimeout() {
@@ -77,4 +83,9 @@ public SnapshotUtil snapshotRestorer() {
public ExecutorService executorService() {
return Executors.newCachedThreadPool();
}
+
+ @Bean
+ public String contextPath(){
+ return servletContext.getContextPath();
+ }
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
index d2955b20ff..bfa54a96c4 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
@@ -24,8 +24,8 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -74,7 +74,7 @@ public CompositeSnapshot createCompositeSnapshot(@RequestParam(value = "parentNo
}
compositeSnapshot.getCompositeSnapshotNode().setUserName(principal.getName());
CompositeSnapshot newCompositeSnapshot = nodeDAO.createCompositeSnapshot(parentNodeId, compositeSnapshot);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newCompositeSnapshot.getCompositeSnapshotNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_ADDED, compositeSnapshot.getCompositeSnapshotNode().getUniqueId()));
return newCompositeSnapshot;
}
@@ -94,7 +94,7 @@ public CompositeSnapshot updateCompositeSnapshot(@RequestBody CompositeSnapshot
}
compositeSnapshot.getCompositeSnapshotNode().setUserName(principal.getName());
CompositeSnapshot updatedCompositeSnapshot = nodeDAO.updateCompositeSnapshot(compositeSnapshot);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedCompositeSnapshot.getCompositeSnapshotNode()));
+ webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_UPDATED, updatedCompositeSnapshot.getCompositeSnapshotNode()));
return updatedCompositeSnapshot;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
index 830ecd2b26..83dfe14a37 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
@@ -21,8 +21,8 @@
import org.phoebus.applications.saveandrestore.model.Configuration;
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -79,7 +79,7 @@ public Configuration createConfiguration(@RequestParam(value = "parentNodeId") S
}
configuration.getConfigurationNode().setUserName(principal.getName());
Configuration newConfiguration = nodeDAO.createConfiguration(parentNodeId, configuration);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newConfiguration.getConfigurationNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_ADDED, newConfiguration.getConfigurationNode().getUniqueId()));
return newConfiguration;
}
@@ -109,7 +109,7 @@ public Configuration updateConfiguration(@RequestBody Configuration configuratio
Principal principal) {
configuration.getConfigurationNode().setUserName(principal.getName());
Configuration updatedConfiguration = nodeDAO.updateConfiguration(configuration);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedConfiguration.getConfigurationNode()));
+ webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_UPDATED, updatedConfiguration.getConfigurationNode()));
return updatedConfiguration;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
index eee74041f4..e1a00c39c8 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
@@ -20,8 +20,8 @@
package org.phoebus.service.saveandrestore.web.controllers;
import org.phoebus.applications.saveandrestore.model.search.Filter;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -63,7 +63,7 @@ public Filter saveFilter(@RequestBody final Filter filter,
Principal principal) {
filter.setUser(principal.getName());
Filter savedFilter = nodeDAO.saveFilter(filter);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_ADDED_OR_UPDATED, filter));
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.FILTER_ADDED_OR_UPDATED, filter));
return savedFilter;
}
@@ -87,6 +87,6 @@ public List getAllFilters() {
@PreAuthorize("@authorizationHelper.maySaveOrDeleteFilter(#name, #root)")
public void deleteFilter(@PathVariable final String name, Principal principal) {
nodeDAO.deleteFilter(name);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.FILTER_REMOVED, name));
+ webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.FILTER_REMOVED, name));
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
index 8ccf7378e4..1e5ad28032 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
@@ -20,8 +20,8 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Tag;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -83,7 +83,7 @@ public Node createNode(@RequestParam(name = "parentNodeId") String parentsUnique
}
node.setUserName(principal.getName());
Node savedNode = nodeDAO.createNode(parentsUniqueId, node);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED,
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_ADDED,
savedNode.getUniqueId()));
return savedNode;
}
@@ -158,7 +158,7 @@ public List getChildNodes(@PathVariable final String uniqueNodeId) {
public void deleteNodes(@RequestBody List nodeIds) {
nodeDAO.deleteNodes(nodeIds);
nodeIds.forEach(id ->
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, id)));
+ webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, id)));
}
/**
@@ -220,7 +220,7 @@ public Node updateNode(@RequestParam(value = "customTimeForMigration", required
}
nodeToUpdate.setUserName(principal.getName());
Node updatedNode = nodeDAO.updateNode(nodeToUpdate, Boolean.parseBoolean(customTimeForMigration));
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedNode));
+ webSocketService.sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_UPDATED, updatedNode));
return updatedNode;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
index 3c48680ae2..166382fe35 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
@@ -21,8 +21,8 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Snapshot;
import org.phoebus.applications.saveandrestore.model.SnapshotData;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -88,7 +88,7 @@ public Snapshot createSnapshot(@RequestParam(value = "parentNodeId") String pare
}
snapshot.getSnapshotNode().setUserName(principal.getName());
Snapshot newSnapshot = nodeDAO.createSnapshot(parentNodeId, snapshot);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_ADDED, newSnapshot.getSnapshotNode().getUniqueId()));
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_ADDED, newSnapshot.getSnapshotNode().getUniqueId()));
return newSnapshot;
}
@@ -108,7 +108,7 @@ public Snapshot updateSnapshot(@RequestBody Snapshot snapshot,
}
snapshot.getSnapshotNode().setUserName(principal.getName());
Snapshot updatedSnapshot = nodeDAO.updateSnapshot(snapshot);
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, updatedSnapshot.getSnapshotNode()));
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED, updatedSnapshot.getSnapshotNode()));
return updatedSnapshot;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
index 597e4c82c5..2fdf5641bc 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
@@ -18,8 +18,8 @@
package org.phoebus.service.saveandrestore.web.controllers;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -76,9 +76,9 @@ public Node moveNodes(@RequestParam(value = "to") String to,
Logger.getLogger(StructureController.class.getName()).info(Thread.currentThread().getName() + " " + (new Date()) + " move");
Node targetNode = nodeDAO.moveNodes(nodes, to, principal.getName());
// Update clients
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED,
targetNode));
- webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_UPDATED,
+ webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED,
sourceParentNode));
return targetNode;
}
@@ -106,7 +106,7 @@ public Node copyNodes(@RequestParam(value = "to") String to,
Logger.getLogger(StructureController.class.getName()).info(Thread.currentThread().getName() + " " + (new Date()) + " copy");
Node targetNode = nodeDAO.getNode(to);
List newNodes = nodeDAO.copyNodes(nodes, to, principal.getName());
- newNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage<>(MessageType.NODE_ADDED, n.getUniqueId())));
+ newNodes.forEach(n -> webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_ADDED, n.getUniqueId())));
return targetNode;
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
index 015d1004cd..6fb2d0c35a 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
@@ -24,8 +24,8 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.Tag;
import org.phoebus.applications.saveandrestore.model.TagData;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -77,7 +77,7 @@ public List addTag(@RequestBody TagData tagData,
Principal principal) {
tagData.getTag().setUserName(principal.getName());
List taggedNodes = nodeDAO.addTag(tagData);
- taggedNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
+ taggedNodes.forEach(n -> webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED, n)));
return taggedNodes;
}
@@ -92,7 +92,7 @@ public List addTag(@RequestBody TagData tagData,
@PreAuthorize("@authorizationHelper.mayAddOrDeleteTag(#tagData, #root)")
public List deleteTag(@RequestBody TagData tagData) {
List untaggedNodes = nodeDAO.deleteTag(tagData);
- untaggedNodes.forEach(n -> webSocketService.sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_UPDATED, n)));
+ untaggedNodes.forEach(n -> webSocketService.sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_UPDATED, n)));
return untaggedNodes;
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
index 46485fce04..2969c5f95c 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
@@ -27,6 +27,7 @@
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
+import javax.servlet.ServletContext;
import java.util.logging.Logger;
@SuppressWarnings("unused")
@@ -37,6 +38,9 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
public ObjectMapper objectMapper;
+ @Autowired
+ private String contextPath;
+
private final Logger logger = Logger.getLogger(WebSocketConfig.class.getName());
private TaskScheduler messageBrokerTaskScheduler;
@@ -48,10 +52,10 @@ public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
- config.enableSimpleBroker("/web-socket/messages")
+ String destination = contextPath.length() > 1 ? contextPath + "/web-socket/messages" : "/web-socket/messages";
+ config.enableSimpleBroker(destination)
.setHeartbeatValue(new long[]{30000, 30000})
.setTaskScheduler(this.messageBrokerTaskScheduler);
- config.setApplicationDestinationPrefixes("/web-socket");
}
@Override
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
index 7b9c534ade..7a7b4afc36 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
@@ -20,13 +20,15 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.stereotype.Service;
+import org.phoebus.core.websocket.WebSocketMessage;
+
+
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -45,15 +47,20 @@ public class WebSocketService {
@Autowired
private ObjectMapper objectMapper;
+ @Autowired
+ private String contextPath;
+
private static final Logger logger = Logger.getLogger(WebSocketService.class.getName());
/**
- * @param webSocketMessage Non-null {@link SaveAndRestoreWebSocketMessage}, will be converted to a JSON string before it is dispatched.
+ * @param webSocketMessage Non-null {@link WebSocketMessage}, will be converted to a JSON string before
+ * it is dispatched to clients.
*/
- public void sendMessageToClients(@NonNull SaveAndRestoreWebSocketMessage webSocketMessage) {
+ public void sendMessageToClients(@NonNull WebSocketMessage> webSocketMessage) {
try {
String message = objectMapper.writeValueAsString(webSocketMessage);
- simpMessagingTemplate.convertAndSend("/web-socket/messages", message);
+ String messageEndpoint = contextPath.length() > 0 ? contextPath : "";
+ simpMessagingTemplate.convertAndSend(messageEndpoint + "/web-socket/messages", message);
} catch (JsonProcessingException e) {
logger.log(Level.WARNING, "Failed to write web socket message to json string", e);
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
index eb75fcae17..63c0cd57f3 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
@@ -31,7 +31,7 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -125,7 +125,7 @@ public void testCreateCompositeSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, CompositeSnapshot.class);
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -142,7 +142,7 @@ public void testCreateCompositeSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -157,7 +157,7 @@ public void testCreateCompositeSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -171,7 +171,7 @@ public void testCreateCompositeSnapshot4() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -187,7 +187,7 @@ public void testCreateCompositeSnapshotWrongNodeType() throws Exception {
.content(objectMapper.writeValueAsString(compositeSnapshot1));
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -214,7 +214,7 @@ public void testUpdateCompositeSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, CompositeSnapshot.class);
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -235,7 +235,7 @@ public void testUpdateCompositeSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(org.phoebus.core.websocket.WebSocketMessage.class));
}
@Test
@@ -262,7 +262,7 @@ public void testUpdateCompositeSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -285,7 +285,7 @@ public void testUpdateCompositeSnapshot4() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -302,7 +302,7 @@ public void testUpdateCompositeSnapshotWrongNodeType() throws Exception {
.content(objectMapper.writeValueAsString(compositeSnapshot1));
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
index 8db4fc24e0..b5675c5f8c 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
@@ -31,7 +31,7 @@
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -111,7 +111,7 @@ public void testCreateConfiguration1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -132,7 +132,7 @@ public void testCreateConfiguration2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -148,7 +148,7 @@ public void testCreateConfiguration3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -162,7 +162,7 @@ public void testCreateConfiguration4() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -183,7 +183,7 @@ public void testUpdateConfiguration1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -204,7 +204,7 @@ public void tesUpdateConfiguration2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -224,7 +224,7 @@ public void testUpdateConfiguration3() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -256,7 +256,7 @@ public void testUpdateConfiguration5() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -271,7 +271,7 @@ public void testUpdateConfiguration6() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
mockMvc.perform(request).andExpect(status().isUnauthorized());
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
index 7c19d5b52b..6c935aff62 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
@@ -25,7 +25,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.phoebus.applications.saveandrestore.model.search.Filter;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -114,7 +114,7 @@ public void testSaveFilter1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(s, Filter.class);
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -134,12 +134,12 @@ public void testSaveFilter2() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
- public void testSaveFiliter3() throws Exception {
+ public void testSaveFilter3() throws Exception {
Filter filter = new Filter();
filter.setName("name");
@@ -154,12 +154,12 @@ public void testSaveFiliter3() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
- public void testSaveFiliter4() throws Exception {
+ public void testSaveFilter4() throws Exception {
Filter filter = new Filter();
filter.setName("name");
@@ -174,7 +174,7 @@ public void testSaveFiliter4() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -192,7 +192,7 @@ public void testDeleteFilter() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -204,7 +204,7 @@ public void testDeleteFilter2() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -216,7 +216,7 @@ public void testDeleteFilter3() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -226,7 +226,7 @@ public void testDeleteFilter4() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -243,7 +243,7 @@ public void testDeleteFilter5() throws Exception {
.contentType(JSON);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
index 615fac0159..35823cc37c 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
@@ -34,8 +34,8 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Tag;
-import org.phoebus.applications.saveandrestore.model.websocket.MessageType;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.NodeNotFoundException;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
@@ -440,7 +440,7 @@ public void testDeleteFolder() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -454,7 +454,7 @@ public void testDeleteForbiddenAccess() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new WebSocketMessage<>(SaveAndRestoreMessageType.NODE_REMOVED, "b"));
}
@Test
@@ -469,12 +469,12 @@ public void testDeleteForbiddenAccess2() throws Exception {
.header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, "b"));
when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.CONFIGURATION).userName(demoUser).build());
when(nodeDAO.getChildNodes("a")).thenReturn(Collections.emptyList());
- verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, "b"));
}
@@ -491,7 +491,7 @@ public void testDeleteFolder2() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@@ -508,7 +508,7 @@ public void testDeleteFolder3() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -523,7 +523,7 @@ public void testDeleteForbidden3() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, "b"));
}
@Test
@@ -538,7 +538,7 @@ public void testDeleteForbidden4() throws Exception {
.header(HttpHeaders.AUTHORIZATION, userAuthorization);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(new SaveAndRestoreWebSocketMessage(MessageType.NODE_REMOVED, "b"));
+ verify(webSocketService, times(0)).sendMessageToClients(new WebSocketMessage(SaveAndRestoreMessageType.NODE_REMOVED, "b"));
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
index f5c8225923..089196e22a 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
@@ -28,7 +28,7 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Snapshot;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -109,7 +109,7 @@ public void testSaveSnapshotWrongNodeType() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -124,7 +124,7 @@ public void testSaveSnapshotNoParentNodeId() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -150,7 +150,7 @@ public void testCreateSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class);
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -173,7 +173,7 @@ public void testCreateSnapshot2() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -194,7 +194,7 @@ public void testCreateSnapshot3() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -220,7 +220,7 @@ public void testUpdateSnapshot1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class);
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -237,7 +237,7 @@ public void testUpdateSnapshot2() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -255,7 +255,7 @@ public void testUpdateSnapshot3() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -276,7 +276,7 @@ public void testUpdateSnapshot4() throws Exception {
.content(snapshotString);
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -297,7 +297,7 @@ public void testDeleteSnapshot1() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -310,7 +310,7 @@ public void testDeleteSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -330,7 +330,7 @@ public void testDeleteSnapshot3() throws Exception {
mockMvc.perform(request).andExpect(status().isOk());
- verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(1)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -342,6 +342,6 @@ public void testDeleteSnapshot4() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
index 7a11c10e02..6c216efc55 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
@@ -25,7 +25,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessage;
+import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -108,7 +108,7 @@ public void testMoveNode1() throws Exception {
// Make sure response contains expected data
objectMapper.readValue(result.getResponse().getContentAsString(), Node.class);
- verify(webSocketService, times(2)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(2)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -126,7 +126,7 @@ public void testMoveNode2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -149,7 +149,7 @@ public void testMoveNode3() throws Exception {
mockMvc.perform(request).andExpect(status().isUnauthorized());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -163,7 +163,7 @@ public void testMoveNodeSourceNodeListEmpty() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -177,7 +177,7 @@ public void testMoveNodeTargetIdEmpty() throws Exception {
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -196,7 +196,7 @@ public void testCopyNodes1() throws Exception {
.param("to", "target");
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -219,7 +219,7 @@ public void testCopyNodesBadRequest1() throws Exception {
.param("to", "");
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
@@ -243,6 +243,6 @@ public void testCopyNodesBadRequest3() throws Exception {
.param("to", "target");
mockMvc.perform(request).andExpect(status().isBadRequest());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(SaveAndRestoreWebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
}
From 02befd85f084c5950c912878384996e29d58dd6b Mon Sep 17 00:00:00 2001
From: georgweiss
Date: Tue, 3 Feb 2026 15:51:36 +0100
Subject: [PATCH 4/4] Restructure modules to reduce code duplication for web
socket stuff
---
app/logbook/olog/ui/pom.xml | 7 ++-
.../olog/ui/LogEntryTableViewController.java | 3 +-
.../olog/ui/LogbookSearchController.java | 9 ++--
.../olog/ui/websocket/LogbookMessageType.java | 2 +-
.../LogbookWebSocketMessageDeserializer.java | 9 ++--
...gbookWebSocketMessageDeserializerTest.java | 2 +-
app/save-and-restore/app/pom.xml | 7 ++-
.../ui/SaveAndRestoreBaseController.java | 11 +---
.../ui/SaveAndRestoreController.java | 10 ++--
.../ui/SaveAndRestoreService.java | 10 ++--
.../saveandrestore/ui/SaveAndRestoreTab.java | 27 +++++++---
.../ConfigurationController.java | 2 +-
.../ui/configuration/ConfigurationTab.java | 17 +------
.../search/SearchAndFilterViewController.java | 2 +-
.../SearchResultTableViewController.java | 2 +-
.../snapshot/CompositeSnapshotController.java | 2 +-
.../ui/snapshot/CompositeSnapshotTab.java | 22 +-------
.../ui/snapshot/SnapshotController.java | 2 +-
.../ui/snapshot/SnapshotTab.java | 17 +------
app/save-and-restore/model/pom.xml | 8 ++-
.../websocket/SaveAndRestoreMessageType.java | 2 +-
...ndRestoreWebSocketMessageDeserializer.java | 4 +-
...SaveAndRestoreWebSocketMessageHandler.java | 2 +-
...storeWebSocketMessageDeserializerTest.java | 2 +-
core/websocket/client/pom.xml | 51 +++++++++++++++++++
.../WebSocketClientService.java | 8 +--
.../WebSocketMessageHandler.java | 6 +--
core/websocket/common/pom.xml | 20 ++++++++
.../core/websocket/common/Constants.java | 14 +++++
.../core/websocket/common}/MessageType.java | 2 +-
.../websocket/common/WebSocketMessage.java | 18 +++++++
core/websocket/pom.xml | 32 ++----------
.../core/websocket/WebSocketMessage.java | 32 ------------
dependencies/phoebus-target/pom.xml | 20 +++-----
pom.xml | 1 +
services/alarm-config-logger/pom.xml | 3 +-
services/alarm-logger/pom.xml | 3 +-
services/save-and-restore/pom.xml | 8 +--
.../web/config/WebConfiguration.java | 10 ++--
.../CompositeSnapshotController.java | 2 +-
.../controllers/ConfigurationController.java | 2 +-
.../web/controllers/FilterController.java | 2 +-
.../web/controllers/NodeController.java | 2 +-
.../web/controllers/SnapshotController.java | 2 +-
.../web/controllers/StructureController.java | 2 +-
.../web/controllers/TagController.java | 2 +-
.../websocket/WebSocketConfig.java | 11 ++--
.../websocket/WebSocketService.java | 7 ++-
.../web/config/ControllersTestConfig.java | 21 ++++++--
.../CompositeSnapshotControllerTest.java | 4 +-
.../ConfigurationControllerTest.java | 2 +-
.../web/controllers/FilterControllerTest.java | 2 +-
.../web/controllers/NodeControllerTest.java | 2 +-
.../controllers/SnapshotControllerTest.java | 2 +-
.../controllers/StructureControllerTest.java | 2 +-
55 files changed, 249 insertions(+), 227 deletions(-)
create mode 100644 core/websocket/client/pom.xml
rename core/websocket/{src/main/java/org/phoebus/core/websocket/springframework => client/src/main/java/org.phoebus.core.websocket.client}/WebSocketClientService.java (97%)
rename core/websocket/{src/main/java/org/phoebus/core/websocket => client/src/main/java/org.phoebus.core.websocket.client}/WebSocketMessageHandler.java (63%)
create mode 100644 core/websocket/common/pom.xml
create mode 100644 core/websocket/common/src/main/java/org/phoebus/core/websocket/common/Constants.java
rename core/websocket/{src/main/java/org/phoebus/core/websocket => common/src/main/java/org/phoebus/core/websocket/common}/MessageType.java (69%)
create mode 100644 core/websocket/common/src/main/java/org/phoebus/core/websocket/common/WebSocketMessage.java
delete mode 100644 core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
diff --git a/app/logbook/olog/ui/pom.xml b/app/logbook/olog/ui/pom.xml
index d2ecdcf8fa..48915873b2 100644
--- a/app/logbook/olog/ui/pom.xml
+++ b/app/logbook/olog/ui/pom.xml
@@ -16,9 +16,14 @@
org.phoebus
- core-websocket
+ core-websocket-common
5.0.3-SNAPSHOT
+
+ org.phoebus
+ core-websocket-client
+ 5.0.3-SNAPSHOT
+
org.phoebus
core-ui
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
index bfd958f7e3..3e5bb795be 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java
@@ -43,7 +43,7 @@
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.phoebus.applications.logbook.authentication.OlogAuthenticationScope;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
+import org.phoebus.core.websocket.client.WebSocketMessageHandler;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.logbook.LogClient;
import org.phoebus.logbook.LogEntry;
@@ -69,7 +69,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
index 1122d6d37a..e3dda11d1c 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogbookSearchController.java
@@ -11,9 +11,10 @@
import javafx.fxml.FXML;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
-import org.phoebus.core.websocket.WebSocketMessage;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
-import org.phoebus.core.websocket.springframework.WebSocketClientService;
+import org.phoebus.core.websocket.client.WebSocketMessageHandler;
+import org.phoebus.core.websocket.common.Constants;
+import org.phoebus.core.websocket.common.WebSocketMessage;
+import org.phoebus.core.websocket.client.WebSocketClientService;
import org.phoebus.framework.jobs.Job;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.logbook.LogClient;
@@ -78,7 +79,7 @@ public void initialize() {
webSocketConnectUrl = Preferences.olog_url.trim().toLowerCase().startsWith("https://") ?
Preferences.olog_url.trim().replace("https", "wss") :
Preferences.olog_url.trim().replace("http", "ws");
- webSocketConnectUrl += "/Olog/web-socket";
+ webSocketConnectUrl += "/Olog" + Constants.WEB_SOCKET;
}
/**
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
index 2cdfbe3c86..28e15291ef 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookMessageType.java
@@ -18,7 +18,7 @@
package org.phoebus.logbook.olog.ui.websocket;
-import org.phoebus.core.websocket.MessageType;
+import org.phoebus.core.websocket.common.MessageType;
/**
* Web socket message types particular to the logbook
diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
index fea7585789..ac9f8c3dab 100644
--- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
+++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializer.java
@@ -8,7 +8,7 @@
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
/**
* Custom JSON deserializer of {@link WebSocketMessage}s particular to the logbook app.
@@ -26,7 +26,7 @@ public LogbookWebSocketMessageDeserializer(Class> clazz) {
* @param context Context that can be used to access information about
* this deserialization activity.
* @return A {@link WebSocketMessage} object, or null if deserialization fails, e.g. due to
- * unknown/invalid {@link org.phoebus.core.websocket.MessageType} or null payload.
+ * unknown/invalid {@link org.phoebus.core.websocket.common.MessageType} or null payload.
*/
@Override
public WebSocketMessage> deserialize(JsonParser jsonParser, DeserializationContext context) {
@@ -41,9 +41,8 @@ public WebSocketMessage> deserialize(JsonParser jsonParser, DeserializationCon
case LOG_ENTRY_UPDATED -> {
return new WebSocketMessage<>(logbookMessageType, payload.textValue());
}
- case SHOW_BANNER -> {
- throw new RuntimeException("SHOW_BANNER not yet implemented");
- }
+ case SHOW_BANNER -> throw new RuntimeException("SHOW_BANNER not yet implemented");
+
}
} catch (Exception e) {
return null;
diff --git a/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java b/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
index 875e6044e3..94c57b5e42 100644
--- a/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
+++ b/app/logbook/olog/ui/src/test/java/org/phoebus/logbook/olog/ui/websocket/LogbookWebSocketMessageDeserializerTest.java
@@ -8,7 +8,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.jupiter.api.Test;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import static org.junit.jupiter.api.Assertions.*;
diff --git a/app/save-and-restore/app/pom.xml b/app/save-and-restore/app/pom.xml
index 29b2b634a8..10a3a4feaf 100644
--- a/app/save-and-restore/app/pom.xml
+++ b/app/save-and-restore/app/pom.xml
@@ -32,7 +32,12 @@
org.phoebus
- core-websocket
+ core-websocket-common
+ 5.0.3-SNAPSHOT
+
+
+ org.phoebus
+ core-websocket-client
5.0.3-SNAPSHOT
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
index b5daadd145..f5837494fa 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreBaseController.java
@@ -21,7 +21,6 @@
import javafx.beans.property.SimpleStringProperty;
import org.phoebus.applications.saveandrestore.authentication.SaveAndRestoreAuthenticationScope;
-import org.phoebus.core.websocket.WebSocketMessage;
import org.phoebus.security.store.SecureStore;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
@@ -50,7 +49,7 @@ public SaveAndRestoreBaseController() {
}
} catch (Exception e) {
Logger.getLogger(SaveAndRestoreBaseController.class.getName()).log(Level.WARNING, "Unable to retrieve authentication token for " +
- new SaveAndRestoreAuthenticationScope().getScope()+ " scope", e);
+ new SaveAndRestoreAuthenticationScope().getScope() + " scope", e);
}
}
@@ -69,13 +68,6 @@ public SimpleStringProperty getUserIdentity() {
return userIdentity;
}
- /**
- * Default no-op implementation of a handler for {@link WebSocketMessage}s.
- * @param webSocketMessage See {@link WebSocketMessage}
- */
- protected void handleWebSocketMessage(WebSocketMessage> webSocketMessage){
- }
-
/**
* Performs suitable cleanup, e.g. close web socket and PVs (where applicable).
*/
@@ -83,6 +75,7 @@ protected void handleWebSocketMessage(WebSocketMessage> webSocketMessage){
/**
* Checks if the tab may be closed, e.g. if data managed in the UI has been saved.
+ *
* @return false if tab contains unsaved data, otherwise true
*/
public abstract boolean doCloseCheck();
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
index 8a37b08313..d18bc71c94 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java
@@ -102,7 +102,7 @@
import org.phoebus.applications.saveandrestore.ui.snapshot.SnapshotTab;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagUtil;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagWidget;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.framework.selection.SelectionService;
@@ -816,8 +816,10 @@ private void nodeAdded(String nodeId) {
// Find the parent to which the new node is to be added
TreeItem parentTreeItem = recursiveSearch(parentNode.getUniqueId(), treeView.getRoot());
TreeItem newTreeItem = createTreeItem(newNode);
- parentTreeItem.getChildren().add(newTreeItem);
- parentTreeItem.getChildren().sort(treeNodeComparator);
+ Platform.runLater(() -> {
+ parentTreeItem.getChildren().add(newTreeItem);
+ parentTreeItem.getChildren().sort(treeNodeComparator);
+ });
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -832,7 +834,7 @@ private void nodeAdded(String nodeId) {
private void nodeRemoved(String nodeId) {
TreeItem treeItemToRemove = recursiveSearch(nodeId, treeView.getRoot());
if (treeItemToRemove != null) {
- treeItemToRemove.getParent().getChildren().remove(treeItemToRemove);
+ Platform.runLater(() -> treeItemToRemove.getParent().getChildren().remove(treeItemToRemove));
}
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
index d75e9c4d61..707e93b90b 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java
@@ -18,7 +18,6 @@
package org.phoebus.applications.saveandrestore.ui;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
@@ -43,9 +42,10 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageDeserializer;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.core.vtypes.VDisconnectedData;
-import org.phoebus.core.websocket.WebSocketMessage;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
-import org.phoebus.core.websocket.springframework.WebSocketClientService;
+import org.phoebus.core.websocket.common.Constants;
+import org.phoebus.core.websocket.common.WebSocketMessage;
+import org.phoebus.core.websocket.client.WebSocketMessageHandler;
+import org.phoebus.core.websocket.client.WebSocketClientService;
import org.phoebus.pv.PV;
import org.phoebus.pv.PVPool;
import org.phoebus.saveandrestore.util.VNoData;
@@ -90,7 +90,7 @@ private SaveAndRestoreService() {
String webSocketConnectUrl = Preferences.jmasarServiceUrl.trim().toLowerCase().startsWith("https://") ?
Preferences.jmasarServiceUrl.trim().replace("https", "wss") :
Preferences.jmasarServiceUrl.trim().replace("http", "ws");
- webSocketConnectUrl += "/web-socket";
+ webSocketConnectUrl += Constants.WEB_SOCKET;
webSocketClientService = new WebSocketClientService(webSocketConnectUrl);
webSocketClientService.addWebSocketMessageHandler(this);
executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
index 887ad37aa6..14a46fc7fb 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreTab.java
@@ -24,9 +24,10 @@
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.image.ImageView;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
+import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.snapshot.SnapshotTab;
-import org.phoebus.core.websocket.WebSocketMessage;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
import org.phoebus.ui.javafx.ImageCache;
@@ -36,7 +37,7 @@
/**
* Base class for save-n-restore {@link Tab}s containing common functionality.
*/
-public abstract class SaveAndRestoreTab extends Tab implements WebSocketMessageHandler {
+public abstract class SaveAndRestoreTab extends Tab implements SaveAndRestoreWebSocketMessageHandler{
protected SaveAndRestoreBaseController controller;
@@ -65,11 +66,14 @@ public SaveAndRestoreTab() {
setOnCloseRequest(event -> {
if (doCloseCheck()) {
+ SaveAndRestoreService.getInstance().removeSaveAndRestoreWebSocketMessageHandler(this);
handleTabClosed();
} else {
event.consume();
}
});
+
+ SaveAndRestoreService.getInstance().addSaveAndRestoreWebSocketMessageHandler(this);
}
/**
@@ -81,11 +85,6 @@ public void secureStoreChanged(List validTokens) {
controller.secureStoreChanged(validTokens);
}
- @Override
- public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
-
- }
-
/**
* Performs suitable cleanup, e.g. close web socket and PVs (where applicable).
*/
@@ -95,9 +94,21 @@ public void handleTabClosed() {
/**
* Checks if the tab may be closed, e.g. if data managed in the UI has been saved.
+ *
* @return false if tab contains unsaved data, otherwise true
*/
public boolean doCloseCheck() {
return controller.doCloseCheck();
}
+
+ @Override
+ public void handleSaveAndRestoreWebSocketMessage(WebSocketMessage> webSocketMessage){
+ if (webSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
+ String nodeId = (String) webSocketMessage.payload();
+ if (getId() != null && nodeId.equals(getId())) {
+ Platform.runLater(() -> getTabPane().getTabs().remove(this));
+ }
+ }
+ }
+
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
index ecb4fd96b8..5967350d36 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java
@@ -63,7 +63,7 @@
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreWebSocketMessageHandler;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.core.types.ProcessVariable;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.selection.SelectionService;
import org.phoebus.ui.application.ContextMenuHelper;
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
index 249f4138e6..5db38a5343 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationTab.java
@@ -18,21 +18,19 @@
*/
package org.phoebus.applications.saveandrestore.ui.configuration;
-import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.image.ImageView;
import org.phoebus.applications.saveandrestore.Messages;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
-public class ConfigurationTab extends SaveAndRestoreTab implements WebSocketMessageHandler {
+public class ConfigurationTab extends SaveAndRestoreTab {
public ConfigurationTab() {
try {
@@ -78,17 +76,4 @@ public void editConfiguration(Node configurationNode) {
public void configureForNewConfiguration(Node parentNode) {
((ConfigurationController) controller).newConfiguration(parentNode);
}
-
- @Override
- public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
- /*
- if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
- String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
- if (getId() != null && nodeId.equals(getId())) {
- Platform.runLater(() -> getTabPane().getTabs().remove(this));
- }
- }
-
- */
- }
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
index 909352e85f..4e00ab82e2 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java
@@ -56,7 +56,7 @@
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.security.tokens.ScopedAuthenticationToken;
import org.phoebus.ui.autocomplete.PVAutocompleteMenu;
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
index 3515514b0b..2c3d203a49 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchResultTableViewController.java
@@ -53,7 +53,7 @@
import org.phoebus.applications.saveandrestore.ui.contextmenu.TagGoldenMenuItem;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagUtil;
import org.phoebus.applications.saveandrestore.ui.snapshot.tag.TagWidget;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.workbench.ApplicationService;
import org.phoebus.ui.dialog.DialogHelper;
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
index edb75f32c7..e73d8321d6 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotController.java
@@ -64,7 +64,7 @@
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.ui.dialog.DialogHelper;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
index cc9aeed295..cf9f16c520 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/CompositeSnapshotTab.java
@@ -19,16 +19,13 @@
package org.phoebus.applications.saveandrestore.ui.snapshot;
-import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.image.ImageView;
import org.phoebus.applications.saveandrestore.Messages;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
import org.phoebus.applications.saveandrestore.ui.ImageRepository;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
@@ -47,8 +44,7 @@
* {@link SnapshotTab} is used to show actual snapshot data.
*
*/
-public class CompositeSnapshotTab extends SaveAndRestoreTab implements WebSocketMessageHandler {
-
+public class CompositeSnapshotTab extends SaveAndRestoreTab {
public CompositeSnapshotTab(SaveAndRestoreController saveAndRestoreController) {
@@ -94,8 +90,6 @@ public void configureForNewCompositeSnapshot(Node parentNode, List snapsho
* Configures UI to edit an existing composite snapshot {@link Node}
*
* @param compositeSnapshotNode non-null configuration {@link Node}
- * @param snapshotNodes A potentially empty (but non-null) list of snapshot nodes that should
- * be added to the list of references snapshots.
*/
public void editCompositeSnapshot(Node compositeSnapshotNode) {
((CompositeSnapshotController) controller).loadCompositeSnapshot(compositeSnapshotNode);
@@ -110,18 +104,4 @@ public void editCompositeSnapshot(Node compositeSnapshotNode) {
public void addToCompositeSnapshot(List snapshotNodes) {
((CompositeSnapshotController) controller).addToCompositeSnapshot(snapshotNodes);
}
-
- @Override
- public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
- /*
- if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
- String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
- if (getId() != null && nodeId.equals(getId())) {
- Platform.runLater(() -> getTabPane().getTabs().remove(this));
- }
- }
-
- */
- }
-
}
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
index 5f84935e4c..e8b53d4c7b 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java
@@ -86,7 +86,7 @@
import org.phoebus.applications.saveandrestore.ui.VTypePair;
import org.phoebus.core.types.TimeStampedProcessVariable;
import org.phoebus.core.vtypes.VDisconnectedData;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.framework.jobs.JobManager;
import org.phoebus.framework.selection.SelectionService;
import org.phoebus.saveandrestore.util.SnapshotUtil;
diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
index 66be135b29..63b5b5b885 100644
--- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
+++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTab.java
@@ -17,7 +17,6 @@
*/
package org.phoebus.applications.saveandrestore.ui.snapshot;
-import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
@@ -30,7 +29,6 @@
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
import org.phoebus.framework.nls.NLS;
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
import org.phoebus.ui.javafx.ImageCache;
@@ -49,7 +47,7 @@
* Note that this class is used also to show the snapshot view for {@link Node}s of type {@link NodeType#COMPOSITE_SNAPSHOT}.
*
*/
-public class SnapshotTab extends SaveAndRestoreTab implements WebSocketMessageHandler {
+public class SnapshotTab extends SaveAndRestoreTab {
public SaveAndRestoreService saveAndRestoreService;
@@ -137,17 +135,4 @@ public Node getSnapshotNode() {
public Node getConfigNode() {
return ((SnapshotController) controller).getConfigurationNode();
}
-
- @Override
- public void handleWebSocketMessage(String saveAndRestoreWebSocketMessage) {
- /*
- if (saveAndRestoreWebSocketMessage.messageType().equals(SaveAndRestoreMessageType.NODE_REMOVED)) {
- String nodeId = (String) saveAndRestoreWebSocketMessage.payload();
- if (getId() != null && nodeId.equals(getId())) {
- Platform.runLater(() -> getTabPane().getTabs().remove(this));
- }
- }
-
- */
- }
}
diff --git a/app/save-and-restore/model/pom.xml b/app/save-and-restore/model/pom.xml
index e0e6f67890..9034653ef4 100644
--- a/app/save-and-restore/model/pom.xml
+++ b/app/save-and-restore/model/pom.xml
@@ -19,7 +19,13 @@
org.phoebus
- core-websocket
+ core-websocket-common
+ 5.0.3-SNAPSHOT
+
+
+
+ org.phoebus
+ core-websocket-client
5.0.3-SNAPSHOT
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
index 58fb4b7fd5..abd666ec1c 100644
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreMessageType.java
@@ -4,7 +4,7 @@
package org.phoebus.applications.saveandrestore.model.websocket;
-import org.phoebus.core.websocket.MessageType;
+import org.phoebus.core.websocket.common.MessageType;
/**
* Enum to indicate what type of web socket message the service is sending to clients.
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
index 23ffae4f0d..77ea7d4fbe 100644
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializer.java
@@ -11,7 +11,7 @@
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.search.Filter;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
/**
* Custom JSON deserializer of {@link WebSocketMessage}s particular to save-and-restore.
@@ -31,7 +31,7 @@ public SaveAndRestoreWebSocketMessageDeserializer(Class> clazz) {
* @param context Context that can be used to access information about
* this deserialization activity.
* @return A {@link WebSocketMessage} object, or null if deserialization fails, e.g. due to
- * unknown/invalid {@link org.phoebus.core.websocket.MessageType} or null payload.
+ * unknown/invalid {@link org.phoebus.core.websocket.common.MessageType} or null payload.
*/
@Override
public WebSocketMessage> deserialize(JsonParser jsonParser, DeserializationContext context) {
diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
index 5f8a26b037..3a83cc476d 100644
--- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
+++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageHandler.java
@@ -4,7 +4,7 @@
package org.phoebus.applications.saveandrestore.model.websocket;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
/**
* Handler for web socket messages that have already been deserialized
diff --git a/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java b/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
index cf2e833ad3..8fd044000a 100644
--- a/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
+++ b/app/save-and-restore/model/src/test/java/org/phoebus/applications/saveandrestore/model/websocket/SaveAndRestoreWebSocketMessageDeserializerTest.java
@@ -10,7 +10,7 @@
import org.junit.jupiter.api.Test;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.search.Filter;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
diff --git a/core/websocket/client/pom.xml b/core/websocket/client/pom.xml
new file mode 100644
index 0000000000..9e5b8093f5
--- /dev/null
+++ b/core/websocket/client/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+ core-websocket-client
+
+
+ org.phoebus
+ core-websocket
+ 5.0.3-SNAPSHOT
+
+
+
+
+ org.phoebus
+ core-framework
+ 5.0.3-SNAPSHOT
+
+
+ org.phoebus
+ core-websocket-common
+ 5.0.3-SNAPSHOT
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+ ${spring.boot.version}
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.version}
+ test
+
+
+
+
\ No newline at end of file
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java b/core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketClientService.java
similarity index 97%
rename from core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
rename to core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketClientService.java
index 2397309935..8aecf35427 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/springframework/WebSocketClientService.java
+++ b/core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketClientService.java
@@ -2,9 +2,9 @@
* Copyright (C) 2025 European Spallation Source ERIC.
*/
-package org.phoebus.core.websocket.springframework;
+package org.phoebus.core.websocket.client;
-import org.phoebus.core.websocket.WebSocketMessageHandler;
+import org.phoebus.core.websocket.common.Constants;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.messaging.converter.StringMessageConverter;
@@ -66,7 +66,7 @@ public class WebSocketClientService {
*/
private final String connectUrl;
/**
- * Subscription endpoint, e.g. /Olog/web-socket/messages
+ * Subscription endpoint, e.g. /Olog/web-socket/messages or /save-restore/web-socket/messages
*/
private final String subscriptionEndpoint;
@@ -86,7 +86,7 @@ public WebSocketClientService(@NonNull String connectUrl) {
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
- this.subscriptionEndpoint = path + "/messages";
+ this.subscriptionEndpoint = path + Constants.MESSAGES;
}
/**
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java b/core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketMessageHandler.java
similarity index 63%
rename from core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java
rename to core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketMessageHandler.java
index b94acc9745..9876f5b4ee 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessageHandler.java
+++ b/core/websocket/client/src/main/java/org.phoebus.core.websocket.client/WebSocketMessageHandler.java
@@ -2,12 +2,8 @@
* Copyright (C) 2025 European Spallation Source ERIC.
*/
-package org.phoebus.core.websocket;
+package org.phoebus.core.websocket.client;
-/**
- * Handler for raw web socket string messages.
- */
public interface WebSocketMessageHandler {
-
void handleWebSocketMessage(String message);
}
diff --git a/core/websocket/common/pom.xml b/core/websocket/common/pom.xml
new file mode 100644
index 0000000000..eb3a988a63
--- /dev/null
+++ b/core/websocket/common/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+ core-websocket-common
+
+ org.phoebus
+ core-websocket
+ 5.0.3-SNAPSHOT
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.version}
+ test
+
+
+
\ No newline at end of file
diff --git a/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/Constants.java b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/Constants.java
new file mode 100644
index 0000000000..32ddd94b3e
--- /dev/null
+++ b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/Constants.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.core.websocket.common;
+
+/**
+ * Utility class to define string constants shared between client and service.
+ */
+public class Constants {
+
+ public static final String WEB_SOCKET = "/web-socket";
+ public static final String MESSAGES = "/messages";
+}
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/MessageType.java
similarity index 69%
rename from core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java
rename to core/websocket/common/src/main/java/org/phoebus/core/websocket/common/MessageType.java
index 27b93238ef..2c61070d65 100644
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/MessageType.java
+++ b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/MessageType.java
@@ -2,7 +2,7 @@
* Copyright (C) 2025 European Spallation Source ERIC.
*/
-package org.phoebus.core.websocket;
+package org.phoebus.core.websocket.common;
public interface MessageType {
}
diff --git a/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/WebSocketMessage.java b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/WebSocketMessage.java
new file mode 100644
index 0000000000..1c261f3fd0
--- /dev/null
+++ b/core/websocket/common/src/main/java/org/phoebus/core/websocket/common/WebSocketMessage.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2025 European Spallation Source ERIC.
+ */
+
+package org.phoebus.core.websocket.common;
+
+/**
+ * Record encapsulating a {@link MessageType} and a payload of arbitrary type.
+ *
+ * The deserialization process of a web socket message into a concrete {@link WebSocketMessage} must be
+ * delegated to a custom deserializer.
+ *
+ * @param messageType The {@link MessageType} of a web socket message. Apps can implement as needed.
+ * @param payload The payload like a {@link String}, or something more specific for the actual use case, e.g. a
+ * logbook entry or save-and-restore object.
+ */
+public record WebSocketMessage(MessageType messageType, T payload) {
+}
diff --git a/core/websocket/pom.xml b/core/websocket/pom.xml
index 2cddf9f45a..89003b9fed 100644
--- a/core/websocket/pom.xml
+++ b/core/websocket/pom.xml
@@ -6,36 +6,14 @@
4.0.0
core-websocket
${project.groupId}:${project.artifactId}
+ pom
org.phoebus
core
5.0.3-SNAPSHOT
-
-
-
- org.phoebus
- core-framework
- 5.0.3-SNAPSHOT
-
-
- org.springframework
- spring-websocket
- ${spring.framework.version}
-
-
- org.springframework
- spring-messaging
- ${spring.framework.version}
-
-
-
- org.junit.jupiter
- junit-jupiter
- ${junit.version}
- test
-
-
-
-
+
+ common
+ client
+
diff --git a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java b/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
deleted file mode 100644
index 7cb366197a..0000000000
--- a/core/websocket/src/main/java/org/phoebus/core/websocket/WebSocketMessage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 European Spallation Source ERIC.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package org.phoebus.core.websocket;
-
-/**
- * Record encapsulating a {@link MessageType} and a payload of arbitrary type.
- *
- * The deserialization process of a web socket message into a concrete {@link WebSocketMessage} must be
- * delegated to a custom deserializer.
- *
- * @param messageType The {@link MessageType} of a web socket message. Apps can implement as needed.
- * @param payload The payload like a {@link String}, or something more specific for the actual use case, e.g. a
- * logbook entry or save-and-restore object.
- */
-public record WebSocketMessage(MessageType messageType, T payload) {
-}
diff --git a/dependencies/phoebus-target/pom.xml b/dependencies/phoebus-target/pom.xml
index 4f7543c0cd..6b141c726a 100644
--- a/dependencies/phoebus-target/pom.xml
+++ b/dependencies/phoebus-target/pom.xml
@@ -8,7 +8,6 @@
phoebus-target
${project.groupId}:${project.artifactId}
- 2.7.3
${project.basedir}/release_classpath.py
@@ -444,7 +443,7 @@
org.springframework.boot
spring-boot-starter
- ${spring.boot-version}
+ ${spring.boot.version}
org.springframework.boot
@@ -455,23 +454,20 @@
org.springframework.boot
spring-boot-starter-web
- ${spring.boot-version}
+ ${spring.boot.version}
+
org.springframework.boot
spring-boot-starter-test
- ${spring.boot-version}
+ ${spring.boot.version}
test
+
- org.springframework
- spring-websocket
- ${spring.framework.version}
-
-
- org.springframework
- spring-messaging
- ${spring.framework.version}
+ org.springframework.boot
+ spring-boot-starter-websocket
+ ${spring.boot.version}
diff --git a/pom.xml b/pom.xml
index 242479548b..7e46e34688 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,7 @@
5.18.4
1.26.1
5.3.22
+ 2.7.3
diff --git a/services/alarm-config-logger/pom.xml b/services/alarm-config-logger/pom.xml
index d3b483faa7..deca1db494 100644
--- a/services/alarm-config-logger/pom.xml
+++ b/services/alarm-config-logger/pom.xml
@@ -6,7 +6,6 @@
5.0.3-SNAPSHOT
- 2.7.3
17
service-alarm-config-logger
@@ -17,7 +16,7 @@
org.springframework.boot
spring-boot-dependencies
- ${spring.boot-version}
+ ${spring.boot.version}
pom
import
diff --git a/services/alarm-logger/pom.xml b/services/alarm-logger/pom.xml
index 5980d534f9..83fd85448c 100644
--- a/services/alarm-logger/pom.xml
+++ b/services/alarm-logger/pom.xml
@@ -7,7 +7,6 @@
17
- 2.7.3
8.2.0
service-alarm-logger
@@ -18,7 +17,7 @@
org.springframework.boot
spring-boot-dependencies
- ${spring.boot-version}
+ ${spring.boot.version}
pom
import
diff --git a/services/save-and-restore/pom.xml b/services/save-and-restore/pom.xml
index c951110900..df45fbee11 100644
--- a/services/save-and-restore/pom.xml
+++ b/services/save-and-restore/pom.xml
@@ -13,7 +13,6 @@
5.0.3-SNAPSHOT
- 2.7.3
4.4
8.2.0
@@ -46,11 +45,6 @@
spring-boot-starter-data-elasticsearch
-
- org.springframework.boot
- spring-boot-starter-websocket
-
-
co.elastic.clients
elasticsearch-java
@@ -88,7 +82,7 @@
org.phoebus
- core-websocket
+ core-websocket-common
5.0.3-SNAPSHOT
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
index 3645f194a2..63fe8a2dc1 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebConfiguration.java
@@ -22,7 +22,6 @@
import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@@ -84,8 +83,13 @@ public ExecutorService executorService() {
return Executors.newCachedThreadPool();
}
+ /**
+ *
+ * @return The context path if other than "/", otherwise empty string.
+ */
@Bean
- public String contextPath(){
- return servletContext.getContextPath();
+ public String context() {
+ return servletContext.getContextPath().length() > 1 ?
+ servletContext.getContextPath() : "";
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
index bfa54a96c4..2d23143353 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java
@@ -25,7 +25,7 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
index 83dfe14a37..671fde1032 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java
@@ -22,7 +22,7 @@
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
index e1a00c39c8..c99b96ec28 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/FilterController.java
@@ -21,7 +21,7 @@
import org.phoebus.applications.saveandrestore.model.search.Filter;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
index 1e5ad28032..87281910cd 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java
@@ -21,7 +21,7 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Tag;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
index 166382fe35..6e124b9780 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java
@@ -22,7 +22,7 @@
import org.phoebus.applications.saveandrestore.model.Snapshot;
import org.phoebus.applications.saveandrestore.model.SnapshotData;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
index 2fdf5641bc..e5fb1a0c63 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java
@@ -19,7 +19,7 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
index 6fb2d0c35a..d9b87093a3 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java
@@ -25,7 +25,7 @@
import org.phoebus.applications.saveandrestore.model.Tag;
import org.phoebus.applications.saveandrestore.model.TagData;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
index 2969c5f95c..28e6153357 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketConfig.java
@@ -18,6 +18,7 @@
package org.phoebus.service.saveandrestore.websocket;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.phoebus.core.websocket.common.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@@ -27,7 +28,7 @@
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
-import javax.servlet.ServletContext;
+import java.util.logging.Level;
import java.util.logging.Logger;
@SuppressWarnings("unused")
@@ -39,7 +40,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public ObjectMapper objectMapper;
@Autowired
- private String contextPath;
+ private String context;
private final Logger logger = Logger.getLogger(WebSocketConfig.class.getName());
@@ -52,14 +53,14 @@ public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
- String destination = contextPath.length() > 1 ? contextPath + "/web-socket/messages" : "/web-socket/messages";
- config.enableSimpleBroker(destination)
+ logger.log(Level.INFO, "Configuring message broker using path " + context + Constants.WEB_SOCKET + Constants.MESSAGES);
+ config.enableSimpleBroker(context + Constants.WEB_SOCKET + Constants.MESSAGES)
.setHeartbeatValue(new long[]{30000, 30000})
.setTaskScheduler(this.messageBrokerTaskScheduler);
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
- registry.addEndpoint("/web-socket");
+ registry.addEndpoint(Constants.WEB_SOCKET);
}
}
diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
index 7a7b4afc36..e13252fb8d 100644
--- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
+++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/websocket/WebSocketService.java
@@ -26,7 +26,7 @@
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.stereotype.Service;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import java.util.logging.Level;
@@ -48,7 +48,7 @@ public class WebSocketService {
private ObjectMapper objectMapper;
@Autowired
- private String contextPath;
+ private String context;
private static final Logger logger = Logger.getLogger(WebSocketService.class.getName());
@@ -59,8 +59,7 @@ public class WebSocketService {
public void sendMessageToClients(@NonNull WebSocketMessage> webSocketMessage) {
try {
String message = objectMapper.writeValueAsString(webSocketMessage);
- String messageEndpoint = contextPath.length() > 0 ? contextPath : "";
- simpMessagingTemplate.convertAndSend(messageEndpoint + "/web-socket/messages", message);
+ simpMessagingTemplate.convertAndSend(context + "/web-socket/messages", message);
} catch (JsonProcessingException e) {
logger.log(Level.WARNING, "Failed to write web socket message to json string", e);
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
index b895a7ead7..a6e5bc57af 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/config/ControllersTestConfig.java
@@ -29,17 +29,17 @@
import org.phoebus.service.saveandrestore.search.SearchUtil;
import org.phoebus.service.saveandrestore.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.user.SimpUserRegistry;
+import org.springframework.mock.web.MockServletContext;
import org.springframework.util.Base64Utils;
import org.springframework.web.socket.WebSocketSession;
+import javax.servlet.ServletContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -145,12 +145,12 @@ public WebSocketService webSocketService() {
}
@Bean
- public SimpMessagingTemplate simpMessagingTemplate(){
+ public SimpMessagingTemplate simpMessagingTemplate() {
return Mockito.mock(SimpMessagingTemplate.class);
}
@Bean
- public SimpUserRegistry simpUserRegistry(){
+ public SimpUserRegistry simpUserRegistry() {
return Mockito.mock(SimpUserRegistry.class);
}
@@ -159,4 +159,17 @@ public long connectionTimeout() {
return 5000;
}
+ @Bean
+ public ServletContext servletContext() {
+ MockServletContext mockServletContext = new MockServletContext();
+ mockServletContext.setContextPath("/");
+ return mockServletContext;
+ }
+
+ @Bean
+ public String context() {
+ return servletContext().getContextPath().length() > 1 ?
+ servletContext().getContextPath() : "";
+ }
+
}
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
index 63c0cd57f3..a11883801d 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java
@@ -31,7 +31,7 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.SnapshotItem;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
@@ -235,7 +235,7 @@ public void testUpdateCompositeSnapshot2() throws Exception {
mockMvc.perform(request).andExpect(status().isForbidden());
- verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(org.phoebus.core.websocket.WebSocketMessage.class));
+ verify(webSocketService, times(0)).sendMessageToClients(Mockito.any(WebSocketMessage.class));
}
@Test
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
index b5675c5f8c..32e517f171 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java
@@ -31,7 +31,7 @@
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
index 6c935aff62..51258ebffe 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerTest.java
@@ -25,7 +25,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.phoebus.applications.saveandrestore.model.search.Filter;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
index 35823cc37c..e0f4f8b6b2 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java
@@ -35,7 +35,7 @@
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Tag;
import org.phoebus.applications.saveandrestore.model.websocket.SaveAndRestoreMessageType;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.NodeNotFoundException;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
index 089196e22a..9b956fe7a3 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java
@@ -28,7 +28,7 @@
import org.phoebus.applications.saveandrestore.model.Node;
import org.phoebus.applications.saveandrestore.model.NodeType;
import org.phoebus.applications.saveandrestore.model.Snapshot;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;
diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
index 6c216efc55..bfc64a40fc 100644
--- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
+++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java
@@ -25,7 +25,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.phoebus.applications.saveandrestore.model.Node;
-import org.phoebus.core.websocket.WebSocketMessage;
+import org.phoebus.core.websocket.common.WebSocketMessage;
import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO;
import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig;
import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig;