From 9a59fb9a20109da336ad4c4b21768d70ecc4ce6b Mon Sep 17 00:00:00 2001 From: georgweiss Date: Wed, 15 May 2024 15:34:34 +0200 Subject: [PATCH 1/7] Move save&restore class to common package --- .../saveandrestore/model}/RestoreResult.java | 4 +-- .../epics/SnapshotRestorer.java | 1 + .../SnapshotRestoreController.java | 30 ++++++++----------- .../SnapshotRestorerControllerTest.java | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) rename {services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics => app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model}/RestoreResult.java (80%) diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/RestoreResult.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/RestoreResult.java similarity index 80% rename from services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/RestoreResult.java rename to app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/RestoreResult.java index aa5d6b5cd5..cab1d697d8 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/RestoreResult.java +++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/RestoreResult.java @@ -1,9 +1,7 @@ -package org.phoebus.service.saveandrestore.epics; +package org.phoebus.applications.saveandrestore.model; import java.io.Serializable; -import org.phoebus.applications.saveandrestore.model.SnapshotItem; - public class RestoreResult implements Serializable { private SnapshotItem snapshotItem; private String errorMsg; diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java index 6f0ff6a2ae..59f6fa8a02 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java @@ -28,6 +28,7 @@ import org.epics.vtype.VULongArray; import org.epics.vtype.VUShortArray; +import org.phoebus.applications.saveandrestore.model.RestoreResult; import org.phoebus.framework.preferences.PropertyPreferenceLoader; import org.phoebus.applications.saveandrestore.model.SnapshotItem; import org.phoebus.core.vtypes.VTypeHelper; diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java index e60e7e664a..2434b7784a 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java @@ -17,28 +17,21 @@ */ package org.phoebus.service.saveandrestore.web.controllers; -import org.phoebus.applications.saveandrestore.model.CompositeSnapshot; -import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData; -import org.phoebus.applications.saveandrestore.model.ConfigPv; -import org.phoebus.applications.saveandrestore.model.Node; -import org.phoebus.applications.saveandrestore.model.Snapshot; -import org.phoebus.applications.saveandrestore.model.SnapshotData; +import org.phoebus.applications.saveandrestore.model.RestoreResult; import org.phoebus.applications.saveandrestore.model.SnapshotItem; import org.phoebus.service.saveandrestore.epics.SnapshotRestorer; -import org.phoebus.service.saveandrestore.epics.RestoreResult; import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -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.util.List; +/** + * {@link RestController} performing server-side restore operation. + */ @SuppressWarnings("unused") @RestController public class SnapshotRestoreController extends BaseController { @@ -48,16 +41,17 @@ public class SnapshotRestoreController extends BaseController { @PostMapping(value = "/restore/items", produces = JSON) public List restoreFromSnapshotItems( - @RequestBody List snapshotItems) throws Exception { - var snapshotRestorer = new SnapshotRestorer(); - return snapshotRestorer.restorePVValues(snapshotItems); + @RequestBody List snapshotItems) throws Exception { + var snapshotRestorer = new SnapshotRestorer(); + return snapshotRestorer.restorePVValues(snapshotItems); } + @PostMapping(value = "/restore/node", produces = JSON) public List restoreFromSnapshotNode( - @RequestParam(value = "parentNodeId") String parentNodeId) throws Exception { - var snapshotRestorer = new SnapshotRestorer(); - var snapshot = nodeDAO.getSnapshotData(parentNodeId); - return snapshotRestorer.restorePVValues(snapshot.getSnapshotItems()); + @RequestParam(value = "parentNodeId") String parentNodeId) throws Exception { + var snapshotRestorer = new SnapshotRestorer(); + var snapshot = nodeDAO.getSnapshotData(parentNodeId); + return snapshotRestorer.restorePVValues(snapshot.getSnapshotItems()); } } 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 4182abedaf..0be8ead7d0 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 @@ -12,10 +12,10 @@ import org.phoebus.applications.saveandrestore.model.ConfigPv; import org.phoebus.applications.saveandrestore.model.Node; import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.applications.saveandrestore.model.RestoreResult; import org.phoebus.applications.saveandrestore.model.Snapshot; import org.phoebus.applications.saveandrestore.model.SnapshotData; import org.phoebus.applications.saveandrestore.model.SnapshotItem; -import org.phoebus.service.saveandrestore.epics.RestoreResult; import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; import org.springframework.beans.factory.annotation.Autowired; From dce56d5396d59c3f83a8c300fff15051a438bcde Mon Sep 17 00:00:00 2001 From: georgweiss Date: Wed, 15 May 2024 15:43:59 +0200 Subject: [PATCH 2/7] Add client side REST calls for server-side restore --- .../client/SaveAndRestoreClient.java | 4 ++ .../client/SaveAndRestoreJerseyClient.java | 51 +++++++++++++++++++ .../SnapshotRestoreController.java | 4 +- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java index a95994721d..29c51a5913 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java @@ -231,4 +231,8 @@ public interface SaveAndRestoreClient { * an exception. */ UserData authenticate(String userName, String password); + + List restore(List snapshotItems); + + List restore(String snapshotNodeId); } diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java index 53287edd87..7fd7d1ff8e 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java @@ -638,4 +638,55 @@ public UserData authenticate(String userName, String password) { return response.getEntity(new GenericType<>() { }); } + + @Override + public List restore(List snapshotItems){ + WebResource webResource = + getClient().resource(Preferences.jmasarServiceUrl + "/restore/items"); + ClientResponse response; + try { + response = webResource.accept(CONTENT_TYPE_JSON) + .entity(snapshotItems, CONTENT_TYPE_JSON) + .post(ClientResponse.class); + } catch (UniformInterfaceException e) { + throw new RuntimeException(e); + } + if (response.getStatus() != ClientResponse.Status.OK.getStatusCode()) { + String message = "Restore failed"; + try { + message = new String(response.getEntityInputStream().readAllBytes()); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to parse response", e); + } + throw new SaveAndRestoreClientException(message); + } + return response.getEntity(new GenericType<>() { + }); + } + + + public List restore(String snapshotNodeId){ + WebResource webResource = + getClient() + .resource(Preferences.jmasarServiceUrl + "/restore/node") + .queryParam("codeId", snapshotNodeId); + ClientResponse response; + try { + response = webResource.accept(CONTENT_TYPE_JSON) + .post(ClientResponse.class); + } catch (UniformInterfaceException e) { + throw new RuntimeException(e); + } + if (response.getStatus() != ClientResponse.Status.OK.getStatusCode()) { + String message = "Restore failed"; + try { + message = new String(response.getEntityInputStream().readAllBytes()); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to parse response", e); + } + throw new SaveAndRestoreClientException(message); + } + return response.getEntity(new GenericType<>() { + }); + } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java index 2434b7784a..1337b10a91 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java @@ -48,9 +48,9 @@ public List restoreFromSnapshotItems( @PostMapping(value = "/restore/node", produces = JSON) public List restoreFromSnapshotNode( - @RequestParam(value = "parentNodeId") String parentNodeId) throws Exception { + @RequestParam(value = "nodeId") String nodeId) throws Exception { var snapshotRestorer = new SnapshotRestorer(); - var snapshot = nodeDAO.getSnapshotData(parentNodeId); + var snapshot = nodeDAO.getSnapshotData(nodeId); return snapshotRestorer.restorePVValues(snapshot.getSnapshotItems()); } From 3771215c4bc8ed86c2958e48d82d8459d288430f Mon Sep 17 00:00:00 2001 From: georgweiss Date: Wed, 15 May 2024 19:45:06 +0200 Subject: [PATCH 3/7] Client API for restore snapshot on service --- .../client/SaveAndRestoreClient.java | 10 +++++++++ .../ui/SaveAndRestoreService.java | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java index 29c51a5913..4a8d999a43 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java @@ -232,7 +232,17 @@ public interface SaveAndRestoreClient { */ UserData authenticate(String userName, String password); + /** + * Requests service to restore the specified {@link SnapshotItem}s + * @param snapshotItems A {@link List} of {@link SnapshotItem}s + * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + */ List restore(List snapshotItems); + /** + * Requests service to restore the specified snapshot. + * @param snapshotNodeId Unique id of a snapshot + * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + */ List restore(String snapshotNodeId); } 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 fee17559ba..540d0ac48f 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 @@ -394,4 +394,26 @@ public UserData authenticate(String userName, String password) throws Exception{ executor.submit(() -> saveAndRestoreClient.authenticate(userName, password)); return future.get(); } + + /** + * Requests service to restore the specified {@link SnapshotItem}s + * @param snapshotItems A {@link List} of {@link SnapshotItem}s + * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + */ + public List restore(List snapshotItems) throws Exception{ + Future> future = + executor.submit(() -> saveAndRestoreClient.restore(snapshotItems)); + return future.get(); + } + + /** + * Requests service to restore the specified snapshot. + * @param snapshotNodeId Unique id of a snapshot + * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + */ + public List restore(String snapshotNodeId) throws Exception{ + Future> future = + executor.submit(() -> saveAndRestoreClient.restore(snapshotNodeId)); + return future.get(); + } } From 9e59351233d26f43db2bf2604f1fb36cc8a12fb4 Mon Sep 17 00:00:00 2001 From: georgweiss Date: Thu, 16 May 2024 11:32:10 +0200 Subject: [PATCH 4/7] Client-side integration of server-side restore operation --- services/save-and-restore/pom.xml | 15 +++ .../epics/SnapshotRestorer.java | 102 ++++-------------- .../web/config/WebConfiguration.java | 9 ++ .../SnapshotRestoreController.java | 9 +- 4 files changed, 49 insertions(+), 86 deletions(-) diff --git a/services/save-and-restore/pom.xml b/services/save-and-restore/pom.xml index 708523fe2c..da39500453 100644 --- a/services/save-and-restore/pom.xml +++ b/services/save-and-restore/pom.xml @@ -74,6 +74,21 @@ core-pv 4.7.4-SNAPSHOT + + org.phoebus + core-pv-pva + 4.7.4-SNAPSHOT + + + org.phoebus + core-pv-ca + 4.7.4-SNAPSHOT + + + org.phoebus + core-vtype + 4.7.4-SNAPSHOT + org.springframework.boot diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java index 59f6fa8a02..41c0f62c51 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java @@ -1,51 +1,33 @@ package org.phoebus.service.saveandrestore.epics; +import org.phoebus.applications.saveandrestore.model.RestoreResult; +import org.phoebus.applications.saveandrestore.model.SnapshotItem; +import org.phoebus.core.vtypes.VTypeHelper; +import org.phoebus.framework.preferences.PropertyPreferenceLoader; +import org.phoebus.pv.PV; +import org.phoebus.pv.PVPool; + import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import org.epics.pva.client.PVAClient; -import org.epics.vtype.VBoolean; -import org.epics.vtype.VBooleanArray; -import org.epics.vtype.VByteArray; -import org.epics.vtype.VDoubleArray; -import org.epics.vtype.VEnum; -import org.epics.vtype.VEnumArray; -import org.epics.vtype.VFloatArray; -import org.epics.vtype.VIntArray; -import org.epics.vtype.VLongArray; -import org.epics.vtype.VNumber; -import org.epics.vtype.VNumberArray; -import org.epics.vtype.VShortArray; -import org.epics.vtype.VString; -import org.epics.vtype.VStringArray; -import org.epics.vtype.VType; -import org.epics.vtype.VUByteArray; -import org.epics.vtype.VUIntArray; -import org.epics.vtype.VULongArray; -import org.epics.vtype.VUShortArray; - -import org.phoebus.applications.saveandrestore.model.RestoreResult; -import org.phoebus.framework.preferences.PropertyPreferenceLoader; -import org.phoebus.applications.saveandrestore.model.SnapshotItem; -import org.phoebus.core.vtypes.VTypeHelper; -import org.phoebus.pv.PV; -import org.phoebus.pv.PVPool; - public class SnapshotRestorer { - PVAClient pva; private final Logger LOG = Logger.getLogger(SnapshotRestorer.class.getName()); - public SnapshotRestorer() throws Exception { - pva = new PVAClient(); + public SnapshotRestorer() { final File site_settings = new File("settings.ini"); if (site_settings.canRead()) { LOG.config("Loading settings from " + site_settings); - PropertyPreferenceLoader.load(new FileInputStream(site_settings)); + try { + PropertyPreferenceLoader.load(new FileInputStream(site_settings)); + } catch (Exception e) { + LOG.log(Level.WARNING, "Unable to read settings.ini, falling back to default values."); + } } } @@ -53,9 +35,9 @@ public SnapshotRestorer() throws Exception { * Restore PV values from a list of snapshot items * *

- * Writes concurrently the pv value to the non null set PVs in + * Writes concurrently the pv value to the non-null set PVs in * the snapshot items. - * Uses synchonized to ensure only one frontend can write at a time. + * Uses synchronized to ensure only one frontend can write at a time. * Returns a list of the snapshot items you have set, with an error message if * an error occurred. * @@ -64,11 +46,11 @@ public SnapshotRestorer() throws Exception { public synchronized List restorePVValues(List snapshotItems) { var futures = snapshotItems.stream().filter( - (snapshot_item) -> snapshot_item.getConfigPv().getPvName() != null) + (snapshot_item) -> snapshot_item.getConfigPv().getPvName() != null) .map((snapshotItem) -> { var pvName = snapshotItem.getConfigPv().getPvName(); var pvValue = snapshotItem.getValue(); - Object rawValue = vTypeToObject(pvValue); + Object rawValue = VTypeHelper.toObject(pvValue); PV pv; CompletableFuture future; try { @@ -96,7 +78,7 @@ public synchronized List restorePVValues(List snaps return restoreResult; }); }) - .collect(Collectors.toList()); + .toList(); CompletableFuture all_done = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); @@ -104,50 +86,6 @@ public synchronized List restorePVValues(List snaps all_done.join(); // Joins should not block as all the futures should be completed. - return futures.stream().map( - (future) -> future.join()).collect(Collectors.toList()); - } - - /** - * Convert a vType to its Object representation - * - * @param type {@link VType} - */ - private Object vTypeToObject(VType type) { - if (type == null) { - return null; - } - if (type instanceof VNumberArray) { - if (type instanceof VIntArray || type instanceof VUIntArray) { - return VTypeHelper.toIntegers(type); - } else if (type instanceof VDoubleArray) { - return VTypeHelper.toDoubles(type); - } else if (type instanceof VFloatArray) { - return VTypeHelper.toFloats(type); - } else if (type instanceof VLongArray || type instanceof VULongArray) { - return VTypeHelper.toLongs(type); - } else if (type instanceof VShortArray || type instanceof VUShortArray) { - return VTypeHelper.toShorts(type); - } else if (type instanceof VByteArray || type instanceof VUByteArray) { - return VTypeHelper.toBytes(type); - } - } else if (type instanceof VEnumArray) { - List data = ((VEnumArray) type).getData(); - return data.toArray(new String[data.size()]); - } else if (type instanceof VStringArray) { - List data = ((VStringArray) type).getData(); - return data.toArray(new String[data.size()]); - } else if (type instanceof VBooleanArray) { - return VTypeHelper.toBooleans(type); - } else if (type instanceof VNumber) { - return ((VNumber) type).getValue(); - } else if (type instanceof VEnum) { - return ((VEnum) type).getIndex(); - } else if (type instanceof VString) { - return ((VString) type).getValue(); - } else if (type instanceof VBoolean) { - return ((VBoolean) type).getValue(); - } - return null; + return futures.stream().map(CompletableFuture::join).collect(Collectors.toList()); } } 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 e44a8ef857..358721fd70 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 @@ -17,10 +17,12 @@ */ package org.phoebus.service.saveandrestore.web.config; +import org.phoebus.service.saveandrestore.epics.SnapshotRestorer; import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchDAO; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; /** * {@link Configuration} class setting up beans for {@link org.springframework.stereotype.Controller} classes. @@ -47,4 +49,11 @@ public NodeDAO nodeDAO() { public AcceptHeaderResolver acceptHeaderResolver() { return new AcceptHeaderResolver(); } + + @SuppressWarnings("unused") + @Bean + @Scope("singleton") + public SnapshotRestorer snapshotRestorer(){ + return new SnapshotRestorer(); + } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java index 1337b10a91..9a51d9647a 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestoreController.java @@ -39,17 +39,18 @@ public class SnapshotRestoreController extends BaseController { @Autowired private NodeDAO nodeDAO; + @Autowired + private SnapshotRestorer snapshotRestorer; + @PostMapping(value = "/restore/items", produces = JSON) public List restoreFromSnapshotItems( - @RequestBody List snapshotItems) throws Exception { - var snapshotRestorer = new SnapshotRestorer(); + @RequestBody List snapshotItems) { return snapshotRestorer.restorePVValues(snapshotItems); } @PostMapping(value = "/restore/node", produces = JSON) public List restoreFromSnapshotNode( - @RequestParam(value = "nodeId") String nodeId) throws Exception { - var snapshotRestorer = new SnapshotRestorer(); + @RequestParam(value = "nodeId") String nodeId){ var snapshot = nodeDAO.getSnapshotData(nodeId); return snapshotRestorer.restorePVValues(snapshot.getSnapshotItems()); } From 779a76d25ba772c14d6751dd40ec9d263e9fe29f Mon Sep 17 00:00:00 2001 From: georgweiss Date: Fri, 17 May 2024 11:26:02 +0200 Subject: [PATCH 5/7] Server-side restore improvements, client-side UI improvements --- .../model/SaveAndRestorePv.java | 77 ---------- .../epics/SnapshotRestorer.java | 144 +++++++++++++----- .../web/config/WebConfiguration.java | 9 ++ .../src/main/resources/application.properties | 3 + 4 files changed, 116 insertions(+), 117 deletions(-) delete mode 100644 app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/SaveAndRestorePv.java diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/SaveAndRestorePv.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/SaveAndRestorePv.java deleted file mode 100644 index 06c053ed21..0000000000 --- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/SaveAndRestorePv.java +++ /dev/null @@ -1,77 +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.applications.saveandrestore.model; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.epics.vtype.VType; -import org.phoebus.applications.saveandrestore.model.json.VTypeDeserializer; -import org.phoebus.applications.saveandrestore.model.json.VTypeSerializer; - -public class SaveAndRestorePv { - - private String pvName; - - @JsonSerialize(using = VTypeSerializer.class) - @JsonDeserialize(using = VTypeDeserializer.class) - private VType value; - - public String getPvName() { - return pvName; - } - - public void setPvName(String pvName) { - this.pvName = pvName; - } - - public VType getValue() { - return value; - } - - public void setValue(VType value) { - this.value = value; - } - - public static Builder builder(){ - return new Builder(); - } - - public static class Builder{ - private SaveAndRestorePv saveAndRestorePv; - - private Builder(){ - saveAndRestorePv = new SaveAndRestorePv(); - } - - public Builder pvName(String pvName){ - saveAndRestorePv.setPvName(pvName); - return this; - } - - public Builder value(VType value){ - saveAndRestorePv.setValue(value); - return this; - } - - public SaveAndRestorePv build(){ - return saveAndRestorePv; - } - } -} diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java index 41c0f62c51..3968a5e9b3 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorer.java @@ -6,19 +6,35 @@ import org.phoebus.framework.preferences.PropertyPreferenceLoader; import org.phoebus.pv.PV; import org.phoebus.pv.PVPool; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import java.io.File; import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.concurrent.CompletableFuture; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; public class SnapshotRestorer { private final Logger LOG = Logger.getLogger(SnapshotRestorer.class.getName()); + @Value("${connection.timeout:5000}") + private int connectionTimeout; + + @Value("${write.timeout:5000}") + private int writeTimeout; + + @Autowired + private ExecutorService executorService; + public SnapshotRestorer() { final File site_settings = new File("settings.ini"); if (site_settings.canRead()) { @@ -44,48 +60,96 @@ public SnapshotRestorer() { * @param snapshotItems {@link SnapshotItem} */ public synchronized List restorePVValues(List snapshotItems) { + // Attempt to connect to all PVs before trying to write/restore. + List connectedPvs = connectPVs(snapshotItems); + List failedPvs = new ArrayList<>(); + final CountDownLatch countDownLatch = new CountDownLatch(snapshotItems.size()); + snapshotItems.forEach(item -> { + String pvName = item.getConfigPv().getPvName(); + Optional pvOptional = null; + try { + // Check if PV is connected. If not, do not even try to write/restore. + pvOptional = connectedPvs.stream().filter(pv -> pv.getName().equals(pvName)).findFirst(); + if (pvOptional.isPresent()) { + pvOptional.get().write(VTypeHelper.toObject(item.getValue())); + } else { + RestoreResult restoreResult = new RestoreResult(); + restoreResult.setSnapshotItem(item); + restoreResult.setErrorMsg("PV disconnected"); + failedPvs.add(restoreResult); + } + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to restore PV " + pvName); + RestoreResult restoreResult = new RestoreResult(); + restoreResult.setSnapshotItem(item); + restoreResult.setErrorMsg(e.getMessage()); + failedPvs.add(restoreResult); + } finally { + if (pvOptional != null && pvOptional.isPresent()) { + PVPool.releasePV(pvOptional.get()); + } + countDownLatch.countDown(); + } + }); + + try { + countDownLatch.await(writeTimeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + LOG.log(Level.INFO, "Encountered InterruptedException", e); + } + + return failedPvs; + } - var futures = snapshotItems.stream().filter( - (snapshot_item) -> snapshot_item.getConfigPv().getPvName() != null) - .map((snapshotItem) -> { - var pvName = snapshotItem.getConfigPv().getPvName(); - var pvValue = snapshotItem.getValue(); - Object rawValue = VTypeHelper.toObject(pvValue); - PV pv; - CompletableFuture future; - try { - pv = PVPool.getPV(pvName); - future = pv.asyncWrite(rawValue); - } catch (Exception e) { - var restoreResult = new RestoreResult(); - var errorMsg = e.getMessage(); - restoreResult.setSnapshotItem(snapshotItem); - restoreResult.setErrorMsg(errorMsg); - LOG.warning(String.format("Error writing to channel %s %s", pvName, errorMsg)); - return CompletableFuture.completedFuture(restoreResult); - } - return future.handle((result, ex) -> { - String errorMsg; - if (ex != null) { - errorMsg = ex.getMessage(); - LOG.warning(String.format("Error writing to channel %s %s", pvName, errorMsg)); - } else { - errorMsg = null; + /** + * Attempts to connect to all PVs using {@link PVPool}. A connection is considered successful once an + * event is received that does not indicate disconnection. + * + *

+ * A timeout of {@link #connectionTimeout} ms is used to wait for a PV to supply a value message indicating + * successful connection. + *

+ *

+ * An {@link ExecutorService} is used to run connection attempts concurrently. However, no timeout is employed + * for the overall execution of all connection attempts. + *

+ * + * @param snapshotItems List of {@link SnapshotItem}s in a snapshot. + * @return A {@link List} of {@link PV}s for which connection succeeded. Ideally this should be all + * PVs as listed in the input argument. + */ + private List connectPVs(List snapshotItems) { + List connectedPvs = new ArrayList<>(); + + List> callables = snapshotItems.stream().map(snapshotItem -> { + return (Callable) () -> { + CountDownLatch countDownLatch = new CountDownLatch(1); + try { + PV pv = PVPool.getPV(snapshotItem.getConfigPv().getPvName()); + pv.onValueEvent().subscribe(value -> { + if (!PV.isDisconnected(value)) { + connectedPvs.add(pv); + countDownLatch.countDown(); } - var restoreResult = new RestoreResult(); - restoreResult.setSnapshotItem(snapshotItem); - restoreResult.setErrorMsg(errorMsg); - return restoreResult; }); - }) - .toList(); + // Wait for a value message indicating connection + countDownLatch.await(connectionTimeout, TimeUnit.MILLISECONDS); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to connect to PV " + snapshotItem.getConfigPv().getPvName(), e); + countDownLatch.countDown(); + } + return null; + }; + }).toList(); - CompletableFuture all_done = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); - - // Wait on the futures concurrently - all_done.join(); + try { + executorService.invokeAll(callables); + } catch (InterruptedException e) { + LOG.log(Level.WARNING, "Got exception waiting for all tasks to finish", e); + // Return empty list here? + return Collections.emptyList(); + } - // Joins should not block as all the futures should be completed. - return futures.stream().map(CompletableFuture::join).collect(Collectors.toList()); + return connectedPvs; } } 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 358721fd70..48087c22fa 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 @@ -24,6 +24,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + /** * {@link Configuration} class setting up beans for {@link org.springframework.stereotype.Controller} classes. */ @@ -56,4 +60,9 @@ public AcceptHeaderResolver acceptHeaderResolver() { public SnapshotRestorer snapshotRestorer(){ return new SnapshotRestorer(); } + + @Bean + public ExecutorService executorService(){ + return Executors.newCachedThreadPool(); + } } diff --git a/services/save-and-restore/src/main/resources/application.properties b/services/save-and-restore/src/main/resources/application.properties index 525552efcb..6ad52e731e 100644 --- a/services/save-and-restore/src/main/resources/application.properties +++ b/services/save-and-restore/src/main/resources/application.properties @@ -92,3 +92,6 @@ authorization.permitall = true role.user=sar-user role.admin=sar-admin +############## EPICS related ################# +connection.timeout=5000 +write.timout=5000 From 6b6166c34cbeb322ec4bb614de1b56c66562354c Mon Sep 17 00:00:00 2001 From: georgweiss Date: Fri, 17 May 2024 11:38:33 +0200 Subject: [PATCH 6/7] Adding missing changes --- .../applications/saveandrestore/Messages.java | 4 +- .../client/SaveAndRestoreClient.java | 4 +- .../ui/SaveAndRestoreService.java | 4 +- .../BaseSnapshotTableViewController.java | 55 ++++-------- .../ui/snapshot/SnapshotController.java | 23 ++++- .../snapshot/SnapshotTableViewController.java | 90 ++++++++++--------- .../saveandrestore/messages.properties | 2 + .../logging/RestoreSnapshotActionInfo.java | 4 +- .../logging/SaveAndRestoreEventLogger.java | 7 +- ...eSnapshotActionAdapterFactoryTestData.java | 6 +- .../event/SaveAndRestoreEventReceiver.java | 14 +-- 11 files changed, 113 insertions(+), 100 deletions(-) diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/Messages.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/Messages.java index b48e686d3b..9688a35962 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/Messages.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/Messages.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2019 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -118,6 +118,8 @@ public class Messages { public static String nodeSelectionForConfiguration; public static String noValueAvailable; public static String readbackPVName; + public static String restoreFailed; + public static String restoreFailedPVs; public static String saveFilter; public static String saveFilterConfirmOverwrite; diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java index 4a8d999a43..394e514bf5 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java @@ -235,14 +235,14 @@ public interface SaveAndRestoreClient { /** * Requests service to restore the specified {@link SnapshotItem}s * @param snapshotItems A {@link List} of {@link SnapshotItem}s - * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + * @return A @{@link List} of {@link RestoreResult}s with information on potentially failed {@link SnapshotItem}s. */ List restore(List snapshotItems); /** * Requests service to restore the specified snapshot. * @param snapshotNodeId Unique id of a snapshot - * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + * @return A @{@link List} of {@link RestoreResult}s with information on potentially failed {@link SnapshotItem}s. */ List restore(String snapshotNodeId); } 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 540d0ac48f..bafc1dd3b1 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 @@ -398,7 +398,7 @@ public UserData authenticate(String userName, String password) throws Exception{ /** * Requests service to restore the specified {@link SnapshotItem}s * @param snapshotItems A {@link List} of {@link SnapshotItem}s - * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + * @return A @{@link List} of {@link RestoreResult}s with information on potentially failed {@link SnapshotItem}s. */ public List restore(List snapshotItems) throws Exception{ Future> future = @@ -409,7 +409,7 @@ public List restore(List snapshotItems) throws Exce /** * Requests service to restore the specified snapshot. * @param snapshotNodeId Unique id of a snapshot - * @return A {@link RestoreResult} with information on potentially failed {@link SnapshotItem}s. + * @return A @{@link List} of {@link RestoreResult}s with information on potentially failed {@link SnapshotItem}s. */ public List restore(String snapshotNodeId) throws Exception{ Future> future = diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java index 035c832d79..a6d33c89f4 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java @@ -23,14 +23,21 @@ import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.collections.ObservableList; import javafx.fxml.FXML; -import javafx.scene.control.*; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.text.Font; import javafx.scene.text.Text; - import org.epics.vtype.VType; import org.phoebus.applications.saveandrestore.Messages; import org.phoebus.applications.saveandrestore.SaveAndRestoreApplication; @@ -42,18 +49,16 @@ import org.phoebus.ui.javafx.FocusUtil; import org.phoebus.util.time.TimestampFormats; -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import static org.phoebus.ui.application.PhoebusApplication.logger; - /** * Base controller class for the snapshot table view. It handles common items (UI components, methods) needed * by all subclasses. @@ -93,8 +98,6 @@ public abstract class BaseSnapshotTableViewController { protected SnapshotController snapshotController; - protected static boolean resizePolicyNotInitialized = true; - protected static final Logger LOGGER = Logger.getLogger(BaseSnapshotTableViewController.class.getName()); @FXML @@ -127,27 +130,6 @@ public abstract class BaseSnapshotTableViewController { */ protected List snapshots = new ArrayList<>(); - public BaseSnapshotTableViewController() { - if (resizePolicyNotInitialized) { - AccessController.doPrivileged(resizePolicyAction); - } - } - - protected static PrivilegedAction resizePolicyAction = () -> { - try { - // Java FX bugfix: the table columns are not properly resized for the first table - Field f = TableView.CONSTRAINED_RESIZE_POLICY.getClass().getDeclaredField("isFirstRun"); - f.setAccessible(true); - f.set(TableView.CONSTRAINED_RESIZE_POLICY, Boolean.FALSE); - } catch (NoSuchFieldException | IllegalAccessException | RuntimeException e) { - // ignore - } - // Even if failed to set the policy, pretend that it was set. In such case the UI will be slightly dorked the - // first time, but will be OK in all other cases. - resizePolicyNotInitialized = false; - return null; - }; - public void initialize() { snapshotTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); snapshotTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); @@ -235,7 +217,7 @@ protected void updateItem(TableEntry item, boolean empty) { VType updatedValue = e.getRowValue().readOnlyProperty().get() ? e.getOldValue() : e.getNewValue(); ObjectProperty value = e.getRowValue().valueProperty(); value.setValue(new VTypePair(value.get().base, updatedValue, value.get().threshold)); - snapshotController.updateLoadedSnapshot( e.getRowValue(), updatedValue); + snapshotController.updateLoadedSnapshot(e.getRowValue(), updatedValue); }); liveValueColumn.setCellFactory(e -> new VTypeCellEditor<>()); @@ -278,11 +260,10 @@ protected String getPVKey(String pvName, boolean isReadonly) { return pvName + "_" + isReadonly; } - protected void showSnapshotInTable(Snapshot snapshot){ - if(snapshots.isEmpty()){ + protected void showSnapshotInTable(Snapshot snapshot) { + if (snapshots.isEmpty()) { snapshots.add(snapshot); - } - else{ + } else { snapshots.set(0, snapshot); } AtomicInteger counter = new AtomicInteger(0); 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 5d32f295a3..29d287f03b 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 @@ -1,5 +1,5 @@ /** - * Copyright (C) 2019 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -42,6 +42,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * This controller is for the use case of loading a configuration {@link Node} to take a new snapshot. @@ -350,12 +351,28 @@ public void loadSnapshot(Node snapshotNode) { } public void restore(ActionEvent actionEvent) { - snapshotTableViewController.restore(snapshotProperty.get(), restoreFailedPVNames -> { + snapshotTableViewController.restore(snapshotProperty.get(), restoreResultList -> { javafx.scene.Node jfxNode = (javafx.scene.Node) actionEvent.getSource(); String userData = (String) jfxNode.getUserData(); if (userData.equalsIgnoreCase("true")) { - eventReceivers.forEach(r -> r.snapshotRestored(snapshotProperty.get().getSnapshotNode(), restoreFailedPVNames, this::showLoggingError)); + eventReceivers.forEach(r -> r.snapshotRestored(snapshotProperty.get().getSnapshotNode(), restoreResultList, this::showLoggingError)); } + if(restoreResultList != null && !restoreResultList.isEmpty()){ + showFailedRestoreResult(restoreResultList); + } + }); + } + + private void showFailedRestoreResult(List restoreResultList){ + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(Messages.restoreFailedPVs).append(System.lineSeparator()); + stringBuilder.append(restoreResultList.stream() + .map(r -> r.getSnapshotItem().getConfigPv().getPvName()).collect(Collectors.joining(System.lineSeparator()))); + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(Messages.restoreFailed); + alert.setContentText(stringBuilder.toString()); + alert.show(); }); } diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java index 7557728093..0302da030a 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -23,7 +23,13 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; -import javafx.scene.control.*; +import javafx.scene.control.Alert; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.Tooltip; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import org.epics.vtype.VNumber; @@ -32,16 +38,29 @@ import org.phoebus.applications.saveandrestore.Messages; import org.phoebus.applications.saveandrestore.Preferences; import org.phoebus.applications.saveandrestore.SafeMultiply; -import org.phoebus.applications.saveandrestore.model.*; -import org.phoebus.applications.saveandrestore.ui.*; -import org.phoebus.core.vtypes.VTypeHelper; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.applications.saveandrestore.model.RestoreResult; +import org.phoebus.applications.saveandrestore.model.Snapshot; +import org.phoebus.applications.saveandrestore.model.SnapshotData; +import org.phoebus.applications.saveandrestore.model.SnapshotItem; +import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService; +import org.phoebus.applications.saveandrestore.ui.Threshold; +import org.phoebus.applications.saveandrestore.ui.Utilities; +import org.phoebus.applications.saveandrestore.ui.VNoData; +import org.phoebus.applications.saveandrestore.ui.VTypePair; import org.phoebus.core.vtypes.VDisconnectedData; import org.phoebus.framework.jobs.JobManager; +import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.util.time.TimestampFormats; import java.text.SimpleDateFormat; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -343,13 +362,17 @@ public void hideEqualItems() { Platform.runLater(() -> updateTable(arrayList)); } - public void restore(Snapshot snapshot, Consumer> completion) { - new Thread(() -> { - List restoreFailedPVNames = new ArrayList<>(); - CountDownLatch countDownLatch = new CountDownLatch(snapshot.getSnapshotData().getSnapshotItems().size()); - snapshot.getSnapshotData().getSnapshotItems() - .forEach(e -> pvs.get(getPVKey(e.getConfigPv().getPvName(), e.getConfigPv().isReadOnly())).setCountDownLatch(countDownLatch)); - + /** + * Restores a snapshot through a call to the remote service. + * + * @param snapshot The {@link Snapshot} object subject to restore + * @param completion A handler for the outcome of the restore operation + */ + public void restore(Snapshot snapshot, Consumer> completion) { + JobManager.schedule("Restore snapshot " + snapshot.getSnapshotNode().getName(), monitor -> { + List itemsToRestore = new ArrayList<>(); + List restoreResultList = null; + // Before invoking restore, compile a list of items where non-restorable PVs are excluded. for (SnapshotItem entry : snapshot.getSnapshotData().getSnapshotItems()) { TableEntry e = tableEntryItems.get(getPVKey(entry.getConfigPv().getPvName(), entry.getConfigPv().isReadOnly())); @@ -359,39 +382,24 @@ public void restore(Snapshot snapshot, Consumer> completion) { !entry.getValue().equals(VNoData.INSTANCE); if (restorable) { - final SaveAndRestorePV pv = pvs.get(getPVKey(e.pvNameProperty().get(), e.readOnlyProperty().get())); - if (entry.getValue() != null) { - try { - pv.getPv().write(VTypeHelper.toObject(entry.getValue())); - } catch (Exception writeException) { - restoreFailedPVNames.add(entry.getConfigPv().getPvName()); - } finally { - pv.countDown(); - } - } - } else { - countDownLatch.countDown(); + itemsToRestore.add(entry); } } try { - countDownLatch.await(10, TimeUnit.MINUTES); - } catch (InterruptedException e) { - LOGGER.log(Level.INFO, "Encountered InterruptedException", e); - } - - if (restoreFailedPVNames.isEmpty()) { - LOGGER.log(Level.FINE, "Restored snapshot {0}", snapshot.getSnapshotNode().getName()); - } else { - Collections.sort(restoreFailedPVNames); - StringBuilder sb = new StringBuilder(restoreFailedPVNames.size() * 200); - restoreFailedPVNames.forEach(e -> sb.append(e).append('\n')); - LOGGER.log(Level.WARNING, - "Not all PVs could be restored for {0}: {1}. The following errors occurred:\n{2}", - new Object[]{snapshot.getSnapshotNode().getName(), snapshot.getSnapshotNode(), sb.toString()}); + restoreResultList = SaveAndRestoreService.getInstance().restore(itemsToRestore); + } catch (Exception e) { + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(Messages.errorActionFailed); + alert.setContentText(e.getMessage()); + alert.setHeaderText(Messages.restoreFailed); + DialogHelper.positionDialog(alert, snapshotTableView, -150, -150); + alert.showAndWait(); + }); } - completion.accept(restoreFailedPVNames); - }).start(); + completion.accept(restoreResultList); + }); } public void setShowDeltaPercentage(boolean showDeltaPercentage) { diff --git a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/messages.properties b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/messages.properties index 0cfe09306d..9dc28c7992 100644 --- a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/messages.properties +++ b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/messages.properties @@ -145,6 +145,8 @@ restore=Restore restoreAndLog=Restore And Log restoreErrorTitle=Restore Error restoreErrorContent=Not all PVs were restored. +restoreFailed=Restore operation failed +restoreFailedPVs=Failed to write the following PVs: save=Save saveAsFilter=Save As Filter saveFilter=Save Filter diff --git a/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionInfo.java b/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionInfo.java index ead1d8a018..62f8a3a1ba 100644 --- a/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionInfo.java +++ b/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -24,7 +24,7 @@ /** * Object wrapping information about a restore action. */ -public class RestoreSnapshotActionInfo extends SaveSnapshotActionInfo{ +public class RestoreSnapshotActionInfo extends SaveSnapshotActionInfo { private boolean isGolden; private List failedPVs; diff --git a/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/SaveAndRestoreEventLogger.java b/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/SaveAndRestoreEventLogger.java index 59e7a96665..469e322832 100644 --- a/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/SaveAndRestoreEventLogger.java +++ b/app/save-and-restore/logging/src/main/java/org/phoebus/applications/saveandrestore/logging/SaveAndRestoreEventLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -20,6 +20,7 @@ import javafx.application.Platform; import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.RestoreResult; import org.phoebus.applications.saveandrestore.model.Tag; import org.phoebus.applications.saveandrestore.model.event.SaveAndRestoreEventReceiver; import org.phoebus.framework.selection.SelectionService; @@ -74,7 +75,7 @@ public void snapshotSaved(Node node, Consumer errorHandler) { * @param errorHandler An error handler callback. */ @Override - public void snapshotRestored(Node node, List failedPVs, Consumer errorHandler) { + public void snapshotRestored(Node node, List failedPVs, Consumer errorHandler) { if (!LogbookPreferences.is_supported) { return; @@ -87,7 +88,7 @@ public void snapshotRestored(Node node, List failedPVs, Consumer restoreSnapshotActionInfo.setComment(node.getDescription()); restoreSnapshotActionInfo.setGolden(node.hasTag(Tag.GOLDEN)); restoreSnapshotActionInfo.setSnapshotName(node.getName()); - restoreSnapshotActionInfo.setFailedPVs(failedPVs); + restoreSnapshotActionInfo.setFailedPVs(failedPVs.stream().map(restoreResult -> restoreResult.getSnapshotItem().getConfigPv().getPvName()).toList()); SelectionService.getInstance().setSelection("SaveAndRestoreLogging", List.of(restoreSnapshotActionInfo)); Platform.runLater(() -> ApplicationService.createInstance("logbook")); } diff --git a/app/save-and-restore/logging/src/test/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionAdapterFactoryTestData.java b/app/save-and-restore/logging/src/test/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionAdapterFactoryTestData.java index 5fbe0691a3..41734e7c8e 100644 --- a/app/save-and-restore/logging/src/test/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionAdapterFactoryTestData.java +++ b/app/save-and-restore/logging/src/test/java/org/phoebus/applications/saveandrestore/logging/RestoreSnapshotActionAdapterFactoryTestData.java @@ -32,18 +32,18 @@ public class RestoreSnapshotActionAdapterFactoryTestData { private RestoreSnapshotActionAdapterFactory restoreSnapshotActionAdapterFactory = new RestoreSnapshotActionAdapterFactory(); @Test - public void testGetAdaptableObject(){ + public void testGetAdaptableObject() { assertTrue(restoreSnapshotActionAdapterFactory.getAdaptableObject().isAssignableFrom(RestoreSnapshotActionInfo.class)); } @Test - public void testGetAdapterList(){ + public void testGetAdapterList() { List list = restoreSnapshotActionAdapterFactory.getAdapterList(); assertTrue(list.get(0).isAssignableFrom(LogEntry.class)); } @Test - public void testAdaptRestoreAction(){ + public void testAdaptRestoreAction() { RestoreSnapshotActionInfo restoreSnapshotActionInfo = new RestoreSnapshotActionInfo(); restoreSnapshotActionInfo.setSnapshotName("snapshot name"); restoreSnapshotActionInfo.setFailedPVs(List.of("pv name")); diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/event/SaveAndRestoreEventReceiver.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/event/SaveAndRestoreEventReceiver.java index 735fb38082..9635a98a59 100644 --- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/event/SaveAndRestoreEventReceiver.java +++ b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/event/SaveAndRestoreEventReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 European Spallation Source ERIC. + * Copyright (C) 2024 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 @@ -19,10 +19,10 @@ package org.phoebus.applications.saveandrestore.model.event; import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.RestoreResult; import java.util.List; import java.util.function.Consumer; -import java.util.function.Function; /** * Implementations registered over Java SPI will be called when: @@ -36,16 +36,18 @@ public interface SaveAndRestoreEventReceiver { /** * Called when a new snapshot {@link Node} has been successfully created and saved by the remote * save-and-restore service. - * @param node The {@link Node} representing the snapshot. + * + * @param node The {@link Node} representing the snapshot. * @param errorHandler An error handler callback. */ void snapshotSaved(Node node, Consumer errorHandler); /** * Called when a new snapshot {@link Node} has been restored. - * @param node The {@link Node} representing the snapshot. - * @param failedPVs List of PVs that for any reason could not be restored. + * + * @param node The {@link Node} representing the snapshot. + * @param failedPVs List of PVs that for any reason could not be restored. * @param errorHandler An error handler callback. */ - void snapshotRestored(Node node, List failedPVs, Consumer errorHandler); + void snapshotRestored(Node node, List failedPVs, Consumer errorHandler); } From 16ed203e033d68496ade27d0e0c9168a7e611413 Mon Sep 17 00:00:00 2001 From: georgweiss Date: Fri, 17 May 2024 13:00:16 +0200 Subject: [PATCH 7/7] Fixing unit tests --- .../snapshot/SnapshotTableViewController.java | 61 +++++++++++++++++++ .../SnapshotRestorerTest.java | 34 +++++++++-- .../epics/SnapshotRestorerTestConfig.java | 58 ++++++++++++++++++ .../web/config/ControllersTestConfig.java | 17 ++++++ .../SnapshotRestorerControllerTest.java | 2 +- 5 files changed, 167 insertions(+), 5 deletions(-) rename services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/{web/controllers => epics}/SnapshotRestorerTest.java (54%) create mode 100644 services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTestConfig.java diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java index 0302da030a..5a47b08446 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotTableViewController.java @@ -50,6 +50,7 @@ import org.phoebus.applications.saveandrestore.ui.VNoData; import org.phoebus.applications.saveandrestore.ui.VTypePair; import org.phoebus.core.vtypes.VDisconnectedData; +import org.phoebus.core.vtypes.VTypeHelper; import org.phoebus.framework.jobs.JobManager; import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.util.time.TimestampFormats; @@ -58,6 +59,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; @@ -402,6 +404,65 @@ public void restore(Snapshot snapshot, Consumer> completion) }); } + /** + * Restores a snapshot from client. + * + * @param snapshot The {@link Snapshot} object subject to restore + * @param completion A handler for the outcome of the restore operation + */ + @SuppressWarnings("unused") + public void restoreFromClient(Snapshot snapshot, Consumer> completion) { + new Thread(() -> { + List restoreFailedPVNames = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(snapshot.getSnapshotData().getSnapshotItems().size()); + snapshot.getSnapshotData().getSnapshotItems() + .forEach(e -> pvs.get(getPVKey(e.getConfigPv().getPvName(), e.getConfigPv().isReadOnly())).setCountDownLatch(countDownLatch)); + + for (SnapshotItem entry : snapshot.getSnapshotData().getSnapshotItems()) { + TableEntry e = tableEntryItems.get(getPVKey(entry.getConfigPv().getPvName(), entry.getConfigPv().isReadOnly())); + + boolean restorable = e.selectedProperty().get() && + !e.readOnlyProperty().get() && + entry.getValue() != null && + !entry.getValue().equals(VNoData.INSTANCE); + + if (restorable) { + final SaveAndRestorePV pv = pvs.get(getPVKey(e.pvNameProperty().get(), e.readOnlyProperty().get())); + if (entry.getValue() != null) { + try { + pv.getPv().write(VTypeHelper.toObject(entry.getValue())); + } catch (Exception writeException) { + restoreFailedPVNames.add(entry.getConfigPv().getPvName()); + } finally { + pv.countDown(); + } + } + } else { + countDownLatch.countDown(); + } + } + + try { + countDownLatch.await(10, TimeUnit.MINUTES); + } catch (InterruptedException e) { + LOGGER.log(Level.INFO, "Encountered InterruptedException", e); + } + + if (restoreFailedPVNames.isEmpty()) { + LOGGER.log(Level.FINE, "Restored snapshot {0}", snapshot.getSnapshotNode().getName()); + } else { + Collections.sort(restoreFailedPVNames); + StringBuilder sb = new StringBuilder(restoreFailedPVNames.size() * 200); + restoreFailedPVNames.forEach(e -> sb.append(e).append('\n')); + LOGGER.log(Level.WARNING, + "Not all PVs could be restored for {0}: {1}. The following errors occurred:\n{2}", + new Object[]{snapshot.getSnapshotNode().getName(), snapshot.getSnapshotNode(), sb.toString()}); + } + completion.accept(restoreFailedPVNames); + }).start(); + } + + public void setShowDeltaPercentage(boolean showDeltaPercentage) { this.showDeltaPercentage.set(showDeltaPercentage); } diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTest.java similarity index 54% rename from services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerTest.java rename to services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTest.java index 8ef2b834d6..02126392cf 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotRestorerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTest.java @@ -1,4 +1,23 @@ -package org.phoebus.service.saveandrestore.web.controllers; +/* + * Copyright (C) 2023 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.epics; import org.junit.jupiter.api.Assertions; @@ -9,18 +28,26 @@ import org.epics.vtype.Alarm; import org.epics.vtype.Display; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.phoebus.applications.saveandrestore.model.ConfigPv; import org.phoebus.applications.saveandrestore.model.SnapshotItem; import org.phoebus.core.vtypes.VTypeHelper; import org.phoebus.pv.PV; import org.phoebus.pv.PVPool; -import org.phoebus.service.saveandrestore.epics.SnapshotRestorer; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = SnapshotRestorerTestConfig.class) public class SnapshotRestorerTest { + @Autowired + private SnapshotRestorer snapshotRestorer; + @Test public void testRestorePVValues() throws Exception { - var snapshotRestorer = new SnapshotRestorer(); PV pv = PVPool.getPV("loc://x(42.0)"); var configPv = new ConfigPv(); configPv.setPvName("loc://x"); @@ -36,7 +63,6 @@ public void testRestorePVValues() throws Exception { @Test public void testCannotConnectPV() throws Exception { - var snapshotRestorer = new SnapshotRestorer(); var configPv = new ConfigPv(); configPv.setPvName("pva://x"); diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTestConfig.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTestConfig.java new file mode 100644 index 0000000000..eb0d996797 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/epics/SnapshotRestorerTestConfig.java @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2018 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.epics; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import org.mockito.Mockito; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ConfigurationDataRepository; +import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchTreeRepository; +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.web.config.AcceptHeaderResolver; +import org.phoebus.service.saveandrestore.web.config.WebSecurityConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +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.context.annotation.Scope; +import org.springframework.util.Base64Utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@SpringBootConfiguration +@ComponentScan(basePackages = "org.phoebus.service.saveandrestore.epics") +@SuppressWarnings("unused") +@Profile("!IT") +public class SnapshotRestorerTestConfig { + + @Bean + public SnapshotRestorer snapshotRestorer(){ + return new SnapshotRestorer(); + } + + @Bean + public ExecutorService executorService(){ + return Executors.newCachedThreadPool(); + } +} 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 8e29211f0d..55613e09dd 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 @@ -20,6 +20,7 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient; import org.mockito.Mockito; +import org.phoebus.service.saveandrestore.epics.SnapshotRestorer; import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ConfigurationDataRepository; import org.phoebus.service.saveandrestore.persistence.dao.impl.elasticsearch.ElasticsearchTreeRepository; @@ -32,8 +33,12 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; +import org.springframework.context.annotation.Scope; import org.springframework.util.Base64Utils; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @SpringBootConfiguration @ComponentScan(basePackages = "org.phoebus.service.saveandrestore.web.controllers") @Import(WebSecurityConfig.class) @@ -115,4 +120,16 @@ public String adminAuthorization() { public String readOnlyAuthorization() { return "Basic " + Base64Utils.encodeToString((demoReadOnly + ":" + demoReadOnlyPassword).getBytes()); } + + @SuppressWarnings("unused") + @Bean + @Scope("singleton") + public SnapshotRestorer snapshotRestorer(){ + return new SnapshotRestorer(); + } + + @Bean + public ExecutorService executorService(){ + return Executors.newCachedThreadPool(); + } } 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 0be8ead7d0..221ddbe9ff 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 @@ -73,7 +73,7 @@ public void testRestoreFromSnapshotNode() throws Exception { when(nodeDAO.getSnapshotData("uniqueId")).thenReturn(snapshotData); - MockHttpServletRequestBuilder request = post("/restore/node?parentNodeId=uniqueId") + MockHttpServletRequestBuilder request = post("/restore/node?nodeId=uniqueId") .header(HttpHeaders.AUTHORIZATION, userAuthorization); MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON))