From 245e25e57efa3622e59e947a38d76ac744802316 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 7 Nov 2021 18:32:07 +0100 Subject: [PATCH 01/32] WIP: Allowing the user to specify new backends functionality --- src/main/java/ecdar/Ecdar.java | 2 + .../BackendInstanceController.java | 86 +++++++++++++ .../ecdar/controllers/EcdarController.java | 113 +++++++++++------- .../BackendInstancePresentation.java | 19 +++ src/main/resources/ecdar/main.css | 82 ++----------- .../ecdar/presentations/BackendInstance.fxml | 59 +++++++++ .../presentations/EcdarPresentation.fxml | 51 +++++--- src/main/resources/ecdar/scroll_pane.css | 57 +++++++++ 8 files changed, 340 insertions(+), 129 deletions(-) create mode 100644 src/main/java/ecdar/controllers/BackendInstanceController.java create mode 100644 src/main/java/ecdar/presentations/BackendInstancePresentation.java create mode 100644 src/main/resources/ecdar/presentations/BackendInstance.fxml create mode 100644 src/main/resources/ecdar/scroll_pane.css diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index cffc332c..f0b5d6d8 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -181,6 +181,8 @@ public void start(final Stage stage) { scene.getStylesheets().add("ecdar/main.css"); scene.getStylesheets().add("ecdar/colors.css"); scene.getStylesheets().add("ecdar/model_canvas.css"); + scene.getStylesheets().add("ecdar/query_pane.css"); + scene.getStylesheets().add("ecdar/scroll_pane.css"); // Handle a mouse click as a deselection of all elements scene.setOnMousePressed(event -> { diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java new file mode 100644 index 00000000..28b2a2e7 --- /dev/null +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -0,0 +1,86 @@ +package ecdar.controllers; + +import com.jfoenix.controls.JFXCheckBox; +import com.jfoenix.controls.JFXRippler; +import com.jfoenix.controls.JFXTextField; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.Cursor; +import javafx.scene.control.RadioButton; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.stage.DirectoryChooser; + +import java.io.File; + +public class BackendInstanceController { + public JFXTextField address; + public RadioButton defaultBackend; + public JFXTextField portRangeStart; + public JFXTextField portRangeEnd; + public JFXCheckBox localAddress; + public JFXTextField pathToBackend; + public JFXRippler pickPathToBackend; + public HBox pathToBackendSection; + public HBox portSection; + public JFXRippler removeBackend; + public StackPane moveBackendInstance; + + public BackendInstanceController() { + Platform.runLater(() -> { + this.handleLocalPropertyChanged(); + moveBackendInstance.setCursor(Cursor.OPEN_HAND); + }); + } + + private void handleLocalPropertyChanged() { + if (localAddress.isSelected()) { + address.setDisable(true); + address.setText("127.0.0.1"); + portSection.setVisible(false); + portSection.setManaged(false); + pathToBackendSection.setVisible(true); + pathToBackendSection.setManaged(true); + } else { + address.setDisable(false); + address.setText(""); + portSection.setVisible(true); + portSection.setManaged(true); + pathToBackendSection.setVisible(false); + pathToBackendSection.setManaged(false); + } + } + + @FXML + private void addressLocalClicked(){ + handleLocalPropertyChanged(); + } + + @FXML + private void openPathToBackendDialog() { + // Dialog title + final DirectoryChooser backendPicker = new DirectoryChooser(); + backendPicker.setTitle("Choose backend"); + + // The initial location for the file choosing dialog + final File jarDir = new File(pathToBackend.getText()).getAbsoluteFile().getParentFile(); + + // If the file does not exist, we must be running it from a development environment, use an default location + if(jarDir.exists()) { + backendPicker.setInitialDirectory(jarDir); + } + + // Prompt the user to find a file (will halt the UI thread) + final File file = backendPicker.showDialog(null); + if(file != null) { + pathToBackend.setText(file.getAbsolutePath()); + } + } + + @FXML + private void removeBackendClicked() { + System.out.println("Backend removed"); + // ToDo NIELS: Handle queries using the given backend + // ToDo NIELS: Handle prompt user to confirm deletion + } +} diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 1dc1f326..f30d413d 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -109,7 +109,8 @@ public class EcdarController implements Initializable { public JFXDialog aboutDialog; public JFXButton aboutAcceptButton; public StackPane canvasPane; - public JFXSlider menuBarOptionsNumberOfSocketsSlider; + public JFXButton addBackendButton; + public HBox addBackendSection; private double expandHeight = 300; @@ -149,7 +150,7 @@ protected void interpolate(final double frac) { public MenuItem menuBarFileExportAsPng; public MenuItem menuBarFileExportAsPngNoBorder; public MenuItem menuBarOptionsCache; - public MenuItem menuBarOptionsDefaultBackend; + public MenuItem menuBarOptionsBackendOptions; public MenuItem menuBarHelpHelp; public MenuItem menuBarHelpAbout; public MenuItem menuBarHelpTest; @@ -165,6 +166,10 @@ protected void interpolate(final double frac) { public Text queryTextResult; public Text queryTextQuery; + public StackPane backendOptionsDialogContainer; + public JFXDialog backendOptionsDialog; + public VBox backendInstanceList; + private static JFXDialog _queryDialog; private static Text _queryTextResult; private static Text _queryTextQuery; @@ -186,6 +191,19 @@ public static EdgeStatus getGlobalEdgeStatus() { @Override public void initialize(final URL location, final ResourceBundle resources) { + initilizeDialogs(); + initializeCanvasPane(); + initializeEdgeStatusHandling(); + initializeKeybindings(); + initializeTabPane(); + initializeStatusBar(); + initializeMessages(); + initializeMenuBar(); + initializeReachabilityAnalysisThread(); + + } + + private void initilizeDialogs() { dialog.setDialogContainer(dialogContainer); dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); dialog.setOnDialogClosed(event -> dialogContainer.setVisible(false)); @@ -193,28 +211,32 @@ public void initialize(final URL location, final ResourceBundle resources) { _queryDialog = queryDialog; _queryTextResult = queryTextResult; _queryTextQuery = queryTextQuery; - queryDialog.setDialogContainer(queryDialogContainer); - queryDialogContainer.opacityProperty().bind(queryDialog.getChildren().get(0).scaleXProperty()); - queryDialog.setOnDialogClosed(event -> { - queryDialogContainer.setVisible(false); - queryDialogContainer.setMouseTransparent(true); - }); - queryDialog.setOnDialogOpened(event -> { - queryDialogContainer.setVisible(true); - queryDialogContainer.setMouseTransparent(false); - }); - initializeCanvasPane(); + initializeDialog(queryDialog, queryDialogContainer); + initializeBackendInstanceList(); + } - initializeEdgeStatusHandling(); + private void initializeBackendInstanceList() { + initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); - initializeKeybindings(); - initializeTabPane(); - initializeStatusBar(); - initializeMessages(); - initializeMenuBar(); - initializeReachabilityAnalysisThread(); + HBox.setHgrow(addBackendButton, Priority.ALWAYS); + addBackendButton.setMaxWidth(Double.MAX_VALUE); + backendInstanceList.getChildren().add(new BackendInstancePresentation()); + } + + private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { + dialog.setDialogContainer(dialogContainer); + dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); + dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); + dialog.setOnDialogClosed(event -> { + dialogContainer.setVisible(false); + dialogContainer.setMouseTransparent(true); + }); + dialog.setOnDialogOpened(event -> { + dialogContainer.setVisible(true); + dialogContainer.setMouseTransparent(false); + }); } /** @@ -508,27 +530,10 @@ private void initializeOptionsMenu() { menuBarOptionsCache.getGraphic().opacityProperty().bind(new When(isCached).then(1).otherwise(0)); }); - menuBarOptionsDefaultBackend.setOnAction(event -> { - BackendHelper.defaultBackend = (BackendHelper.defaultBackend.equals(BackendHelper.BackendNames.jEcdar) - ? BackendHelper.BackendNames.Reveaal - : BackendHelper.BackendNames.jEcdar); - - menuBarOptionsDefaultBackend.setText("Default backend: " + BackendHelper.defaultBackend.name()); - - Ecdar.preferences.put("default_backend", Integer.toString(BackendHelper.defaultBackend.ordinal())); - }); - - menuBarOptionsDefaultBackend.setText("Default backend: " + BackendHelper.defaultBackend.name()); - - menuBarOptionsNumberOfSocketsSlider.valueChangingProperty().addListener((observable, oldValue, newValue) -> { - if (oldValue && !newValue) { - int newIntValue = (int) Math.round(menuBarOptionsNumberOfSocketsSlider.getValue()); - Ecdar.getBackendDriver().setMaxNumberOfSockets(newIntValue); - Ecdar.preferences.put("number_of_backend_sockets", Integer.toString(newIntValue)); - } + menuBarOptionsBackendOptions.setOnAction(event -> { + backendOptionsDialogContainer.setVisible(true); + backendOptionsDialog.show(backendOptionsDialogContainer); }); - - menuBarOptionsNumberOfSocketsSlider.setValue(Ecdar.getBackendDriver().getMaxNumberOfSockets()); } private void initializeEditMenu() { @@ -616,7 +621,7 @@ private void initializeOpenProjectMenuItem() { // The initial location for the file choosing dialog final File jarDir = new File(System.getProperty("java.class.path")).getAbsoluteFile().getParentFile(); - // If the file does not exist, we must be running it from a development environment, use an default location + // If the file does not exist, we must be running it from a development environment, use default location if(jarDir.exists()) { projectPicker.setInitialDirectory(jarDir); } @@ -1379,11 +1384,35 @@ private void setGlobalEdgeStatus(EdgeStatus status) { } @FXML - private void closeDialog() { + private void closeQueryDialog() { dialog.close(); queryDialog.close(); } + @FXML + private void closeBackendDialog() { + dialog.close(); + backendOptionsDialog.close(); + } + + @FXML + private void saveChangesToBackendOptions() { + /*portRangeStart.focusedProperty().addListener((observable, oldValue, newValue) -> { + if (oldValue && !newValue) { + int newIntValue = (int) Math.round(menuBarOptionsNumberOfSocketsSlider.getValue()); + Ecdar.getBackendDriver().setMaxNumberOfSockets(newIntValue); + Ecdar.preferences.put("number_of_backend_sockets", Integer.toString(newIntValue)); + } + }); + + BackendHelper.defaultBackend = (BackendHelper.defaultBackend.equals(BackendHelper.BackendNames.jEcdar) + ? BackendHelper.BackendNames.Reveaal + : BackendHelper.BackendNames.jEcdar); + Ecdar.preferences.put("default_backend", Integer.toString(BackendHelper.defaultBackend.ordinal())); +*/ + this.closeBackendDialog(); + } + public static void openQueryDialog(final Query query, final String text) { if (text != null) { _queryTextResult.setText(text); diff --git a/src/main/java/ecdar/presentations/BackendInstancePresentation.java b/src/main/java/ecdar/presentations/BackendInstancePresentation.java new file mode 100644 index 00000000..a7e93f55 --- /dev/null +++ b/src/main/java/ecdar/presentations/BackendInstancePresentation.java @@ -0,0 +1,19 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXRippler; +import ecdar.controllers.BackendInstanceController; +import ecdar.utility.colors.Color; +import javafx.scene.Cursor; +import javafx.scene.layout.StackPane; + +public class BackendInstancePresentation extends StackPane { + private final BackendInstanceController controller; + + public BackendInstancePresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("BackendInstance.fxml", this); + + controller.pickPathToBackend.setCursor(Cursor.HAND); + controller.pickPathToBackend.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); + controller.pickPathToBackend.setMaskType(JFXRippler.RipplerMask.CIRCLE); + } +} diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index c687abec..3ed516a8 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -232,66 +232,7 @@ -fx-background-insets: 0 0 0 0; } - -.scroll-bar:horizontal .track, -.scroll-bar:vertical .track{ - -fx-opacity: 0; - -fx-border-color: transparent; - -fx-focus-color: transparent; -} - -.scroll-bar:horizontal .increment-button , -.scroll-bar:horizontal .decrement-button { - visibility: hidden; - -fx-background-radius : 0.0em; - -fx-padding :0.0 0.0 4.0 0.0; -} - -.scroll-bar:vertical .increment-button , -.scroll-bar:vertical .decrement-button { - visibility: hidden; - -fx-background-radius : 0.0em; - -fx-padding :0.0 4.0 0.0 0.0; -} - -.scroll-bar .increment-arrow, -.scroll-bar .decrement-arrow{ - -fx-shape : " "; - -fx-padding :0.15em 0.0; -} - -.scroll-bar:vertical .increment-arrow, -.scroll-bar:vertical .decrement-arrow{ - -fx-shape : " "; - -fx-padding :0.0 0.15em; -} - -.scroll-bar:horizontal .thumb, -.scroll-bar:vertical .thumb { - -fx-background-color : derive(-divider-color, 40.0%); - -fx-background-insets : 2.0, 0.0, 0.0; - -fx-background-radius : 2.0em; -} - -.scroll-bar:horizontal .thumb:hover, -.scroll-bar:vertical .thumb:hover { - -fx-background-color : derive(-divider-color, 10.0%); - -fx-background-insets : 2.0, 0.0, 0.0; - -fx-background-radius : 2.0em; -} - -.scroll-bar{ - -fx-background-color: transparent; - -fx-background-radius: 2em; - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; -} - -.scroll-bar:vertical:focused { - -fx-background-color: transparent; -} - -.jfx-slider { +/*.jfx-slider { -fx-pref-width: 60; } @@ -311,16 +252,19 @@ .jfx-slider > .slider-value { -fx-fill: -primary-color-darker; -fx-stroke: -primary-color-darker; -} - -.menu-item-embedded-control { +}*/ +.backendInstancesVBox { + -fx-padding: 5; + -fx-border-style: SOLID SOLID SOLID SOLID; + -fx-border-color: -divider-color; + -fx-border-width: 2px; } -.menu-item-embedded-control:hover { - -fx-background-color: none; +.backendInstanceStackPane { + -fx-background-color: -blue-grey-50; + -fx-padding: 20 0 20 0; + -fx-border-style: SOLID SOLID SOLID SOLID; + -fx-border-color: -divider-color; + -fx-border-width: 2px; } - -.menu-item-embedded-control:selected { - -fx-background-color: none; -} \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml new file mode 100644 index 00000000..61a05c74 --- /dev/null +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + New backend + + + + + + + + + Address: + + Local + + + Port range: + + - + + + + Path to backend: + + + + + + + + Default + + + \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 3b0f7fef..46749c6f 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -11,6 +11,7 @@ + - + - + - - - - - - - - - Backend Sockets: - - - - + @@ -375,7 +364,7 @@ - + @@ -478,7 +467,7 @@ - + @@ -565,4 +554,30 @@ + + + + + + + + + + Backends + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ecdar/scroll_pane.css b/src/main/resources/ecdar/scroll_pane.css new file mode 100644 index 00000000..7029b480 --- /dev/null +++ b/src/main/resources/ecdar/scroll_pane.css @@ -0,0 +1,57 @@ +.scroll-bar:horizontal .track, +.scroll-bar:vertical .track{ + -fx-opacity: 0; + -fx-border-color: transparent; + -fx-focus-color: transparent; +} + +.scroll-bar:horizontal .increment-button , +.scroll-bar:horizontal .decrement-button { + visibility: hidden; + -fx-background-radius : 0.0em; + -fx-padding :0.0 0.0 4.0 0.0; +} + +.scroll-bar:vertical .increment-button , +.scroll-bar:vertical .decrement-button { + visibility: hidden; + -fx-background-radius : 0.0em; + -fx-padding :0.0 4.0 0.0 0.0; +} + +.scroll-bar .increment-arrow, +.scroll-bar .decrement-arrow{ + -fx-shape : " "; + -fx-padding :0.15em 0.0; +} + +.scroll-bar:vertical .increment-arrow, +.scroll-bar:vertical .decrement-arrow{ + -fx-shape : " "; + -fx-padding :0.0 0.15em; +} + +.scroll-bar:horizontal .thumb, +.scroll-bar:vertical .thumb { + -fx-background-color : derive(-divider-color, 40.0%); + -fx-background-insets : 2.0, 0.0, 0.0; + -fx-background-radius : 2.0em; +} + +.scroll-bar:horizontal .thumb:hover, +.scroll-bar:vertical .thumb:hover { + -fx-background-color : derive(-divider-color, 10.0%); + -fx-background-insets : 2.0, 0.0, 0.0; + -fx-background-radius : 2.0em; +} + +.scroll-bar{ + -fx-background-color: transparent; + -fx-background-radius: 2em; + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} + +.scroll-bar:vertical:focused { + -fx-background-color: transparent; +} \ No newline at end of file From 32e7abacded2e30e18f790cbcc319e4411464f65 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 9 Nov 2021 20:24:43 +0100 Subject: [PATCH 02/32] WIP: Initial backend instances design --- .../BackendInstanceController.java | 16 +++++------ .../ecdar/controllers/EcdarController.java | 21 ++++++++++++-- .../BackendInstancePresentation.java | 4 +++ src/main/resources/ecdar/main.css | 3 +- .../ecdar/presentations/BackendInstance.fxml | 28 ++++++++++++------- .../presentations/EcdarPresentation.fxml | 6 ++-- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 28b2a2e7..4ec8ed49 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -4,8 +4,10 @@ import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXTextField; import javafx.application.Platform; +import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.Cursor; +import javafx.scene.Node; import javafx.scene.control.RadioButton; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; @@ -24,12 +26,15 @@ public class BackendInstanceController { public HBox pathToBackendSection; public HBox portSection; public JFXRippler removeBackend; - public StackPane moveBackendInstance; + public StackPane moveBackendInstanceUp; + public StackPane moveBackendInstanceDown; + private ObservableList instanceList; public BackendInstanceController() { Platform.runLater(() -> { this.handleLocalPropertyChanged(); - moveBackendInstance.setCursor(Cursor.OPEN_HAND); + moveBackendInstanceUp.setCursor(Cursor.HAND); + moveBackendInstanceDown.setCursor(Cursor.HAND); }); } @@ -76,11 +81,4 @@ private void openPathToBackendDialog() { pathToBackend.setText(file.getAbsolutePath()); } } - - @FXML - private void removeBackendClicked() { - System.out.println("Backend removed"); - // ToDo NIELS: Handle queries using the given backend - // ToDo NIELS: Handle prompt user to confirm deletion - } } diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index f30d413d..da6f7eb4 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -109,8 +109,6 @@ public class EcdarController implements Initializable { public JFXDialog aboutDialog; public JFXButton aboutAcceptButton; public StackPane canvasPane; - public JFXButton addBackendButton; - public HBox addBackendSection; private double expandHeight = 300; @@ -169,6 +167,7 @@ protected void interpolate(final double frac) { public StackPane backendOptionsDialogContainer; public JFXDialog backendOptionsDialog; public VBox backendInstanceList; + public JFXButton addBackendButton; private static JFXDialog _queryDialog; private static Text _queryTextResult; @@ -221,8 +220,24 @@ private void initializeBackendInstanceList() { HBox.setHgrow(addBackendButton, Priority.ALWAYS); addBackendButton.setMaxWidth(Double.MAX_VALUE); + addBackendButton.setOnMouseClicked((event) -> { + BackendInstancePresentation newBackendInstance = new BackendInstancePresentation(); + + backendInstanceList.getChildren().add(newBackendInstance); + newBackendInstance.getController().moveBackendInstanceUp.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, -1)); + newBackendInstance.getController().moveBackendInstanceDown.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, +1)); + newBackendInstance.getController().removeBackend.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstance)); + }); + } + + private void moveBackendInstance(BackendInstancePresentation newBackendInstance, int i) { + int currentIndex = backendInstanceList.getChildren().indexOf(newBackendInstance); + // Math.max added to avoid index -1 + int newIndex = Math.max(0, (currentIndex + i) % backendInstanceList.getChildren().size()); + // ToDo NIELS: Prevent loop around for overflow or add for underflow - backendInstanceList.getChildren().add(new BackendInstancePresentation()); + backendInstanceList.getChildren().remove(newBackendInstance); + backendInstanceList.getChildren().add(newIndex, newBackendInstance); } private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { diff --git a/src/main/java/ecdar/presentations/BackendInstancePresentation.java b/src/main/java/ecdar/presentations/BackendInstancePresentation.java index a7e93f55..efaec055 100644 --- a/src/main/java/ecdar/presentations/BackendInstancePresentation.java +++ b/src/main/java/ecdar/presentations/BackendInstancePresentation.java @@ -16,4 +16,8 @@ public BackendInstancePresentation() { controller.pickPathToBackend.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); controller.pickPathToBackend.setMaskType(JFXRippler.RipplerMask.CIRCLE); } + + public BackendInstanceController getController() { + return controller; + } } diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 3ed516a8..22747173 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -254,7 +254,7 @@ -fx-stroke: -primary-color-darker; }*/ -.backendInstancesVBox { +.backend-instances-list { -fx-padding: 5; -fx-border-style: SOLID SOLID SOLID SOLID; -fx-border-color: -divider-color; @@ -262,7 +262,6 @@ } .backendInstanceStackPane { - -fx-background-color: -blue-grey-50; -fx-padding: 20 0 20 0; -fx-border-style: SOLID SOLID SOLID SOLID; -fx-border-color: -divider-color; diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index 61a05c74..27297d96 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -13,24 +13,32 @@ type="StackPane" fx:controller="ecdar.controllers.BackendInstanceController" styleClass="backendInstanceStackPane"> - - - + + + + + + + + + + + + - - New backend - + + New backend + - + Address: diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 46749c6f..720a8f3c 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -565,9 +565,9 @@ Backends - - - + + + + From 4c30d39c49383bd800d164a5ab1edacb10173dad Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 9 Nov 2021 21:33:28 +0100 Subject: [PATCH 03/32] WIP: Major design update --- .../BackendInstanceController.java | 17 ++++- .../ecdar/controllers/EcdarController.java | 2 +- src/main/resources/ecdar/main.css | 9 +-- .../ecdar/presentations/BackendInstance.fxml | 76 +++++++++++-------- .../presentations/EcdarPresentation.fxml | 15 ++-- 5 files changed, 76 insertions(+), 43 deletions(-) diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 4ec8ed49..06dc86e5 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -12,6 +12,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.DirectoryChooser; +import org.kordamp.ikonli.javafx.FontIcon; import java.io.File; @@ -28,7 +29,8 @@ public class BackendInstanceController { public JFXRippler removeBackend; public StackPane moveBackendInstanceUp; public StackPane moveBackendInstanceDown; - private ObservableList instanceList; + public StackPane content; + public FontIcon expansionIcon; public BackendInstanceController() { Platform.runLater(() -> { @@ -61,6 +63,19 @@ private void addressLocalClicked(){ handleLocalPropertyChanged(); } + @FXML + private void expansionClicked() { + if (expansionIcon.getIconLiteral().equals("gmi-expand-less")) { + expansionIcon.setIconLiteral("gmi-expand-more"); + content.setVisible(false); + content.setManaged(false); + } else { + expansionIcon.setIconLiteral("gmi-expand-less"); + content.setVisible(true); + content.setManaged(true); + } + } + @FXML private void openPathToBackendDialog() { // Dialog title diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index da6f7eb4..9794c94c 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -167,7 +167,7 @@ protected void interpolate(final double frac) { public StackPane backendOptionsDialogContainer; public JFXDialog backendOptionsDialog; public VBox backendInstanceList; - public JFXButton addBackendButton; + public JFXRippler addBackendButton; private static JFXDialog _queryDialog; private static Text _queryTextResult; diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 22747173..2ef64627 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -256,14 +256,13 @@ .backend-instances-list { -fx-padding: 5; - -fx-border-style: SOLID SOLID SOLID SOLID; + -fx-border-style: SOLID HIDDEN SOLID HIDDEN; -fx-border-color: -divider-color; -fx-border-width: 2px; } -.backendInstanceStackPane { - -fx-padding: 20 0 20 0; - -fx-border-style: SOLID SOLID SOLID SOLID; +.backend-instance { + -fx-border-style: DASHED DASHED DASHED DASHED; -fx-border-color: -divider-color; - -fx-border-width: 2px; + -fx-border-width: 1px; } diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index 27297d96..e08c4c97 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -8,59 +8,75 @@ + + - - + styleClass="backend-instance"> + + - + - + - - - + + + + + New backend - + - + onMouseClicked="#expansionClicked" + StackPane.alignment="CENTER"> + - - - - Address: - - Local - - - Port range: - - - - - - - Path to backend: - - + - + + + + + + Address: + + + Local + + + Port range: + + - + + + + Path to backend: + + + + + + + + + Default diff --git a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml index 720a8f3c..b00c8580 100644 --- a/src/main/resources/ecdar/presentations/EcdarPresentation.fxml +++ b/src/main/resources/ecdar/presentations/EcdarPresentation.fxml @@ -565,12 +565,15 @@ Backends - - - - + - - + + + + + + + + From c9a15278aa6f40af7390376c36670f8e8674eaf2 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 10 Nov 2021 07:45:42 +0100 Subject: [PATCH 04/32] WIP: Border on backendinstance changed to SOLID --- src/main/resources/ecdar/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 2ef64627..24976a8b 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -262,7 +262,7 @@ } .backend-instance { - -fx-border-style: DASHED DASHED DASHED DASHED; + -fx-border-style: SOLID SOLID SOLID SOLID; -fx-border-color: -divider-color; -fx-border-width: 1px; } From 564be1ef63baeed91a0b12614e7d962b9856a1f0 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 10 Nov 2021 13:09:55 +0100 Subject: [PATCH 05/32] WIP: Default backend toggles working --- .../BackendInstanceController.java | 24 +++++------ .../ecdar/controllers/EcdarController.java | 8 ++-- .../ecdar/presentations/BackendInstance.fxml | 43 +++++++++---------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 06dc86e5..938bf51a 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -4,10 +4,8 @@ import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXTextField; import javafx.application.Platform; -import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.Cursor; -import javafx.scene.Node; import javafx.scene.control.RadioButton; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; @@ -18,25 +16,25 @@ public class BackendInstanceController { public JFXTextField address; - public RadioButton defaultBackend; + public RadioButton defaultBackendRadioButton; public JFXTextField portRangeStart; public JFXTextField portRangeEnd; public JFXCheckBox localAddress; public JFXTextField pathToBackend; + public HBox addressSection; public JFXRippler pickPathToBackend; public HBox pathToBackendSection; - public HBox portSection; - public JFXRippler removeBackend; - public StackPane moveBackendInstanceUp; - public StackPane moveBackendInstanceDown; + public JFXRippler removeBackendRippler; + public StackPane moveBackendInstanceUpRippler; + public StackPane moveBackendInstanceDownRippler; public StackPane content; public FontIcon expansionIcon; public BackendInstanceController() { Platform.runLater(() -> { this.handleLocalPropertyChanged(); - moveBackendInstanceUp.setCursor(Cursor.HAND); - moveBackendInstanceDown.setCursor(Cursor.HAND); + moveBackendInstanceUpRippler.setCursor(Cursor.HAND); + moveBackendInstanceDownRippler.setCursor(Cursor.HAND); }); } @@ -44,15 +42,15 @@ private void handleLocalPropertyChanged() { if (localAddress.isSelected()) { address.setDisable(true); address.setText("127.0.0.1"); - portSection.setVisible(false); - portSection.setManaged(false); + addressSection.setVisible(false); + addressSection.setManaged(false); pathToBackendSection.setVisible(true); pathToBackendSection.setManaged(true); } else { address.setDisable(false); address.setText(""); - portSection.setVisible(true); - portSection.setManaged(true); + addressSection.setVisible(true); + addressSection.setManaged(true); pathToBackendSection.setVisible(false); pathToBackendSection.setManaged(false); } diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 9794c94c..c1bef38e 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -168,6 +168,7 @@ protected void interpolate(final double frac) { public JFXDialog backendOptionsDialog; public VBox backendInstanceList; public JFXRippler addBackendButton; + public ToggleGroup defaultBackendToggleGroup = new ToggleGroup(); private static JFXDialog _queryDialog; private static Text _queryTextResult; @@ -224,9 +225,10 @@ private void initializeBackendInstanceList() { BackendInstancePresentation newBackendInstance = new BackendInstancePresentation(); backendInstanceList.getChildren().add(newBackendInstance); - newBackendInstance.getController().moveBackendInstanceUp.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, -1)); - newBackendInstance.getController().moveBackendInstanceDown.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, +1)); - newBackendInstance.getController().removeBackend.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstance)); + newBackendInstance.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, -1)); + newBackendInstance.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, +1)); + newBackendInstance.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstance)); + newBackendInstance.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); }); } diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index e08c4c97..85184a5e 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -1,14 +1,11 @@ - - - - + - + @@ -42,7 +39,7 @@ - + @@ -53,31 +50,33 @@ - Address: - - + + Address: + + + + Path: + + + + + + + + Local - + Port range: - - - Path to backend: - - - - - - - - Default + Default \ No newline at end of file From 329bd105aca4b6795880b17b23e588f320337348 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 10 Nov 2021 13:28:09 +0100 Subject: [PATCH 06/32] WIP: Design update (not doen) --- .../resources/ecdar/presentations/BackendInstance.fxml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index 85184a5e..695bca83 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -26,9 +26,9 @@ - + - + New backend @@ -50,10 +50,11 @@ - + Address: + Path: @@ -74,6 +75,7 @@ - + Default From d669bed98357b12641693600cb7b682cc3c7631d Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 14 Nov 2021 16:01:50 +0100 Subject: [PATCH 07/32] Design update --- .../BackendInstanceController.java | 15 ++++++++++++++ .../ecdar/presentations/BackendInstance.fxml | 20 ++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 938bf51a..6432fa93 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -8,6 +8,7 @@ import javafx.scene.Cursor; import javafx.scene.control.RadioButton; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.stage.DirectoryChooser; import org.kordamp.ikonli.javafx.FontIcon; @@ -29,15 +30,29 @@ public class BackendInstanceController { public StackPane moveBackendInstanceDownRippler; public StackPane content; public FontIcon expansionIcon; + public JFXTextField backendName; public BackendInstanceController() { Platform.runLater(() -> { this.handleLocalPropertyChanged(); moveBackendInstanceUpRippler.setCursor(Cursor.HAND); moveBackendInstanceDownRippler.setCursor(Cursor.HAND); + setHGrow(); }); } + private void setHGrow() { + HBox.setHgrow(backendName.getParent().getParent(), Priority.ALWAYS); + HBox.setHgrow(backendName, Priority.ALWAYS); + HBox.setHgrow(content, Priority.ALWAYS); + HBox.setHgrow(addressSection, Priority.ALWAYS); + HBox.setHgrow(address, Priority.ALWAYS); + HBox.setHgrow(pathToBackendSection, Priority.ALWAYS); + HBox.setHgrow(pathToBackend, Priority.ALWAYS); + HBox.setHgrow(portRangeStart, Priority.ALWAYS); + HBox.setHgrow(portRangeEnd, Priority.ALWAYS); + } + private void handleLocalPropertyChanged() { if (localAddress.isSelected()) { address.setDisable(true); diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index 695bca83..82b3d870 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -12,7 +12,7 @@ type="StackPane" fx:controller="ecdar.controllers.BackendInstanceController" styleClass="backend-instance"> - + @@ -28,10 +28,10 @@ - + - New backend + New backend + + + + Address: - + - Path: - + - Local Port range: - + - - + From 48cb1e0699b8097eee8ffafaafbca9ae1749d50a Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 14 Nov 2021 16:30:53 +0100 Subject: [PATCH 08/32] WIP: Backend instance logic --- .../ecdar/abstractions/BackendInstance.java | 91 +++++++++++++++++++ .../BackendInstanceController.java | 37 +++++--- .../ecdar/presentations/BackendInstance.fxml | 2 +- 3 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 src/main/java/ecdar/abstractions/BackendInstance.java diff --git a/src/main/java/ecdar/abstractions/BackendInstance.java b/src/main/java/ecdar/abstractions/BackendInstance.java new file mode 100644 index 00000000..eb9efe7c --- /dev/null +++ b/src/main/java/ecdar/abstractions/BackendInstance.java @@ -0,0 +1,91 @@ +package ecdar.abstractions; + +import com.google.gson.JsonObject; +import ecdar.utility.serialize.Serializable; + +public class BackendInstance implements Serializable { + private static final String NAME = "name"; + private static final String IS_LOCAL = "isLocal"; + private static final String IS_DEFAULT = "isDefault"; + private static final String LOCATION = "location"; + private static final String PORT_RANGE_START = "portRangeStart"; + private static final String PORT_RANGE_END = "portRangeEnd"; + + private String name; + private boolean isLocal; + private boolean isDefault; + private String backendLocation; + private int portStart; + private int portEnd; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isLocal() { + return isLocal; + } + + public void setLocal(boolean local) { + isLocal = local; + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + public String getBackendLocation() { + return backendLocation; + } + + public void setBackendLocation(String backendLocation) { + this.backendLocation = backendLocation; + } + + public int getPortStart() { + return portStart; + } + + public void setPortStart(int portStart) { + this.portStart = portStart; + } + + public int getPortEnd() { + return portEnd; + } + + public void setPortEnd(int portEnd) { + this.portEnd = portEnd; + } + + @Override + public JsonObject serialize() { + final JsonObject result = new JsonObject(); + result.addProperty(NAME, getName()); + result.addProperty(IS_LOCAL, isLocal()); + result.addProperty(IS_DEFAULT, isDefault()); + result.addProperty(LOCATION, getBackendLocation()); + result.addProperty(PORT_RANGE_START, getPortStart()); + result.addProperty(PORT_RANGE_END, getPortEnd()); + + return result; + } + + @Override + public void deserialize(final JsonObject json) { + setName(json.getAsJsonPrimitive(NAME).getAsString()); + setLocal(json.getAsJsonPrimitive(IS_LOCAL).getAsBoolean()); + setDefault(json.getAsJsonPrimitive(IS_DEFAULT).getAsBoolean()); + setBackendLocation(json.getAsJsonPrimitive(LOCATION).getAsString()); + setPortStart(json.getAsJsonPrimitive(PORT_RANGE_START).getAsInt()); + setPortEnd(json.getAsJsonPrimitive(PORT_RANGE_END).getAsInt()); + } +} diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 6432fa93..e0408046 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -3,8 +3,10 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXRippler; import com.jfoenix.controls.JFXTextField; +import ecdar.abstractions.BackendInstance; import javafx.application.Platform; import javafx.fxml.FXML; +import javafx.fxml.Initializable; import javafx.scene.Cursor; import javafx.scene.control.RadioButton; import javafx.scene.layout.HBox; @@ -14,25 +16,30 @@ import org.kordamp.ikonli.javafx.FontIcon; import java.io.File; +import java.net.URL; +import java.util.ResourceBundle; -public class BackendInstanceController { +public class BackendInstanceController implements Initializable { + private BackendInstance backendInstance = null; + + public JFXTextField backendName; + public FontIcon expansionIcon; + public JFXRippler removeBackendRippler; + public StackPane content; + public HBox addressSection; public JFXTextField address; - public RadioButton defaultBackendRadioButton; + public JFXCheckBox isLocal; + public HBox pathToBackendSection; + public JFXRippler pickPathToBackend; + public JFXTextField pathToBackend; public JFXTextField portRangeStart; public JFXTextField portRangeEnd; - public JFXCheckBox localAddress; - public JFXTextField pathToBackend; - public HBox addressSection; - public JFXRippler pickPathToBackend; - public HBox pathToBackendSection; - public JFXRippler removeBackendRippler; public StackPane moveBackendInstanceUpRippler; public StackPane moveBackendInstanceDownRippler; - public StackPane content; - public FontIcon expansionIcon; - public JFXTextField backendName; + public RadioButton defaultBackendRadioButton; - public BackendInstanceController() { + @Override + public void initialize(URL location, ResourceBundle resources) { Platform.runLater(() -> { this.handleLocalPropertyChanged(); moveBackendInstanceUpRippler.setCursor(Cursor.HAND); @@ -41,6 +48,10 @@ public BackendInstanceController() { }); } + public void setBackendInstance(BackendInstance instance) { + this.backendInstance = instance; + } + private void setHGrow() { HBox.setHgrow(backendName.getParent().getParent(), Priority.ALWAYS); HBox.setHgrow(backendName, Priority.ALWAYS); @@ -54,7 +65,7 @@ private void setHGrow() { } private void handleLocalPropertyChanged() { - if (localAddress.isSelected()) { + if (isLocal.isSelected()) { address.setDisable(true); address.setText("127.0.0.1"); addressSection.setVisible(false); diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index 82b3d870..c9e037bb 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -69,7 +69,7 @@ - Local + Local Port range: From f5d4250f46b825d2ab8f6218a1f3398a4d75b19c Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Mon, 15 Nov 2021 08:36:29 +0100 Subject: [PATCH 09/32] WIP: Using custom backends for the queries (initial setup started) --- .../ecdar/abstractions/BackendInstance.java | 10 +++++++ src/main/java/ecdar/abstractions/Project.java | 4 +-- .../BackendInstanceController.java | 13 +++++++- .../ecdar/controllers/EcdarController.java | 30 ++++++++++++++----- .../BackendInstancePresentation.java | 6 ++++ 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/main/java/ecdar/abstractions/BackendInstance.java b/src/main/java/ecdar/abstractions/BackendInstance.java index eb9efe7c..acbc03e2 100644 --- a/src/main/java/ecdar/abstractions/BackendInstance.java +++ b/src/main/java/ecdar/abstractions/BackendInstance.java @@ -18,6 +18,12 @@ public class BackendInstance implements Serializable { private int portStart; private int portEnd; + public BackendInstance() {}; + + public BackendInstance(final JsonObject jsonObject) { + deserialize(jsonObject); + }; + public String getName() { return name; } @@ -66,6 +72,10 @@ public void setPortEnd(int portEnd) { this.portEnd = portEnd; } + public int getNumberOfInstances() { + return this.portEnd - this.portStart; + } + @Override public JsonObject serialize() { final JsonObject result = new JsonObject(); diff --git a/src/main/java/ecdar/abstractions/Project.java b/src/main/java/ecdar/abstractions/Project.java index b4db434c..efd11e6f 100644 --- a/src/main/java/ecdar/abstractions/Project.java +++ b/src/main/java/ecdar/abstractions/Project.java @@ -221,7 +221,7 @@ private void deserializeComponents(final File componentsFolder) throws IOExcepti final String fileContent = Files.toString(file, Charset.defaultCharset()); // Parse the file to an json object - final JsonObject jsonObject = new JsonParser().parse(fileContent).getAsJsonObject(); + final JsonObject jsonObject = JsonParser.parseString(fileContent).getAsJsonObject(); // Fetch the name of the component final String componentName = jsonObject.get("name").getAsString(); @@ -261,7 +261,7 @@ private void deserializeSystems(final File systemsFolder) throws IOException { // If JSON file if (file.getName().endsWith(JSON_FILENAME_EXTENSION)) { final String fileContent = Files.toString(file, Charset.defaultCharset()); - final JsonObject json = new JsonParser().parse(fileContent).getAsJsonObject(); + final JsonObject json = JsonParser.parseString(fileContent).getAsJsonObject(); final String name = json.get("name").getAsString(); nameJsonMap.put(name, json); } diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index e0408046..a228658e 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -20,7 +20,7 @@ import java.util.ResourceBundle; public class BackendInstanceController implements Initializable { - private BackendInstance backendInstance = null; + private BackendInstance backendInstance = new BackendInstance(); public JFXTextField backendName; public FontIcon expansionIcon; @@ -52,6 +52,17 @@ public void setBackendInstance(BackendInstance instance) { this.backendInstance = instance; } + public BackendInstance updateBackendInstance() { + backendInstance.setName(backendName.getText()); + backendInstance.setLocal(isLocal.isSelected()); + backendInstance.setDefault(defaultBackendRadioButton.isSelected()); + backendInstance.setBackendLocation(isLocal.isSelected() ? pathToBackend.getText() : address.getText()); + backendInstance.setPortStart(Integer.parseInt(portRangeStart.getText())); + backendInstance.setPortEnd(Integer.parseInt(portRangeEnd.getText())); + + return backendInstance; + } + private void setHGrow() { HBox.setHgrow(backendName.getParent().getParent(), Priority.ALWAYS); HBox.setHgrow(backendName, Priority.ALWAYS); diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index c1bef38e..ec8e489a 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -1,5 +1,8 @@ package ecdar.controllers; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import ecdar.Debug; import ecdar.Ecdar; import ecdar.abstractions.*; @@ -219,19 +222,32 @@ private void initilizeDialogs() { private void initializeBackendInstanceList() { initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); + String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': '/', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': '/', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; + final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backends", defaultBackendInstanceList)).getAsJsonObject(); + final JsonArray backends = jsonObject.getAsJsonArray("backends"); + + backends.forEach((backend) -> { + BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); + addBackendInstancePresentationToList(newBackendInstancePresentation); + }); + HBox.setHgrow(addBackendButton, Priority.ALWAYS); addBackendButton.setMaxWidth(Double.MAX_VALUE); addBackendButton.setOnMouseClicked((event) -> { - BackendInstancePresentation newBackendInstance = new BackendInstancePresentation(); - - backendInstanceList.getChildren().add(newBackendInstance); - newBackendInstance.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, -1)); - newBackendInstance.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstance, +1)); - newBackendInstance.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstance)); - newBackendInstance.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(); + addBackendInstancePresentationToList(newBackendInstancePresentation); }); } + private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { + backendInstanceList.getChildren().add(newBackendInstancePresentation); + newBackendInstancePresentation.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, -1)); + newBackendInstancePresentation.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, +1)); + newBackendInstancePresentation.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstancePresentation)); + newBackendInstancePresentation.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); + } + private void moveBackendInstance(BackendInstancePresentation newBackendInstance, int i) { int currentIndex = backendInstanceList.getChildren().indexOf(newBackendInstance); // Math.max added to avoid index -1 diff --git a/src/main/java/ecdar/presentations/BackendInstancePresentation.java b/src/main/java/ecdar/presentations/BackendInstancePresentation.java index efaec055..7299a904 100644 --- a/src/main/java/ecdar/presentations/BackendInstancePresentation.java +++ b/src/main/java/ecdar/presentations/BackendInstancePresentation.java @@ -1,6 +1,7 @@ package ecdar.presentations; import com.jfoenix.controls.JFXRippler; +import ecdar.abstractions.BackendInstance; import ecdar.controllers.BackendInstanceController; import ecdar.utility.colors.Color; import javafx.scene.Cursor; @@ -9,6 +10,11 @@ public class BackendInstancePresentation extends StackPane { private final BackendInstanceController controller; + public BackendInstancePresentation(BackendInstance backendInstance) { + this(); + controller.setBackendInstance(backendInstance); + } + public BackendInstancePresentation() { controller = new EcdarFXMLLoader().loadAndGetController("BackendInstance.fxml", this); From 7aab046af19011a9eefc6c6e7d7d2c150a97c113 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 17 Nov 2021 13:20:39 +0100 Subject: [PATCH 10/32] WIP: Logic being implemented... --- src/main/java/ecdar/Ecdar.java | 16 +-- src/main/java/ecdar/abstractions/Query.java | 12 +- .../java/ecdar/backend/BackendDriver.java | 115 +++++++----------- .../java/ecdar/backend/BackendHelper.java | 40 +++--- .../BackendInstanceController.java | 25 +++- .../ecdar/controllers/EcdarController.java | 16 +-- .../mutation/MutationTestPlanController.java | 2 +- .../presentations/QueryPresentation.java | 69 +++++------ .../ecdar/presentations/BackendInstance.fxml | 4 +- .../presentations/EcdarPresentation.fxml | 2 +- .../presentations/QueryPresentation.fxml | 4 +- 11 files changed, 142 insertions(+), 163 deletions(-) diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index f0b5d6d8..b9ffffb6 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -45,7 +45,7 @@ public class Ecdar extends Application { public static SimpleStringProperty projectDirectory = new SimpleStringProperty(); private static BooleanProperty isUICached = new SimpleBooleanProperty(); private static final BooleanProperty isSplit = new SimpleBooleanProperty(true); //Set to true to ensure correct behaviour at first toggle. - private static BackendDriver backendDriver; + private static final BackendDriver backendDriver = new BackendDriver(); private Stage debugStage; /** @@ -159,7 +159,7 @@ public void start(final Stage stage) { // Load the fonts required for the project IconFontFX.register(GoogleMaterialDesignIcons.getIconFont()); loadFonts(); - loadPreferences(); + loadBackends(); // Remove the classic decoration // kyrke - 2020-04-17: Disabled due to bug https://bugs.openjdk.java.net/browse/JDK-8154847 @@ -250,7 +250,7 @@ public void start(final Stage stage) { BackendHelper.stopQueries(); try { - backendDriver.closeAllSockets(); + backendDriver.closeAllBackendConnection(); } catch (IOException e) { e.printStackTrace(); } @@ -279,14 +279,8 @@ public void start(final Stage stage) { // } } - private void loadPreferences() { - BackendHelper.defaultBackend = preferences.getInt("default_backend", BackendHelper.BackendNames.jEcdar.ordinal()) - == BackendHelper.BackendNames.jEcdar.ordinal() - ? BackendHelper.BackendNames.jEcdar - : BackendHelper.BackendNames.Reveaal; - - backendDriver = new BackendDriver(preferences.get("backend_host_address", "127.0.0.1")); - getBackendDriver().setMaxNumberOfSockets(preferences.getInt("number_of_backend_sockets", 5)); + private void loadBackends() { + // ToDo NIELS: Load backends } /** diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index 728c0854..f4301064 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -30,7 +30,7 @@ public class Query implements Serializable { private final SimpleBooleanProperty isPeriodic = new SimpleBooleanProperty(false); private final StringProperty errors = new SimpleStringProperty(""); private final ObjectProperty type = new SimpleObjectProperty<>(); - private BackendHelper.BackendNames backend; + private BackendInstance backend; private Consumer runQuery; public Query(final String query, final String comment, final QueryState queryState) { @@ -98,11 +98,11 @@ public void setIsPeriodic(final boolean isPeriodic) { this.isPeriodic.set(isPeriodic); } - public BackendHelper.BackendNames getBackend() { + public BackendInstance getBackend() { return backend; } - public void setBackend(BackendHelper.BackendNames backend) { + public void setBackend(BackendInstance backend) { this.backend = backend; } @@ -179,7 +179,7 @@ public JsonObject serialize() { result.add(IGNORED_INPUTS, getHashMapAsJsonObject(ignoredInputs)); result.add(IGNORED_OUTPUTS, getHashMapAsJsonObject(ignoredOutputs)); - result.addProperty(BACKEND, backend.ordinal()); + result.addProperty(BACKEND, backend.getName()); return result; } @@ -219,9 +219,7 @@ public void deserialize(final JsonObject json) { } if(json.has(BACKEND)) { - setBackend(json.getAsJsonPrimitive(BACKEND).getAsInt() == BackendHelper.BackendNames.jEcdar.ordinal() - ? BackendHelper.BackendNames.jEcdar - : BackendHelper.BackendNames.Reveaal); + setBackend(BackendHelper.getBackendInstanceByName(json.getAsJsonPrimitive(BACKEND).getAsString())); } else { setBackend(BackendHelper.defaultBackend); } diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index aadab7dc..e6590c1b 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -5,6 +5,7 @@ import EcdarProtoBuf.QueryProtos; import com.google.protobuf.Empty; import ecdar.Ecdar; +import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; import ecdar.abstractions.QueryState; import io.grpc.*; @@ -20,22 +21,14 @@ import java.util.function.Consumer; public class BackendDriver { - private final Pair numberOfReveaalConnections = new Pair<>(new AtomicInteger(0), new AtomicInteger(5)); - private final List reveaalConnections = new CopyOnWriteArrayList<>(); - - private final Pair numberOfJEcdarConnections = new Pair<>(new AtomicInteger(0), new AtomicInteger(5)); - private final List jEcdarConnections = new CopyOnWriteArrayList<>(); - - private final String hostAddress; - + private final List openBackendConnections = new CopyOnWriteArrayList<>(); private final int deadlineForResponses = 20000; private final int rerunQueryDelay = 200; - public BackendDriver(String hostAddress) { - this.hostAddress = hostAddress; + public BackendDriver() { } - public void addQueryToExecutionQueue(String query, BackendHelper.BackendNames backend, Consumer success, Consumer failure, QueryListener queryListener) { + public void addQueryToExecutionQueue(String query, BackendInstance backend, Consumer success, Consumer failure, QueryListener queryListener) { new Timer().schedule(new TimerTask() { @Override public void run() { @@ -86,39 +79,26 @@ public Pair, ArrayList> getInputOutputs(String query) return null; //inputOutputs; } - public void closeAllSockets() throws IOException { - for (BackendConnection s : reveaalConnections) s.close(); - for (BackendConnection s : jEcdarConnections) s.close(); - } - - public void setMaxNumberOfSockets(int i) { - numberOfReveaalConnections.getValue().set(i); - numberOfJEcdarConnections.getValue().set(i); - - // ToDo NIELS: Potentially close connections until within new range [0, i] - } - - public int getMaxNumberOfSockets() { - return numberOfReveaalConnections.getValue().get(); + /** + * Close all open backend connection + * + * @throws IOException if any of the sockets do not respond + */ + public void closeAllBackendConnection() throws IOException { + for (BackendConnection s : openBackendConnections) s.close(); } private void executeQuery(ExecutableQuery executableQuery) { - if(executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; + if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) return; // Get available connection or start new - final Optional connection; - if (executableQuery.backend.equals(BackendHelper.BackendNames.jEcdar)) { - connection = jEcdarConnections.stream().filter((element) -> !element.isRunningQuery()).findFirst(); - } else { - connection = reveaalConnections.stream().filter((element) -> !element.isRunningQuery()).findFirst(); - } + final BackendConnection backendConnection = openBackendConnections.stream() + .filter((connection) -> connection.getBackendInstance().equals(executableQuery.backend)) + .findFirst() + .orElseGet(() -> startNewBackendConnection(executableQuery.backend)); - final BackendConnection backendConnection = connection.orElseGet(() -> (executableQuery.backend == BackendHelper.BackendNames.Reveaal - ? startNewBackendConnection(BackendHelper.BackendNames.Reveaal, numberOfReveaalConnections, reveaalConnections) - : startNewBackendConnection(BackendHelper.BackendNames.jEcdar, numberOfJEcdarConnections, jEcdarConnections))); - - // If the query connection is null, there are no available sockets - // and the maximum number of sockets has already been reached + // If the connection is null, there are no available sockets + // and the specified port range is occupied. Schedule a rerun of the query if (backendConnection == null) { new Timer().schedule(new TimerTask() { @Override @@ -146,7 +126,8 @@ public void onNext(Empty value) { @Override public void onError(Throwable t) { - if(executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) backendConnection.setExecutableQuery(null); + if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) + backendConnection.setExecutableQuery(null); else { handleBackendError(t, backendConnection); error = true; @@ -159,7 +140,7 @@ public void onCompleted() { StreamObserver responseObserver = new StreamObserver<>() { @Override public void onNext(QueryProtos.QueryResponse value) { - if(executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) { + if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) { backendConnection.setExecutableQuery(null); return; } @@ -244,25 +225,28 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe } } - private BackendConnection startNewBackendConnection(BackendHelper.BackendNames backend, Pair numberOfSockets, List backendConnections) { - if (numberOfSockets.getKey().get() < numberOfSockets.getValue().get()) { + private BackendConnection startNewBackendConnection(BackendInstance backend) { + try { + Process p; + BackendConnection newConnection = null; + String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); + try { - Process p; - int portNumber = SocketUtils.findAvailableTcpPort(); + int portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); do { ProcessBuilder pb; - if (backend.equals(BackendHelper.BackendNames.jEcdar)) { - pb = new ProcessBuilder("java", "-jar", "src/libs/j-Ecdar.jar"); + if (backend.getBackendLocation().endsWith(".jar")) { + pb = new ProcessBuilder("java", "-jar", backend.getBackendLocation()); } else { - pb = new ProcessBuilder("src/Reveaal", "-p", this.hostAddress + ":" + portNumber).redirectErrorStream(true); + pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); } - + p = pb.start(); // If the process is not alive, it failed while starting up, try again } while (!p.isAlive()); - ManagedChannel channel = ManagedChannelBuilder.forTarget(this.hostAddress + ":" + portNumber) + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) .usePlaintext() .keepAliveWithoutCalls(true) .keepAliveTime(1000, TimeUnit.MILLISECONDS) @@ -271,18 +255,15 @@ private BackendConnection startNewBackendConnection(BackendHelper.BackendNames b EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); - BackendConnection newConnection = new BackendConnection(p, stub); - backendConnections.add(newConnection); - numberOfSockets.getKey().getAndIncrement(); - - return newConnection; - - } catch (IOException e) { - e.printStackTrace(); + newConnection = new BackendConnection(p, stub); + this.openBackendConnections.add(newConnection); + } catch (IllegalStateException e) { + Ecdar.showToast("Unable to find a free port in port range: " + backend.getPortStart() + " - " + backend.getPortEnd() + " for " + backend.getName() + " sockets"); } - } else { - System.out.println("Max number of sockets already reached"); + return newConnection; + } catch (IOException e) { + e.printStackTrace(); } return null; @@ -290,12 +271,12 @@ private BackendConnection startNewBackendConnection(BackendHelper.BackendNames b private class ExecutableQuery { private final String query; - private final BackendHelper.BackendNames backend; + private final BackendInstance backend; private final Consumer success; private final Consumer failure; private final QueryListener queryListener; - ExecutableQuery(String query, BackendHelper.BackendNames backend, Consumer success, Consumer failure, QueryListener queryListener) { + ExecutableQuery(String query, BackendInstance backend, Consumer success, Consumer failure, QueryListener queryListener) { this.query = query; this.backend = backend; this.success = success; @@ -326,6 +307,10 @@ public ExecutableQuery getExecutableQuery() { return executableQuery; } + public BackendInstance getBackendInstance() { + return executableQuery.backend; + } + public void setExecutableQuery(ExecutableQuery executableQuery) { this.executableQuery = executableQuery; } @@ -335,13 +320,7 @@ public boolean isRunningQuery() { } public void close() throws IOException { - // Remove the socket from the socket list - if (jEcdarConnections.remove(this)) { - numberOfJEcdarConnections.getKey().getAndDecrement(); - } else if(reveaalConnections.remove(this)) { - numberOfReveaalConnections.getKey().getAndDecrement(); - } - + // ToDo NIELS: Close channels as well process.destroy(); } } diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index 6614f27b..06075d90 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -2,10 +2,7 @@ import com.uppaal.model.core2.Document; import ecdar.Ecdar; -import ecdar.abstractions.Component; -import ecdar.abstractions.Location; -import ecdar.abstractions.Project; -import ecdar.abstractions.Query; +import ecdar.abstractions.*; import org.apache.commons.io.FileUtils; import java.io.File; @@ -17,11 +14,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; public final class BackendHelper { final static String TEMP_DIRECTORY = "temporary"; private static EcdarDocument ecdarDocument; - public static BackendHelper.BackendNames defaultBackend = BackendHelper.BackendNames.jEcdar; + public static BackendInstance defaultBackend = null; + private static List backendInstances = new ArrayList<>(); public static String storeBackendModel(Project project, String fileName) throws BackendException, IOException, URISyntaxException { return storeBackendModel(project, TEMP_DIRECTORY, fileName); @@ -81,8 +80,8 @@ public static String storeQuery(String query, String fileName) throws URISyntaxE * @param backend the name of the backend to check * @return true if the backend supports ignored inputs and outputs, else false */ - public static Boolean backendSupportsInputOutputs(BackendHelper.BackendNames backend) { - return backend == BackendHelper.BackendNames.Reveaal; + public static Boolean backendSupportsInputOutputs(BackendInstance backend) { + return true; } /** @@ -140,15 +139,24 @@ public static String getExistDeadlockQuery(final Component component) { return "E<> (" + String.join(" || ", locationNames) + ") && deadlock"; } - /** - * Enum for the available backends. Used for saving and loading the queries. - */ - public enum BackendNames { - jEcdar, Reveaal; + public static BackendInstance getBackendInstanceByName(String backendInstanceName) { + Optional backendInstance = backendInstances.stream().filter(bi -> bi.getName().equals(backendInstanceName)).findFirst(); + return backendInstance.orElse(null); + } - @Override - public String toString() { - return this.ordinal() == 0 ? "jEcdar" : "Reveaal"; - } + public static BackendInstance getDefaultBackend() { + return defaultBackend; + } + + public static void setBackendInstances(List backendInstances) { + BackendHelper.backendInstances = backendInstances; + } + + public static List getBackendInstances() { + return backendInstances; + } + + public static void setDefaultBackendInstance(BackendInstance newDefaultBackend) { + defaultBackend = newDefaultBackend; } } diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index a228658e..2da5296f 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -13,6 +13,7 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; import org.kordamp.ikonli.javafx.FontIcon; import java.io.File; @@ -48,8 +49,26 @@ public void initialize(URL location, ResourceBundle resources) { }); } + /*** + * Sets the BackendInstance object and overrides the current settings shown in the GUI + * @param instance the new BackendInstance + */ public void setBackendInstance(BackendInstance instance) { this.backendInstance = instance; + + this.backendName.setText(instance.getName()); + this.isLocal.setSelected(instance.isLocal()); + this.defaultBackendRadioButton.setSelected(instance.isDefault()); + + // Check if the path or the address should be used + if (isLocal.isSelected()) { + this.pathToBackend.setText(instance.getBackendLocation()); + } else { + this.address.setText(instance.getBackendLocation()); + } + + this.portRangeStart.setText(String.valueOf(instance.getPortStart())); + this.portRangeEnd.setText(String.valueOf(instance.getPortEnd())); } public BackendInstance updateBackendInstance() { @@ -114,19 +133,19 @@ private void expansionClicked() { @FXML private void openPathToBackendDialog() { // Dialog title - final DirectoryChooser backendPicker = new DirectoryChooser(); + final FileChooser backendPicker = new FileChooser(); backendPicker.setTitle("Choose backend"); // The initial location for the file choosing dialog final File jarDir = new File(pathToBackend.getText()).getAbsoluteFile().getParentFile(); - // If the file does not exist, we must be running it from a development environment, use an default location + // If the file does not exist, we must be running it from a development environment, use a default location if(jarDir.exists()) { backendPicker.setInitialDirectory(jarDir); } // Prompt the user to find a file (will halt the UI thread) - final File file = backendPicker.showDialog(null); + final File file = backendPicker.showOpenDialog(null); if(file != null) { pathToBackend.setText(file.getAbsolutePath()); } diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index ec8e489a..a5f6ff4e 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -222,7 +222,7 @@ private void initilizeDialogs() { private void initializeBackendInstanceList() { initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); - String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': '/', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': '/', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; + String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backends", defaultBackendInstanceList)).getAsJsonObject(); final JsonArray backends = jsonObject.getAsJsonArray("backends"); @@ -1430,19 +1430,7 @@ private void closeBackendDialog() { @FXML private void saveChangesToBackendOptions() { - /*portRangeStart.focusedProperty().addListener((observable, oldValue, newValue) -> { - if (oldValue && !newValue) { - int newIntValue = (int) Math.round(menuBarOptionsNumberOfSocketsSlider.getValue()); - Ecdar.getBackendDriver().setMaxNumberOfSockets(newIntValue); - Ecdar.preferences.put("number_of_backend_sockets", Integer.toString(newIntValue)); - } - }); - - BackendHelper.defaultBackend = (BackendHelper.defaultBackend.equals(BackendHelper.BackendNames.jEcdar) - ? BackendHelper.BackendNames.Reveaal - : BackendHelper.BackendNames.jEcdar); - Ecdar.preferences.put("default_backend", Integer.toString(BackendHelper.defaultBackend.ordinal())); -*/ + Ecdar.preferences.put("default_backend", BackendHelper.getDefaultBackend().getName()); this.closeBackendDialog(); } diff --git a/src/main/java/ecdar/mutation/MutationTestPlanController.java b/src/main/java/ecdar/mutation/MutationTestPlanController.java index 485f5a84..b98a5b82 100644 --- a/src/main/java/ecdar/mutation/MutationTestPlanController.java +++ b/src/main/java/ecdar/mutation/MutationTestPlanController.java @@ -211,7 +211,7 @@ public void onSelectSutButtonPressed() { jarDir = new File(Ecdar.projectDirectory.get()); - // If the file does not exist, we must be running it from a development environment, use an default location + // If the file does not exist, we must be running it from a development environment, use a default location if (jarDir.exists()) { fileChooser.setInitialDirectory(jarDir); } diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 6101acac..f07f509d 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -48,7 +48,7 @@ public QueryPresentation(final Query query) { initializeDetailsButton(); initializeTextFields(); initializeInputOutputPaneAndAddIgnoredInputOutputs(); - initializeSwapBackendButton(); + // initializeSwapBackendButton(); initializeMoreInformationButtonAndQueryTypeSymbol(); } @@ -317,32 +317,32 @@ private void initializeResetInputOutputPaneButton(TitledPane inputOutputPane, }); }); } - - private void initializeSwapBackendButton() { - Platform.runLater(() -> { - final JFXRippler swapBackendButton = (JFXRippler) lookup("#swapBackendButton"); - final TitledPane inputOutputPane = (TitledPane) lookup("#inputOutputPane"); - this.currentBackendLabel = (Label) lookup("#currentBackendLabel"); - - swapBackendButton.setCursor(Cursor.HAND); - swapBackendButton.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); - swapBackendButton.setMaskType(JFXRippler.RipplerMask.CIRCLE); - swapBackendButton.setOnMousePressed(event -> { - // Set the backend to the one not currently used and update GUI - final BackendHelper.BackendNames newBackend = (this.controller.getQuery().getBackend().equals(BackendHelper.BackendNames.jEcdar) - ? BackendHelper.BackendNames.Reveaal - : BackendHelper.BackendNames.jEcdar); - - this.controller.getQuery().setBackend(newBackend); - setSwapBackendTooltipAndLabel(newBackend); - updateTitlePaneVisibility(inputOutputPane, controller.getQuery().getQuery()); - }); - - swapBackendButtonTooltip = new Tooltip(); - setSwapBackendTooltipAndLabel(this.controller.getQuery().getBackend()); - JFXTooltip.install(swapBackendButton, swapBackendButtonTooltip); - }); - } +// +// private void initializeSwapBackendButton() { +// Platform.runLater(() -> { +// final JFXRippler swapBackendButton = (JFXRippler) lookup("#swapBackendButton"); +// final TitledPane inputOutputPane = (TitledPane) lookup("#inputOutputPane"); +// this.currentBackendLabel = (Label) lookup("#currentBackendLabel"); +// +// swapBackendButton.setCursor(Cursor.HAND); +// swapBackendButton.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); +// swapBackendButton.setMaskType(JFXRippler.RipplerMask.CIRCLE); +// swapBackendButton.setOnMousePressed(event -> { +// // Set the backend to the one not currently used and update GUI +// final BackendHelper.BackendNames newBackend = (this.controller.getQuery().getBackend().equals(BackendHelper.BackendNames.jEcdar) +// ? BackendHelper.BackendNames.Reveaal +// : BackendHelper.BackendNames.jEcdar); +// +// this.controller.getQuery().setBackend(newBackend); +// setSwapBackendTooltipAndLabel(newBackend); +// updateTitlePaneVisibility(inputOutputPane, controller.getQuery().getQuery()); +// }); +// +// swapBackendButtonTooltip = new Tooltip(); +// setSwapBackendTooltipAndLabel(this.controller.getQuery().getBackend()); +// JFXTooltip.install(swapBackendButton, swapBackendButtonTooltip); +// }); +// } private void updateTitlePaneVisibility(TitledPane inputOutputPane, String queryString) { // Check if the query is a refinement and that the engine is set to Reveaal @@ -468,17 +468,10 @@ private void addIgnoredInputOutputsFromQuery(TitledPane inputOutputPane) { } } - private void setSwapBackendTooltipAndLabel(BackendHelper.BackendNames backend) { - boolean isReveaal; - if(backend == null){ - isReveaal = false; - } else { - isReveaal = backend.equals(BackendHelper.BackendNames.Reveaal); - } - - swapBackendButtonTooltip.setText("Switch to the " + (isReveaal ? "jEcdar" : "Reveaal") + " backend"); - currentBackendLabel.setText((isReveaal ? "Reveaal" : "jEcdar")); - } +// private void setSwapBackendTooltipAndLabel(BackendInstance backend) { +// swapBackendButtonTooltip.setText("Switch to the " + (isReveaal ? "jEcdar" : "Reveaal") + " backend"); +// currentBackendLabel.setText((isReveaal ? "Reveaal" : "jEcdar")); +// } private void initializeMoreInformationButtonAndQueryTypeSymbol() { Platform.runLater(() -> { diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstance.fxml index c9e037bb..ffc2a614 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstance.fxml @@ -56,11 +56,11 @@ Address: - + Path: - + Backends - + - + From 3c01c71e00466da15e463459a23209b0d929a9f7 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 21 Nov 2021 12:59:35 +0100 Subject: [PATCH 11/32] WIP: Refactoring and input validation on backend options preparations --- .../java/ecdar/backend/BackendDriver.java | 76 ++++---- .../BackendInstanceController.java | 9 + .../BackendOptionsDialogController.java | 180 ++++++++++++++++++ .../ecdar/controllers/EcdarController.java | 127 +++++------- .../BackendInstancePresentation.java | 2 +- .../BackendOptionsDialogPresentation.java | 16 ++ src/main/resources/ecdar/main.css | 4 + ....fxml => BackendInstancePresentation.fxml} | 2 + .../BackendOptionsDialogPresentation.fxml | 40 ++++ .../presentations/EcdarPresentation.fxml | 26 +-- 10 files changed, 341 insertions(+), 141 deletions(-) create mode 100644 src/main/java/ecdar/controllers/BackendOptionsDialogController.java create mode 100644 src/main/java/ecdar/presentations/BackendOptionsDialogPresentation.java rename src/main/resources/ecdar/presentations/{BackendInstance.fxml => BackendInstancePresentation.fxml} (96%) create mode 100644 src/main/resources/ecdar/presentations/BackendOptionsDialogPresentation.fxml diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index e6590c1b..dafe1d93 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -8,6 +8,7 @@ import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Component; import ecdar.abstractions.QueryState; +import ecdar.controllers.EcdarController; import io.grpc.*; import io.grpc.stub.StreamObserver; import javafx.util.Pair; @@ -97,8 +98,8 @@ private void executeQuery(ExecutableQuery executableQuery) { .findFirst() .orElseGet(() -> startNewBackendConnection(executableQuery.backend)); - // If the connection is null, there are no available sockets - // and the specified port range is occupied. Schedule a rerun of the query + // If the connection is null, there are no available connections, + // and it was not possible to start a new one if (backendConnection == null) { new Timer().schedule(new TimerTask() { @Override @@ -226,47 +227,48 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe } private BackendConnection startNewBackendConnection(BackendInstance backend) { - try { - Process p; - BackendConnection newConnection = null; - String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); + Process p; + String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); - try { - int portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); - - do { - ProcessBuilder pb; - if (backend.getBackendLocation().endsWith(".jar")) { - pb = new ProcessBuilder("java", "-jar", backend.getBackendLocation()); - } else { - pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); - } - p = pb.start(); - // If the process is not alive, it failed while starting up, try again - } while (!p.isAlive()); + int portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); - ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) - .usePlaintext() - .keepAliveWithoutCalls(true) - .keepAliveTime(1000, TimeUnit.MILLISECONDS) - .keepAliveTimeout(2000, TimeUnit.MILLISECONDS) - .build(); - - EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); + do { + ProcessBuilder pb; + if (backend.getBackendLocation().endsWith(".jar")) { + pb = new ProcessBuilder("java", "-jar", backend.getBackendLocation()); + } else { + pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); + } - newConnection = new BackendConnection(p, stub); - this.openBackendConnections.add(newConnection); - } catch (IllegalStateException e) { - Ecdar.showToast("Unable to find a free port in port range: " + backend.getPortStart() + " - " + backend.getPortEnd() + " for " + backend.getName() + " sockets"); + try { + p = pb.start(); + } catch (IOException ioException) { + Ecdar.showToast("Unable to start backend instance. Check the error tab for more details."); + // ToDo NIELS: Add error to errors tab with text: + // "The backend instance could not be started. Make sure that the following is correct: + // - Path/address + // - At least one port in the port range is free for the given address (localhost if backend is set to local) + // - The backend is an executable or a .jar file + // - The backend supports the '-p {host}:{port}' flag on startup + ioException.printStackTrace(); + return null; } + // If the process is not alive, it failed while starting up, try again + } while (!p.isAlive()); - return newConnection; - } catch (IOException e) { - e.printStackTrace(); - } + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) + .usePlaintext() + .keepAliveWithoutCalls(true) + .keepAliveTime(1000, TimeUnit.MILLISECONDS) + .keepAliveTimeout(2000, TimeUnit.MILLISECONDS) + .build(); + + EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); - return null; + BackendConnection newConnection = new BackendConnection(p, stub); + this.openBackendConnections.add(newConnection); + return newConnection; } private class ExecutableQuery { @@ -294,7 +296,7 @@ private class BackendConnection { private final EcdarBackendGrpc.EcdarBackendStub stub; private ExecutableQuery executableQuery = null; - BackendConnection(Process process, EcdarBackendGrpc.EcdarBackendStub stub) throws IOException { + BackendConnection(Process process, EcdarBackendGrpc.EcdarBackendStub stub) { this.process = process; this.stub = stub; } diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 2da5296f..b925d5d0 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -8,6 +8,7 @@ import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Cursor; +import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -24,6 +25,7 @@ public class BackendInstanceController implements Initializable { private BackendInstance backendInstance = new BackendInstance(); public JFXTextField backendName; + public Label backendNameIssue; public FontIcon expansionIcon; public JFXRippler removeBackendRippler; public StackPane content; @@ -46,6 +48,13 @@ public void initialize(URL location, ResourceBundle resources) { moveBackendInstanceUpRippler.setCursor(Cursor.HAND); moveBackendInstanceDownRippler.setCursor(Cursor.HAND); setHGrow(); + + // Prevent deletion of default backend instance + removeBackendRippler.setDisable(defaultBackendRadioButton.isSelected()); + defaultBackendRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> { + removeBackendRippler.setDisable(newValue); + }); + // ToDo NIELS: Visualize remove rippler disabled }); } diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java new file mode 100644 index 00000000..893efd76 --- /dev/null +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -0,0 +1,180 @@ +package ecdar.controllers; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXDialog; +import com.jfoenix.controls.JFXRippler; +import ecdar.Ecdar; +import ecdar.abstractions.BackendInstance; +import ecdar.backend.BackendHelper; +import ecdar.presentations.BackendInstancePresentation; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.apache.commons.lang3.Range; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ResourceBundle; + +public class BackendOptionsDialogController implements Initializable { + public VBox backendInstanceList; + public JFXRippler addBackendButton; + public JFXButton closeButton; + public ToggleGroup defaultBackendToggleGroup = new ToggleGroup(); + public JFXButton saveButton; + + @Override + public void initialize(URL location, ResourceBundle resources) { + initializeBackendInstanceList(); + } + + public void resetBackendOptions() { + // ToDo NIELS: Read saved options and override current + } + + private void initializeBackendInstanceList() { + String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; + final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backends", defaultBackendInstanceList)).getAsJsonObject(); + final JsonArray backends = jsonObject.getAsJsonArray("backends"); + + backends.forEach((backend) -> { + BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); + addBackendInstancePresentationToList(newBackendInstancePresentation); + }); + + HBox.setHgrow(addBackendButton, Priority.ALWAYS); + addBackendButton.setMaxWidth(Double.MAX_VALUE); + addBackendButton.setOnMouseClicked((event) -> { + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(); + addBackendInstancePresentationToList(newBackendInstancePresentation); + }); + } + + private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { + backendInstanceList.getChildren().add(newBackendInstancePresentation); + newBackendInstancePresentation.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, -1)); + newBackendInstancePresentation.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, +1)); + newBackendInstancePresentation.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstancePresentation)); + newBackendInstancePresentation.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); + } + + private void moveBackendInstance(BackendInstancePresentation newBackendInstance, int i) { + int currentIndex = backendInstanceList.getChildren().indexOf(newBackendInstance); + // Math.max added to avoid index -1 + int newIndex = Math.max(0, (currentIndex + i) % backendInstanceList.getChildren().size()); + // ToDo NIELS: Prevent loop around for overflow or add for underflow + + backendInstanceList.getChildren().remove(newBackendInstance); + backendInstanceList.getChildren().add(newIndex, newBackendInstance); + } + + /** + * Marks input fields in the backendInstanceList if any are present and returns whether any were found + * + * @return whether any errors were found + */ + private boolean backendInstaceListIsErrorFree() { + for (Node child : backendInstanceList.getChildren()) { + if (child instanceof BackendInstancePresentation) { + BackendInstanceController backendInstanceController = ((BackendInstancePresentation) child).getController(); + + return portRangeIsErrorFree(backendInstanceController); + } + } + + return true; + } + + private boolean portRangeIsErrorFree(BackendInstanceController backendInstanceController) { + int portRangeStart; + int portRangeEnd; + + try { + portRangeStart = Integer.parseInt(backendInstanceController.portRangeStart.getText()); + } catch (NumberFormatException numberFormatException) { + // ToDO NIELS: The value is not an integer + return false; + } + + try { + portRangeEnd = Integer.parseInt(backendInstanceController.portRangeEnd.getText()); + } catch (NumberFormatException numberFormatException) { + // ToDO NIELS: The value is not an integer + return false; + } + + Range portRange = Range.between(0, 65535); + + if (!portRange.contains(portRangeStart) + || !portRange.contains(portRangeEnd) + || portRangeEnd - portRangeStart < 0) { + // ToDo NIELS: The port range is not acceptable + return false; + } + + return true; + } + + private boolean backendInstanceLocationIsErrorFree(BackendInstanceController backendInstanceController) { + if (backendInstanceController.isLocal.isSelected()) { + Path localBackendPath = Paths.get(backendInstanceController.pathToBackend.getText()); + if (!Files.isExecutable(localBackendPath)) { + // ToDo NIELS: The path either does not exist or it is read/execute protected + return false; + } + + if (!localBackendPath.endsWith(".jar") && !localBackendPath.endsWith(".exe") && !localBackendPath.getFileName().toString().contains(".")) { + // ToDo NIELS: The path is not an accepted file type + return false; + } + } else { + try { + InetAddress address = InetAddress.getByName(backendInstanceController.address.getText()); + boolean reachable = address.isReachable(200); + + if (!reachable) { + // ToDo NIELS: Address is unreachable + return false; + } + + } catch (UnknownHostException unknownHostException) { + // ToDo NIELS: The address is not an acceptable hostname + return false; + } catch (IOException ioException) { + // ToDo NIELS: IOException while trying to reach host + return false; + } + } + + return true; + } + + /** + * Returns true if no errors where found in the backend instance definitions, otherwise false + * + * @return whether the changes could be saved + */ + public boolean saveChangesToBackendOptions() { + if (this.backendInstaceListIsErrorFree()) { + Ecdar.preferences.put("default_backend", BackendHelper.getDefaultBackend().getName()); + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index a5f6ff4e..09cc4c97 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -1,8 +1,5 @@ package ecdar.controllers; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import ecdar.Debug; import ecdar.Ecdar; import ecdar.abstractions.*; @@ -168,10 +165,7 @@ protected void interpolate(final double frac) { public Text queryTextQuery; public StackPane backendOptionsDialogContainer; - public JFXDialog backendOptionsDialog; - public VBox backendInstanceList; - public JFXRippler addBackendButton; - public ToggleGroup defaultBackendToggleGroup = new ToggleGroup(); + public BackendOptionsDialogPresentation backendOptionsDialog; private static JFXDialog _queryDialog; private static Text _queryTextResult; @@ -203,7 +197,6 @@ public void initialize(final URL location, final ResourceBundle resources) { initializeMessages(); initializeMenuBar(); initializeReachabilityAnalysisThread(); - } private void initilizeDialogs() { @@ -216,48 +209,22 @@ private void initilizeDialogs() { _queryTextQuery = queryTextQuery; initializeDialog(queryDialog, queryDialogContainer); - initializeBackendInstanceList(); - } - - private void initializeBackendInstanceList() { initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); - String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; - final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backends", defaultBackendInstanceList)).getAsJsonObject(); - final JsonArray backends = jsonObject.getAsJsonArray("backends"); - - backends.forEach((backend) -> { - BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); - BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); - addBackendInstancePresentationToList(newBackendInstancePresentation); + backendOptionsDialog.getController().closeButton.setOnMouseClicked(event -> { + backendOptionsDialog.getController().resetBackendOptions(); + dialog.close(); + backendOptionsDialog.close(); }); - HBox.setHgrow(addBackendButton, Priority.ALWAYS); - addBackendButton.setMaxWidth(Double.MAX_VALUE); - addBackendButton.setOnMouseClicked((event) -> { - BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(); - addBackendInstancePresentationToList(newBackendInstancePresentation); + backendOptionsDialog.getController().saveButton.setOnMouseClicked(event -> { + if (backendOptionsDialog.getController().saveChangesToBackendOptions()) { + dialog.close(); + backendOptionsDialog.close(); + } }); } - private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { - backendInstanceList.getChildren().add(newBackendInstancePresentation); - newBackendInstancePresentation.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, -1)); - newBackendInstancePresentation.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, +1)); - newBackendInstancePresentation.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstancePresentation)); - newBackendInstancePresentation.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); - } - - private void moveBackendInstance(BackendInstancePresentation newBackendInstance, int i) { - int currentIndex = backendInstanceList.getChildren().indexOf(newBackendInstance); - // Math.max added to avoid index -1 - int newIndex = Math.max(0, (currentIndex + i) % backendInstanceList.getChildren().size()); - // ToDo NIELS: Prevent loop around for overflow or add for underflow - - backendInstanceList.getChildren().remove(newBackendInstance); - backendInstanceList.getChildren().add(newIndex, newBackendInstance); - } - private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { dialog.setDialogContainer(dialogContainer); dialogContainer.opacityProperty().bind(dialog.getChildren().get(0).scaleXProperty()); @@ -274,11 +241,11 @@ private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { /** * Initializes the keybinding for: - * - New component - * - Nudging with arrow keys - * - Nudging with WASD - * - Deletion - * - Colors + * - New component + * - Nudging with arrow keys + * - Nudging with WASD + * - Deletion + * - Colors */ private void initializeKeybindings() { //Press ctrl+N or cmd+N to create a new component. The canvas changes to this new component @@ -345,12 +312,12 @@ private void initializeKeybindings() { /** * Handles the change of color on selected objects - * @param enabledColor The new color for the selected objects + * + * @param enabledColor The new color for the selected objects * @param previousColor The color old color of the selected objects */ public void changeColorOnSelectedElements(final EnabledColor enabledColor, - final List> previousColor) - { + final List> previousColor) { UndoRedoStack.pushAndPerform(() -> { // Perform SelectHelper.getSelectedElements() .forEach(selectable -> selectable.color(enabledColor.color, enabledColor.intensity)); @@ -490,7 +457,7 @@ private void initializeStatusBar() { public void onChanged(final Change c) { while (c.next()) { Platform.runLater(() -> { - if(Debug.backgroundThreads.size() == 0) { + if (Debug.backgroundThreads.size() == 0) { queryStatusContainer.setOpacity(0); } else { queryStatusContainer.setOpacity(1); @@ -564,8 +531,9 @@ private void initializeOptionsMenu() { }); menuBarOptionsBackendOptions.setOnAction(event -> { - backendOptionsDialogContainer.setVisible(true); - backendOptionsDialog.show(backendOptionsDialogContainer); + backendOptionsDialogContainer.setVisible(true); + backendOptionsDialog.show(backendOptionsDialogContainer); + backendOptionsDialog.setMouseTransparent(false); }); } @@ -631,7 +599,7 @@ private void initializeViewMenu() { menuBarViewCanvasSplit.getGraphic().setOpacity(1); menuBarViewCanvasSplit.setOnAction(event -> { final BooleanProperty isSplit = Ecdar.toggleCanvasSplit(); - if(isSplit.get()) { + if (isSplit.get()) { setCanvasModeToSingular(); menuBarViewCanvasSplit.setText("Split canvas"); } else { @@ -655,13 +623,13 @@ private void initializeOpenProjectMenuItem() { final File jarDir = new File(System.getProperty("java.class.path")).getAbsoluteFile().getParentFile(); // If the file does not exist, we must be running it from a development environment, use default location - if(jarDir.exists()) { + if (jarDir.exists()) { projectPicker.setInitialDirectory(jarDir); } // Prompt the user to find a file (will halt the UI thread) final File file = projectPicker.showDialog(root.getScene().getWindow()); - if(file != null) { + if (file != null) { try { Ecdar.projectDirectory.set(file.getAbsolutePath()); Ecdar.initializeProjectFolder(); @@ -706,6 +674,7 @@ private void saveAs() { /** * Save project at a given directory. + * * @param directory directory to save at */ private static void save(final File directory) { @@ -818,7 +787,7 @@ private void initializeFileExportAsPng() { final ComponentPresentation presentation = canvas.getController().getActiveComponentPresentation(); //If there is no active component - if (presentation == null){ + if (presentation == null) { Ecdar.showToast("No component to export."); return; } @@ -843,7 +812,7 @@ private void setCanvasModeToSingular() { CanvasShellPresentation canvasShellPresentation = new CanvasShellPresentation(); HighLevelModelObject model = activeCanvasPresentation.get().getController().getActiveModel(); - if(model != null) { + if (model != null) { canvasShellPresentation.getController().canvasPresentation.getController().setActiveModel(activeCanvasPresentation.get().getController().getActiveModel()); } else { canvasShellPresentation.getController().canvasPresentation.getController().setActiveModel(Ecdar.getProject().getComponents().get(0)); @@ -928,6 +897,7 @@ private void setCanvasModeToSplit() { /** * Initialize a new CanvasShellPresentation and set its active component to the next component encountered from the startIndex and return it + * * @param components the list of components for assigning active component of the CanvasPresentation * @param startIndex the index to start at when trying to find the component to set as active * @return new CanvasShellPresentation @@ -937,8 +907,8 @@ private CanvasShellPresentation initializeNewCanvasShellPresentationWithActiveCo int numComponents = components.size(); canvasShellPresentation.getController().canvasPresentation.getController().setActiveModel(null); - for(int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++){ - if(getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { + for (int currentCompNum = startIndex; currentCompNum < numComponents; currentCompNum++) { + if (getActiveCanvasPresentation().getController().getActiveModel() != components.get(currentCompNum)) { canvasShellPresentation.getController().canvasPresentation.getController().setActiveModel(components.get(currentCompNum)); break; } @@ -949,6 +919,7 @@ private CanvasShellPresentation initializeNewCanvasShellPresentationWithActiveCo /** * Initialize a new CanvasShellPresentation and return it + * * @return new CanvasShellPresentation */ private CanvasShellPresentation initializeNewCanvasShellPresentation() { @@ -960,6 +931,7 @@ private CanvasShellPresentation initializeNewCanvasShellPresentation() { /** * Take a snapshot with the grid hidden. * The grid is put into its original state afterwards. + * * @return the snapshot */ private WritableImage takeSnapshot(CanvasPresentation canvas) { @@ -975,6 +947,7 @@ private WritableImage takeSnapshot(CanvasPresentation canvas) { /** * Zooms in times 4 to get a higher resolution. * Then take snapshot and zoom to times 1 again. + * * @return the snapshot */ private WritableImage scaleAndTakeSnapshot(CanvasPresentation canvas) { @@ -990,6 +963,7 @@ private WritableImage scaleAndTakeSnapshot(CanvasPresentation canvas) { /** * Crops and exports an image. + * * @param image the image */ private void CropAndExportImage(final WritableImage image) { @@ -1015,12 +989,12 @@ private void CropAndExportImage(final WritableImage image) { } final File file = filePicker.showSaveDialog(root.getScene().getWindow()); - if (file != null){ + if (file != null) { try { ImageIO.write(finalImage, "png", file); Ecdar.showToast("Export succeeded."); } catch (final IOException e) { - Ecdar.showToast("Export failed. "+ e.getMessage()); + Ecdar.showToast("Export failed. " + e.getMessage()); } } else { Ecdar.showToast("Export was cancelled."); @@ -1029,6 +1003,7 @@ private void CropAndExportImage(final WritableImage image) { /** * Crops an image so that the all-white borders are removed. + * * @param image the original image * @return the cropped image */ @@ -1044,6 +1019,7 @@ private static BufferedImage autoCropImage(final BufferedImage image) { /** * Gets the top y coordinate of an auto cropped image. + * * @param image the original image * @return the y coordinate */ @@ -1061,6 +1037,7 @@ private static int getAutoCropTopY(final BufferedImage image) { /** * Gets the left x coordinate of an auto cropped image. + * * @param image the original image * @return the x coordinate */ @@ -1078,6 +1055,7 @@ private static int getAutoCropLeftX(final BufferedImage image) { /** * Gets the bottom y coordinate of an auto cropped image. + * * @param image the original image * @return the y coordinate */ @@ -1095,6 +1073,7 @@ private static int getAutoCropBottomY(final BufferedImage image) { /** * Gets the right x coordinate of an auto cropped image. + * * @param image the original image * @return the x coordinate */ @@ -1291,11 +1270,12 @@ protected void interpolate(final double frac) { /** * This method is used as a central place to decide whether the tabPane is opened or closed + * * @param height the value used to set the height of the tabPane */ public void setMaxHeight(double height) { tabPaneContainer.setMaxHeight(height); - if(height > 35) { //The tabpane is opened + if (height > 35) { //The tabpane is opened filePane.showBottomInset(false); queryPane.showBottomInset(false); CanvasPresentation.showBottomInset(false); @@ -1328,7 +1308,7 @@ private void nudgeSelected(final NudgeDirection direction) { }); // If some one was not able to nudge disallow the current nudge and remove from the undo stack - if(foundUnNudgableElement[0]){ + if (foundUnNudgableElement[0]) { nudgedElements.forEach(nudgedElement -> nudgedElement.nudge(direction.reverse())); UndoRedoStack.forgetLast(); } @@ -1353,7 +1333,9 @@ private void deleteSelectedClicked() { final DisplayableEdge edge = ((EdgeController) selectable).getEdge(); // Dont delete edge if it is locked - if(edge.getIsLockedProperty().getValue()){return;} + if (edge.getIsLockedProperty().getValue()) { + return; + } UndoRedoStack.pushAndPerform(() -> { // Perform // Remove the edge @@ -1410,6 +1392,7 @@ private void switchEdgeStatusClicked() { /** * Sets the global edge status. + * * @param status the status */ private void setGlobalEdgeStatus(EdgeStatus status) { @@ -1422,18 +1405,6 @@ private void closeQueryDialog() { queryDialog.close(); } - @FXML - private void closeBackendDialog() { - dialog.close(); - backendOptionsDialog.close(); - } - - @FXML - private void saveChangesToBackendOptions() { - Ecdar.preferences.put("default_backend", BackendHelper.getDefaultBackend().getName()); - this.closeBackendDialog(); - } - public static void openQueryDialog(final Query query, final String text) { if (text != null) { _queryTextResult.setText(text); diff --git a/src/main/java/ecdar/presentations/BackendInstancePresentation.java b/src/main/java/ecdar/presentations/BackendInstancePresentation.java index 7299a904..52d9029b 100644 --- a/src/main/java/ecdar/presentations/BackendInstancePresentation.java +++ b/src/main/java/ecdar/presentations/BackendInstancePresentation.java @@ -16,7 +16,7 @@ public BackendInstancePresentation(BackendInstance backendInstance) { } public BackendInstancePresentation() { - controller = new EcdarFXMLLoader().loadAndGetController("BackendInstance.fxml", this); + controller = new EcdarFXMLLoader().loadAndGetController("BackendInstancePresentation.fxml", this); controller.pickPathToBackend.setCursor(Cursor.HAND); controller.pickPathToBackend.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); diff --git a/src/main/java/ecdar/presentations/BackendOptionsDialogPresentation.java b/src/main/java/ecdar/presentations/BackendOptionsDialogPresentation.java new file mode 100644 index 00000000..c3949083 --- /dev/null +++ b/src/main/java/ecdar/presentations/BackendOptionsDialogPresentation.java @@ -0,0 +1,16 @@ +package ecdar.presentations; + +import com.jfoenix.controls.JFXDialog; +import ecdar.controllers.BackendOptionsDialogController; + +public class BackendOptionsDialogPresentation extends JFXDialog { + private final BackendOptionsDialogController controller; + + public BackendOptionsDialogPresentation() { + controller = new EcdarFXMLLoader().loadAndGetController("BackendOptionsDialogPresentation.fxml", this); + } + + public BackendOptionsDialogController getController() { + return controller; + } +} diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 24976a8b..426ff915 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -266,3 +266,7 @@ -fx-border-color: -divider-color; -fx-border-width: 1px; } + +.input-violation { + -fx-text-fill: red; +} \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/BackendInstance.fxml b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml similarity index 96% rename from src/main/resources/ecdar/presentations/BackendInstance.fxml rename to src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml index ffc2a614..f442a6b4 100644 --- a/src/main/resources/ecdar/presentations/BackendInstance.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml @@ -7,6 +7,7 @@ + New backend + From 6dfb8687a7add13d86e515b26f6f32454e338afb Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 21 Nov 2021 16:50:01 +0100 Subject: [PATCH 12/32] WIP: ComboBox added for backend instances and refactoring (not executable) --- src/main/java/ecdar/abstractions/Query.java | 4 +- .../java/ecdar/backend/BackendHelper.java | 13 +++-- .../BackendInstanceController.java | 12 +++-- .../BackendOptionsDialogController.java | 41 ++++++++++++--- .../ecdar/controllers/QueryController.java | 10 ++-- .../presentations/QueryPresentation.java | 50 +++++++------------ .../BackendInstancePresentation.fxml | 2 +- .../presentations/QueryPresentation.fxml | 7 +-- 8 files changed, 79 insertions(+), 60 deletions(-) diff --git a/src/main/java/ecdar/abstractions/Query.java b/src/main/java/ecdar/abstractions/Query.java index f4301064..eb94cd19 100644 --- a/src/main/java/ecdar/abstractions/Query.java +++ b/src/main/java/ecdar/abstractions/Query.java @@ -37,7 +37,7 @@ public Query(final String query, final String comment, final QueryState querySta this.query.set(query); this.comment.set(comment); this.queryState.set(queryState); - setBackend(BackendHelper.defaultBackend); + setBackend(BackendHelper.getDefaultBackendInstance()); initializeRunQuery(); } @@ -221,7 +221,7 @@ public void deserialize(final JsonObject json) { if(json.has(BACKEND)) { setBackend(BackendHelper.getBackendInstanceByName(json.getAsJsonPrimitive(BACKEND).getAsString())); } else { - setBackend(BackendHelper.defaultBackend); + setBackend(BackendHelper.getDefaultBackendInstance()); } } diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index 06075d90..df63dc23 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -3,6 +3,8 @@ import com.uppaal.model.core2.Document; import ecdar.Ecdar; import ecdar.abstractions.*; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.ObservableList; import org.apache.commons.io.FileUtils; import java.io.File; @@ -19,8 +21,8 @@ public final class BackendHelper { final static String TEMP_DIRECTORY = "temporary"; private static EcdarDocument ecdarDocument; - public static BackendInstance defaultBackend = null; - private static List backendInstances = new ArrayList<>(); + private static BackendInstance defaultBackend = null; + private static ObservableList backendInstances = new SimpleListProperty<>(); public static String storeBackendModel(Project project, String fileName) throws BackendException, IOException, URISyntaxException { return storeBackendModel(project, TEMP_DIRECTORY, fileName); @@ -144,15 +146,16 @@ public static BackendInstance getBackendInstanceByName(String backendInstanceNam return backendInstance.orElse(null); } - public static BackendInstance getDefaultBackend() { + public static BackendInstance getDefaultBackendInstance() { return defaultBackend; } public static void setBackendInstances(List backendInstances) { - BackendHelper.backendInstances = backendInstances; + BackendHelper.backendInstances = new SimpleListProperty<>(); + BackendHelper.backendInstances.addAll(backendInstances); } - public static List getBackendInstances() { + public static ObservableList getBackendInstances() { return backendInstances; } diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index b925d5d0..856c5f96 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -5,6 +5,7 @@ import com.jfoenix.controls.JFXTextField; import ecdar.abstractions.BackendInstance; import javafx.application.Platform; +import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Cursor; @@ -13,6 +14,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import org.kordamp.ikonli.javafx.FontIcon; @@ -28,6 +30,7 @@ public class BackendInstanceController implements Initializable { public Label backendNameIssue; public FontIcon expansionIcon; public JFXRippler removeBackendRippler; + public FontIcon removeBackendIcon; public StackPane content; public HBox addressSection; public JFXTextField address; @@ -50,11 +53,13 @@ public void initialize(URL location, ResourceBundle resources) { setHGrow(); // Prevent deletion of default backend instance - removeBackendRippler.setDisable(defaultBackendRadioButton.isSelected()); defaultBackendRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> { - removeBackendRippler.setDisable(newValue); + if (newValue) { + removeBackendIcon.setFill(Color.GREY); + } else { + removeBackendIcon.setFill(Color.BLACK); + } }); - // ToDo NIELS: Visualize remove rippler disabled }); } @@ -81,6 +86,7 @@ public void setBackendInstance(BackendInstance instance) { } public BackendInstance updateBackendInstance() { + System.out.println("Saving " + backendName.getText() + "..."); backendInstance.setName(backendName.getText()); backendInstance.setLocal(isLocal.isSelected()); backendInstance.setDefault(defaultBackendRadioButton.isSelected()); diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 893efd76..561abc6e 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -4,20 +4,16 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXRippler; import ecdar.Ecdar; import ecdar.abstractions.BackendInstance; import ecdar.backend.BackendHelper; import ecdar.presentations.BackendInstancePresentation; -import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.control.ToggleGroup; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; -import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import org.apache.commons.lang3.Range; @@ -28,6 +24,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.ResourceBundle; public class BackendOptionsDialogController implements Initializable { @@ -48,15 +45,20 @@ public void resetBackendOptions() { private void initializeBackendInstanceList() { String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; - final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backends", defaultBackendInstanceList)).getAsJsonObject(); - final JsonArray backends = jsonObject.getAsJsonArray("backends"); + final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList)).getAsJsonObject(); + final JsonArray backends = jsonObject.getAsJsonArray(); + + ArrayList backendInstances = new ArrayList<>(); backends.forEach((backend) -> { BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); addBackendInstancePresentationToList(newBackendInstancePresentation); + backendInstances.add(newBackendInstance); }); + BackendHelper.setBackendInstances(backendInstances); + HBox.setHgrow(addBackendButton, Priority.ALWAYS); addBackendButton.setMaxWidth(Double.MAX_VALUE); addBackendButton.setOnMouseClicked((event) -> { @@ -69,7 +71,11 @@ private void addBackendInstancePresentationToList(BackendInstancePresentation ne backendInstanceList.getChildren().add(newBackendInstancePresentation); newBackendInstancePresentation.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, -1)); newBackendInstancePresentation.getController().moveBackendInstanceDownRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, +1)); - newBackendInstancePresentation.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> backendInstanceList.getChildren().remove(newBackendInstancePresentation)); + newBackendInstancePresentation.getController().removeBackendRippler.setOnMouseClicked((mouseEvent) -> { + if (!newBackendInstancePresentation.getController().defaultBackendRadioButton.isSelected()) { + backendInstanceList.getChildren().remove(newBackendInstancePresentation); + } + }); newBackendInstancePresentation.getController().defaultBackendRadioButton.setToggleGroup(defaultBackendToggleGroup); } @@ -171,7 +177,26 @@ private boolean backendInstanceLocationIsErrorFree(BackendInstanceController bac */ public boolean saveChangesToBackendOptions() { if (this.backendInstaceListIsErrorFree()) { - Ecdar.preferences.put("default_backend", BackendHelper.getDefaultBackend().getName()); + ArrayList backendInstances = new ArrayList<>(); + for (Node backendInstance : backendInstanceList.getChildren()) { + if (backendInstance instanceof BackendInstancePresentation) { + backendInstances.add(((BackendInstancePresentation) backendInstance).getController().updateBackendInstance()); + } + } + + BackendHelper.setBackendInstances(backendInstances); + + JsonArray jsonArray = new JsonArray(); + for (BackendInstance bi : backendInstances) { + jsonArray.add(bi.serialize()); + } + + Ecdar.preferences.put("backend_instances", jsonArray.getAsString()); + + // The is always a default backend set, so isPresent check is unnecessary + String defaultBackendName = (backendInstances.stream().filter(BackendInstance::isDefault).findFirst().get().getName()); + Ecdar.preferences.put("default_backend", defaultBackendName); + return true; } else { return false; diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index c357bbd1..512e782f 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -1,5 +1,6 @@ package ecdar.controllers; +import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXRippler; import ecdar.abstractions.Query; import ecdar.abstractions.QueryType; @@ -20,9 +21,10 @@ public class QueryController implements Initializable { public JFXRippler actionButton; public JFXRippler queryTypeExpand; public Text queryTypeSymbol; + public JFXComboBox backendsDropdown; private Query query; private final Map queryTypeListElementsSelectedState = new HashMap<>(); - private final Tooltip noQueryTypeSatTooltip = new Tooltip("Please select a query type beneath the status icon"); + private final Tooltip noQueryTypeSetTooltip = new Tooltip("Please select a query type beneath the status icon"); @Override public void initialize(URL location, ResourceBundle resources) { @@ -36,13 +38,13 @@ public void setQuery(Query query) { actionButton.setDisable(false); ((FontIcon) actionButton.lookup("#actionButtonIcon")).setIconColor(Color.GREY.getColor(Color.Intensity.I900)); Platform.runLater(() -> { - Tooltip.uninstall(actionButton.getParent(), noQueryTypeSatTooltip); + Tooltip.uninstall(actionButton.getParent(), noQueryTypeSetTooltip); }); } else { actionButton.setDisable(true); ((FontIcon) actionButton.lookup("#actionButtonIcon")).setIconColor(Color.GREY.getColor(Color.Intensity.I500)); Platform.runLater(() -> { - Tooltip.install(actionButton.getParent(), noQueryTypeSatTooltip); + Tooltip.install(actionButton.getParent(), noQueryTypeSetTooltip); }); } })); @@ -57,7 +59,7 @@ private void initializeActionButton() { if (query.getType() == null) { actionButton.setDisable(true); ((FontIcon) actionButton.lookup("#actionButtonIcon")).setIconColor(Color.GREY.getColor(Color.Intensity.I500)); - Tooltip.install(actionButton.getParent(), noQueryTypeSatTooltip); + Tooltip.install(actionButton.getParent(), noQueryTypeSetTooltip); } }); } diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index f07f509d..84c363fe 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -10,6 +10,7 @@ import javafx.application.Platform; import javafx.beans.binding.When; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Cursor; @@ -22,11 +23,9 @@ import javafx.util.Pair; import org.kordamp.ikonli.javafx.FontIcon; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import static javafx.scene.paint.Color.*; @@ -48,10 +47,16 @@ public QueryPresentation(final Query query) { initializeDetailsButton(); initializeTextFields(); initializeInputOutputPaneAndAddIgnoredInputOutputs(); - // initializeSwapBackendButton(); + initializeBackendsDropdown(); initializeMoreInformationButtonAndQueryTypeSymbol(); } + private void initializeBackendsDropdown() { + SimpleListProperty backendNames = new SimpleListProperty<>(); + backendNames.addAll(BackendHelper.getBackendInstances().stream().map(BackendInstance::getName).collect(Collectors.toList())); + controller.backendsDropdown.setItems(backendNames); + } + private void initializeTextFields() { Platform.runLater(() -> { final JFXTextField queryTextField = (JFXTextField) lookup("#query"); @@ -317,32 +322,15 @@ private void initializeResetInputOutputPaneButton(TitledPane inputOutputPane, }); }); } -// -// private void initializeSwapBackendButton() { -// Platform.runLater(() -> { -// final JFXRippler swapBackendButton = (JFXRippler) lookup("#swapBackendButton"); -// final TitledPane inputOutputPane = (TitledPane) lookup("#inputOutputPane"); -// this.currentBackendLabel = (Label) lookup("#currentBackendLabel"); -// -// swapBackendButton.setCursor(Cursor.HAND); -// swapBackendButton.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); -// swapBackendButton.setMaskType(JFXRippler.RipplerMask.CIRCLE); -// swapBackendButton.setOnMousePressed(event -> { -// // Set the backend to the one not currently used and update GUI -// final BackendHelper.BackendNames newBackend = (this.controller.getQuery().getBackend().equals(BackendHelper.BackendNames.jEcdar) -// ? BackendHelper.BackendNames.Reveaal -// : BackendHelper.BackendNames.jEcdar); -// -// this.controller.getQuery().setBackend(newBackend); -// setSwapBackendTooltipAndLabel(newBackend); -// updateTitlePaneVisibility(inputOutputPane, controller.getQuery().getQuery()); -// }); -// -// swapBackendButtonTooltip = new Tooltip(); -// setSwapBackendTooltipAndLabel(this.controller.getQuery().getBackend()); -// JFXTooltip.install(swapBackendButton, swapBackendButtonTooltip); -// }); -// } + + private void initializeBackendDropdown() { + Platform.runLater(() -> { + this.currentBackendLabel = (Label) lookup("#currentBackendLabel"); + swapBackendButtonTooltip = new Tooltip(); + swapBackendButtonTooltip.setText("Current backend used for the query"); + JFXTooltip.install(controller.backendsDropdown, swapBackendButtonTooltip); + }); + } private void updateTitlePaneVisibility(TitledPane inputOutputPane, String queryString) { // Check if the query is a refinement and that the engine is set to Reveaal diff --git a/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml index f442a6b4..b85c8ec5 100644 --- a/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml @@ -44,7 +44,7 @@ - + diff --git a/src/main/resources/ecdar/presentations/QueryPresentation.fxml b/src/main/resources/ecdar/presentations/QueryPresentation.fxml index bd8439c2..366009ea 100644 --- a/src/main/resources/ecdar/presentations/QueryPresentation.fxml +++ b/src/main/resources/ecdar/presentations/QueryPresentation.fxml @@ -78,12 +78,7 @@ - + From 792c4ba9f638fc565ae8989c7eabf8c9b718b59c Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Mon, 22 Nov 2021 08:32:01 +0100 Subject: [PATCH 13/32] WIP: Preferences update and validation label added to backend name --- src/main/java/ecdar/backend/BackendHelper.java | 4 ++-- .../java/ecdar/controllers/BackendInstanceController.java | 5 ++++- .../ecdar/controllers/BackendOptionsDialogController.java | 7 +++---- src/main/java/ecdar/presentations/QueryPresentation.java | 5 +++-- .../ecdar/presentations/BackendInstancePresentation.fxml | 6 ++++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index df63dc23..f874c2c9 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -4,6 +4,7 @@ import ecdar.Ecdar; import ecdar.abstractions.*; import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; import org.apache.commons.io.FileUtils; @@ -151,8 +152,7 @@ public static BackendInstance getDefaultBackendInstance() { } public static void setBackendInstances(List backendInstances) { - BackendHelper.backendInstances = new SimpleListProperty<>(); - BackendHelper.backendInstances.addAll(backendInstances); + BackendHelper.backendInstances = FXCollections.observableList(backendInstances); } public static ObservableList getBackendInstances() { diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index 856c5f96..ac382a54 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -60,6 +60,8 @@ public void initialize(URL location, ResourceBundle resources) { removeBackendIcon.setFill(Color.BLACK); } }); + + if (defaultBackendRadioButton.isSelected()) removeBackendIcon.setFill(Color.GREY); }); } @@ -98,7 +100,8 @@ public BackendInstance updateBackendInstance() { } private void setHGrow() { - HBox.setHgrow(backendName.getParent().getParent(), Priority.ALWAYS); + HBox.setHgrow(backendName.getParent().getParent().getParent(), Priority.ALWAYS); + HBox.setHgrow(backendName.getParent(), Priority.ALWAYS); HBox.setHgrow(backendName, Priority.ALWAYS); HBox.setHgrow(content, Priority.ALWAYS); HBox.setHgrow(addressSection, Priority.ALWAYS); diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 561abc6e..ca8994a4 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -44,9 +44,8 @@ public void resetBackendOptions() { } private void initializeBackendInstanceList() { - String defaultBackendInstanceList = "{'backends': [{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]}"; - final JsonObject jsonObject = JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList)).getAsJsonObject(); - final JsonArray backends = jsonObject.getAsJsonArray(); + String defaultBackendInstanceList = "[{'name': 'Reveaal', 'isLocal': 'true', 'isDefault': 'true', 'location': 'src/Reveaal', 'portRangeStart': '5032', 'portRangeEnd': '5040'},{'name': 'jECDAR', 'isLocal': 'True', 'isDefault': 'False', 'location': 'src/libs/j-Ecdar.jar', 'portRangeStart': '5042', 'portRangeEnd': '5050'}]"; + final JsonArray backends = JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList)).getAsJsonArray(); ArrayList backendInstances = new ArrayList<>(); @@ -191,7 +190,7 @@ public boolean saveChangesToBackendOptions() { jsonArray.add(bi.serialize()); } - Ecdar.preferences.put("backend_instances", jsonArray.getAsString()); + Ecdar.preferences.put("backend_instances", jsonArray.toString()); // The is always a default backend set, so isPresent check is unnecessary String defaultBackendName = (backendInstances.stream().filter(BackendInstance::isDefault).findFirst().get().getName()); diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index 84c363fe..f8bc8e87 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -11,6 +11,8 @@ import javafx.beans.binding.When; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Cursor; @@ -52,8 +54,7 @@ public QueryPresentation(final Query query) { } private void initializeBackendsDropdown() { - SimpleListProperty backendNames = new SimpleListProperty<>(); - backendNames.addAll(BackendHelper.getBackendInstances().stream().map(BackendInstance::getName).collect(Collectors.toList())); + ObservableList backendNames = FXCollections.observableList(BackendHelper.getBackendInstances().stream().map(BackendInstance::getName).collect(Collectors.toList())); controller.backendsDropdown.setItems(backendNames); } diff --git a/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml index b85c8ec5..328c585c 100644 --- a/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml +++ b/src/main/resources/ecdar/presentations/BackendInstancePresentation.fxml @@ -32,8 +32,10 @@ - New backend - - + From b2421a8e2cae41c03b4855b38539471138d0bc06 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 28 Dec 2021 22:48:10 +0100 Subject: [PATCH 19/32] Queries executable using Reveaal and default backend issue fixed --- .gitignore | 3 +++ .gitmodules | 2 +- src/main/java/ecdar/abstractions/Project.java | 2 +- src/main/java/ecdar/backend/BackendDriver.java | 10 ++++++++++ src/main/java/ecdar/backend/BackendHelper.java | 5 +++-- .../controllers/BackendOptionsDialogController.java | 2 +- src/main/java/ecdar/controllers/EcdarController.java | 4 ++++ src/main/java/ecdar/controllers/QueryController.java | 5 +++++ .../java/ecdar/presentations/QueryPresentation.java | 2 +- src/main/proto | 1 + src/main/proto/README.md | 3 --- 11 files changed, 30 insertions(+), 9 deletions(-) create mode 160000 src/main/proto delete mode 100644 src/main/proto/README.md diff --git a/.gitignore b/.gitignore index ea0fc28a..59558236 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk + +# The backends +/src/Reveaal diff --git a/.gitmodules b/.gitmodules index 10a3eac1..f3837e3f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/main/proto"] path = src/main/proto - url = git@github.com:Ecdar/Ecdar-ProtoBuf.git + url = https://github.com/Ecdar/Ecdar-ProtoBuf diff --git a/src/main/java/ecdar/abstractions/Project.java b/src/main/java/ecdar/abstractions/Project.java index efd11e6f..f0b132bf 100644 --- a/src/main/java/ecdar/abstractions/Project.java +++ b/src/main/java/ecdar/abstractions/Project.java @@ -169,7 +169,7 @@ public void deserialize(final File projectFolder) throws IOException { deserializeFileHelper(file); } } - // Now we have gone though all the files in the directory we can now deserialize folders + // Now we have gone through all the files in the directory we can now deserialize folders if(componentFolder != null || systemFolder != null) { deserializeComponents(componentFolder); deserializeSystems(systemFolder); diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 38c9cc30..002a50e9 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -382,7 +382,17 @@ public ExecutableQuery getExecutableQuery() { return executableQuery; } + /** + * Get the backend instance that should be used to execute + * the query currently associated with this backend connection + * + * @return the instance of the associated executable query object, + * or null, if no executable query is currently associated + */ public BackendInstance getBackendInstance() { + if (executableQuery == null) { + return null; + } return executableQuery.backend; } diff --git a/src/main/java/ecdar/backend/BackendHelper.java b/src/main/java/ecdar/backend/BackendHelper.java index 4d5c535e..b34e098d 100644 --- a/src/main/java/ecdar/backend/BackendHelper.java +++ b/src/main/java/ecdar/backend/BackendHelper.java @@ -147,11 +147,12 @@ public static String getExistDeadlockQuery(final Component component) { * Returns the BackendInstance with the specified name, or null, if no such BackendInstance exists * * @param backendInstanceName Name of the BackendInstance to return - * @return The BackendInstance with matching name or null if such a BackendInstance does not exist + * @return The BackendInstance with matching name + * or the default backend instance, if no matching backendInstance exists */ public static BackendInstance getBackendInstanceByName(String backendInstanceName) { Optional backendInstance = BackendHelper.backendInstances.stream().filter(bi -> bi.getName().equals(backendInstanceName)).findFirst(); - return backendInstance.orElse(null); + return backendInstance.orElse(BackendHelper.getDefaultBackendInstance()); } /** diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 61be0016..ab21b189 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -262,7 +262,7 @@ public boolean saveChangesToBackendOptions() { Ecdar.preferences.put("backend_instances", jsonArray.toString()); - // The is always a default backend set, so isPresent check is unnecessary + // There is always a default backend set, so isPresent check is unnecessary BackendInstance defaultBackend = backendInstances.stream().filter(BackendInstance::isDefault).findFirst().get(); BackendHelper.setDefaultBackendInstance(defaultBackend); diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index 39e2c052..b31e6c24 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -224,6 +224,10 @@ private void initilizeDialogs() { backendOptionsDialog.close(); } }); + + // Set default backend instance + BackendInstance defaultBackend = BackendHelper.getBackendInstances().stream().filter(BackendInstance::isDefault).findFirst().get(); + BackendHelper.setDefaultBackendInstance(defaultBackend); } private void initializeDialog(JFXDialog dialog, StackPane dialogContainer) { diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index c9242a0b..64824088 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -49,6 +49,11 @@ public void setQuery(Query query) { }); } })); + + backendsDropdown.setValue(query.getBackend()); + backendsDropdown.valueProperty().addListener((observable, oldValue, newValue) -> { + query.setBackend(newValue); + }); } public Query getQuery() { diff --git a/src/main/java/ecdar/presentations/QueryPresentation.java b/src/main/java/ecdar/presentations/QueryPresentation.java index cb238260..464f5d57 100644 --- a/src/main/java/ecdar/presentations/QueryPresentation.java +++ b/src/main/java/ecdar/presentations/QueryPresentation.java @@ -40,8 +40,8 @@ public QueryPresentation(final Query query) { initializeDetailsButton(); initializeTextFields(); initializeInputOutputPaneAndAddIgnoredInputOutputs(); - initializeBackendsDropdown(); initializeMoreInformationButtonAndQueryTypeSymbol(); + initializeBackendsDropdown(); } private void initializeBackendsDropdown() { diff --git a/src/main/proto b/src/main/proto new file mode 160000 index 00000000..b4d83889 --- /dev/null +++ b/src/main/proto @@ -0,0 +1 @@ +Subproject commit b4d83889935f6f6e6d0a0a5547c28b2ab1388cd1 diff --git a/src/main/proto/README.md b/src/main/proto/README.md deleted file mode 100644 index dc419d1c..00000000 --- a/src/main/proto/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Ecdar-ProtoBuf -This repository is used to organize and maintain the Protocol Buffers used across the ECDAR modules. -The messages are used to communicate between the [Ecdar-GUI](https://github.com/Ecdar/Ecdar-GUI) and the two back-end model checks: [REVEAAL](https://github.com/Ecdar/Reveaal) and [J-ECDAR](https://github.com/Ecdar/j-Ecdar). From fb9da280b790461af46d3d73eac88882560009c0 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 16 Jan 2022 11:08:14 +0100 Subject: [PATCH 20/32] WIP: Implementing remote backend communication --- .../java/ecdar/backend/BackendDriver.java | 65 +++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 9b7001f9..1f8fffbf 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -291,30 +291,57 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe } private BackendConnection startNewBackendConnection(BackendInstance backend) { - Process p; + Process p = null; String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); - int portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); + int portNumber = 0; + + try { + portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); + } catch (IllegalStateException e) { + // No free port could be found + } // ToDo NIELS: Check for the number of open connections to ensure that we do not exceed the number of desired backend // Possibly just try all ports in range and exiting after reaching the highest numbered port - do { - ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); - try { - p = pb.start(); - } catch (IOException ioException) { - Ecdar.showToast("Unable to start backend instance. Check the error tab for more details."); - // ToDo NIELS: Add error to errors tab with text: - // "The backend instance could not be started. Make sure that the following is correct: - // - Path/address - // - At least one port in the port range is free for the given address (localhost if backend is set to local) - // - The backend is an executable or a .jar file - // - The backend supports the '-p {host}:{port}' flag on startup - ioException.printStackTrace(); - return null; - } - // If the process is not alive, it failed while starting up, try again - } while (!p.isAlive()); + if (backend.isLocal()) { + do { + ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); +// ToDo NIELS: Find out if we should still use path search (BELOW IS FROM FIXED BACKEND IMPLEMENTATION) +// File engine = null; +// if (isReveaal) { +// List searchPath = List.of ( +// new File("lib/Reveaal.exe"), new File("lib/Reveaal") +// ); +// for (var f: searchPath){ +// if (f.exists()) { +// engine = f; +// break; +// } +// } +// if (engine == null) { +// throw new RuntimeException("Could not locate Reveaal engine"); +// } +// pb = new ProcessBuilder(engine.getAbsolutePath(), "-p", this.hostAddress + ":" + portNumber); +// } else { +// pb = new ProcessBuilder("java", "-jar", "lib/j-Ecdar.jar", "-p" + portNumber ); +// } + try { + p = pb.start(); + } catch (IOException ioException) { + Ecdar.showToast("Unable to start backend instance. Check the error tab for more details."); + // ToDo NIELS: Add error to errors tab with text: + // "The backend instance could not be started. Make sure that the following is correct: + // - Path/address + // - At least one port in the port range is free for the given address (localhost if backend is set to local) + // - The backend is an executable or a .jar file + // - The backend supports the '-p {host}:{port}' flag on startup + ioException.printStackTrace(); + return null; + } + // If the process is not alive, it failed while starting up, try again + } while (!p.isAlive()); + } ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) .usePlaintext() From a81161bf9140dcaa8e7c4a4bec2f18401f53608e Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Tue, 1 Feb 2022 15:25:40 +0100 Subject: [PATCH 21/32] Nullpointer exception fixed and local consistency updated to correspond with Reveaal --- src/main/java/ecdar/abstractions/QueryType.java | 2 +- src/main/java/ecdar/backend/BackendDriver.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ecdar/abstractions/QueryType.java b/src/main/java/ecdar/abstractions/QueryType.java index 4c399601..3864ecaa 100644 --- a/src/main/java/ecdar/abstractions/QueryType.java +++ b/src/main/java/ecdar/abstractions/QueryType.java @@ -5,7 +5,7 @@ public enum QueryType { QUOTIENT("quotient", "\\"), SPECIFICATION("specification", "Spec"), IMPLEMENTATION("implementation", "Imp"), - LOCAL_CONSISTENCY("local-consistency", "lCon"), + LOCAL_CONSISTENCY("consistency", "lCon"), // ToDo NIELS: Will become local-consistency GLOBAL_CONSISTENCY("global-consistency", "gCon"), BISIM_MIN("bisim", "bsim"), GET_COMPONENT("get-component", "get"), diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 1f8fffbf..e39d6fe4 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -139,7 +139,7 @@ private void executeQuery(ExecutableQuery executableQuery) { // Get available connection or start new final BackendConnection backendConnection = openBackendConnections.stream() - .filter((connection) -> connection.getBackendInstance().equals(executableQuery.backend)) + .filter((connection) -> connection.getBackendInstance() != null && connection.getBackendInstance().equals(executableQuery.backend)) .findFirst() .orElseGet(() -> startNewBackendConnection(executableQuery.backend)); From ed5191263b1da6330da9ae12867f5b38d1d53511 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sat, 5 Feb 2022 09:43:17 +0100 Subject: [PATCH 22/32] WIP: j-Ecdar communication --- .gitignore | 1 + src/main/java/ecdar/backend/BackendDriver.java | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5a7310a5..69ed61c1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ lib/libJDBM.so lib/libJDBM.dylib lib/JDBM.dll lib/j-ecdar.jar +lib/j-Ecdar.bat lib/*.dll diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index e39d6fe4..624a012d 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -289,7 +289,7 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe executableQuery.success.accept(false); } } - + private BackendConnection startNewBackendConnection(BackendInstance backend) { Process p = null; String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); @@ -306,7 +306,7 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { if (backend.isLocal()) { do { - ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber).redirectErrorStream(true); + ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", "" + portNumber).redirectErrorStream(true); // ToDo NIELS: Find out if we should still use path search (BELOW IS FROM FIXED BACKEND IMPLEMENTATION) // File engine = null; // if (isReveaal) { @@ -326,6 +326,8 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { // } else { // pb = new ProcessBuilder("java", "-jar", "lib/j-Ecdar.jar", "-p" + portNumber ); // } + pb.inheritIO().redirectErrorStream(true); + try { p = pb.start(); } catch (IOException ioException) { From a80056d788de90326aa80c6efa02e818f16ea1e7 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 6 Feb 2022 08:39:29 +0100 Subject: [PATCH 23/32] WIP: Remote backend instance testing and minor clean-up --- src/main/java/ecdar/backend/BackendDriver.java | 7 +++++-- .../java/ecdar/controllers/BackendInstanceController.java | 2 -- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 624a012d..1dc4de69 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -336,7 +336,7 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { // "The backend instance could not be started. Make sure that the following is correct: // - Path/address // - At least one port in the port range is free for the given address (localhost if backend is set to local) - // - The backend is an executable or a .jar file + // - The backend is an executable // - The backend supports the '-p {host}:{port}' flag on startup ioException.printStackTrace(); return null; @@ -455,7 +455,10 @@ public void close() throws IOException { System.out.println("Tried to remove a connection not present in either connection list"); } - process.destroy(); + // If the backend-instance is null, or it is a remote process, we do not need to destroy it + if (this.getBackendInstance() != null && !this.getBackendInstance().isLocal()) { + process.destroy(); + } } } diff --git a/src/main/java/ecdar/controllers/BackendInstanceController.java b/src/main/java/ecdar/controllers/BackendInstanceController.java index cd19e768..7ff90b3d 100644 --- a/src/main/java/ecdar/controllers/BackendInstanceController.java +++ b/src/main/java/ecdar/controllers/BackendInstanceController.java @@ -118,14 +118,12 @@ private void setHGrow() { private void handleLocalPropertyChanged() { if (isLocal.isSelected()) { address.setDisable(true); - address.setText("127.0.0.1"); addressSection.setVisible(false); addressSection.setManaged(false); pathToBackendSection.setVisible(true); pathToBackendSection.setManaged(true); } else { address.setDisable(false); - address.setText(""); addressSection.setVisible(true); addressSection.setManaged(true); pathToBackendSection.setVisible(false); From 32d8537bb9fe5ded0da2ff41cc3dda0bc35eaf84 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 6 Feb 2022 10:02:23 +0100 Subject: [PATCH 24/32] Clean-up, queries set to deleted backend is set to default, move backend instances fixed, and basically ready for merge with master --- .gitignore | 3 -- .gitmodules | 2 +- src/main/java/ecdar/Ecdar.java | 12 ----- .../java/ecdar/backend/BackendDriver.java | 44 ++----------------- .../BackendOptionsDialogController.java | 37 ++++++++-------- .../ecdar/controllers/QueryController.java | 7 ++- .../resources/ecdar/default_backends.json | 4 +- src/main/resources/ecdar/main.css | 22 ---------- 8 files changed, 31 insertions(+), 100 deletions(-) diff --git a/.gitignore b/.gitignore index 69ed61c1..2ee647ef 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,3 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk - -# The backends -/src/Reveaal diff --git a/.gitmodules b/.gitmodules index f3837e3f..10a3eac1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/main/proto"] path = src/main/proto - url = https://github.com/Ecdar/Ecdar-ProtoBuf + url = git@github.com:Ecdar/Ecdar-ProtoBuf.git diff --git a/src/main/java/ecdar/Ecdar.java b/src/main/java/ecdar/Ecdar.java index 8487b962..ea170b62 100644 --- a/src/main/java/ecdar/Ecdar.java +++ b/src/main/java/ecdar/Ecdar.java @@ -190,7 +190,6 @@ public void start(final Stage stage) { // Load the fonts required for the project IconFontFX.register(GoogleMaterialDesignIcons.getIconFont()); loadFonts(); - loadBackends(); // Remove the classic decoration // kyrke - 2020-04-17: Disabled due to bug https://bugs.openjdk.java.net/browse/JDK-8154847 @@ -291,17 +290,6 @@ public void start(final Stage stage) { }); } - private void loadBackends() { - // ToDo NIELS: Load backends -// BackendHelper.defaultBackend = preferences.getInt("default_backend", BackendHelper.BackendNames.jEcdar.ordinal()) -// == BackendHelper.BackendNames.jEcdar.ordinal() -// ? BackendHelper.BackendNames.jEcdar -// : BackendHelper.BackendNames.Reveaal; -// -// backendDriver = new BackendDriver(preferences.get("backend_host_address", "127.0.0.1")); -// getBackendDriver().setMaxNumberOfConnections(preferences.getInt("number_of_backend_sockets", 5)); - } - /** * Initializes and resets the project. * This can be used as a test setup. diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 1dc4de69..ba368b42 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -254,13 +254,6 @@ public void run() { default: backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); - -// try { -// backendConnection.close(); -// } catch (IOException e) { -// e.printStackTrace(); -// } - break; } @@ -298,46 +291,17 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { try { portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); } catch (IllegalStateException e) { - // No free port could be found + Ecdar.showToast("No available port for " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); } - // ToDo NIELS: Check for the number of open connections to ensure that we do not exceed the number of desired backend - // Possibly just try all ports in range and exiting after reaching the highest numbered port - if (backend.isLocal()) { do { - ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", "" + portNumber).redirectErrorStream(true); -// ToDo NIELS: Find out if we should still use path search (BELOW IS FROM FIXED BACKEND IMPLEMENTATION) -// File engine = null; -// if (isReveaal) { -// List searchPath = List.of ( -// new File("lib/Reveaal.exe"), new File("lib/Reveaal") -// ); -// for (var f: searchPath){ -// if (f.exists()) { -// engine = f; -// break; -// } -// } -// if (engine == null) { -// throw new RuntimeException("Could not locate Reveaal engine"); -// } -// pb = new ProcessBuilder(engine.getAbsolutePath(), "-p", this.hostAddress + ":" + portNumber); -// } else { -// pb = new ProcessBuilder("java", "-jar", "lib/j-Ecdar.jar", "-p" + portNumber ); -// } - pb.inheritIO().redirectErrorStream(true); + ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber); try { p = pb.start(); } catch (IOException ioException) { - Ecdar.showToast("Unable to start backend instance. Check the error tab for more details."); - // ToDo NIELS: Add error to errors tab with text: - // "The backend instance could not be started. Make sure that the following is correct: - // - Path/address - // - At least one port in the port range is free for the given address (localhost if backend is set to local) - // - The backend is an executable - // - The backend supports the '-p {host}:{port}' flag on startup + Ecdar.showToast("Unable to start backend instance"); ioException.printStackTrace(); return null; } @@ -455,7 +419,7 @@ public void close() throws IOException { System.out.println("Tried to remove a connection not present in either connection list"); } - // If the backend-instance is null, or it is a remote process, we do not need to destroy it + // If the backend-instance is null, or remote, there will be no process to destroy if (this.getBackendInstance() != null && !this.getBackendInstance().isLocal()) { process.destroy(); } diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index ab21b189..a0ff87af 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -48,20 +48,10 @@ public void resetBackendOptions() { private void initializeBackendInstanceList() { try { - // ToDo NIELS: Maybe use load JSON from file function instead of reading from file like this - File myObj = new File("src/main/resources/ecdar/default_backends.json"); - Scanner myReader = new Scanner(myObj); - - StringBuilder defaultBackendInstanceList = new StringBuilder(); - while (myReader.hasNextLine()) { - defaultBackendInstanceList.append(myReader.nextLine()); - } - myReader.close(); - - final JsonArray backends = JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList.toString())).getAsJsonArray(); + final JsonArray backends = getSavedBackendInstances(); + // Instantiate loaded backend instances ArrayList backendInstances = new ArrayList<>(); - backends.forEach((backend) -> { BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); @@ -71,7 +61,7 @@ private void initializeBackendInstanceList() { BackendHelper.updateBackendInstances(backendInstances); } catch (FileNotFoundException e) { - System.out.println("An error occurred."); + Ecdar.showToast("Unable to load backends from either preferences or JSON with default backends"); e.printStackTrace(); } @@ -83,6 +73,17 @@ private void initializeBackendInstanceList() { }); } + private JsonArray getSavedBackendInstances() throws FileNotFoundException { + File myObj = new File("src/main/resources/ecdar/default_backends.json"); + Scanner myReader = new Scanner(myObj); + StringBuilder defaultBackendInstanceList = new StringBuilder(); + while (myReader.hasNextLine()) { + defaultBackendInstanceList.append(myReader.nextLine()); + } + myReader.close(); + return JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList.toString())).getAsJsonArray(); + } + private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { backendInstanceList.getChildren().add(newBackendInstancePresentation); newBackendInstancePresentation.getController().moveBackendInstanceUpRippler.setOnMouseClicked((mouseEvent) -> moveBackendInstance(newBackendInstancePresentation, -1)); @@ -97,10 +98,10 @@ private void addBackendInstancePresentationToList(BackendInstancePresentation ne private void moveBackendInstance(BackendInstancePresentation newBackendInstance, int i) { int currentIndex = backendInstanceList.getChildren().indexOf(newBackendInstance); - - // Math.max added to avoid index -1 - int newIndex = Math.max(0, (currentIndex + i) % backendInstanceList.getChildren().size()); - // ToDo NIELS: Prevent loop around for overflow or add for underflow + int newIndex = (currentIndex + i) % backendInstanceList.getChildren().size(); + if (newIndex < 0) { + newIndex = backendInstanceList.getChildren().size() - 1; + } backendInstanceList.getChildren().remove(newBackendInstance); backendInstanceList.getChildren().add(newIndex, newBackendInstance); @@ -117,7 +118,6 @@ private boolean backendInstaceListIsErrorFree() { for (Node child : backendInstanceList.getChildren()) { if (child instanceof BackendInstancePresentation) { BackendInstanceController backendInstanceController = ((BackendInstancePresentation) child).getController(); - error = backendNameIsErrorFree(backendInstanceController) && error; error = portRangeIsErrorFree(backendInstanceController) && error; error = backendInstanceLocationIsErrorFree(backendInstanceController) && error; @@ -228,7 +228,6 @@ private boolean backendInstanceLocationIsErrorFree(BackendInstanceController bac errorFree = false; } catch (IOException ioException) { backendInstanceController.locationIssue.setText(ValidationErrorMessages.IO_EXCEPTION_WITH_HOST.toString()); - // ToDo NIELS: Log the following: ioException.getMessage(); errorFree = false; } } diff --git a/src/main/java/ecdar/controllers/QueryController.java b/src/main/java/ecdar/controllers/QueryController.java index 64824088..114f867a 100644 --- a/src/main/java/ecdar/controllers/QueryController.java +++ b/src/main/java/ecdar/controllers/QueryController.java @@ -5,6 +5,7 @@ import ecdar.abstractions.BackendInstance; import ecdar.abstractions.Query; import ecdar.abstractions.QueryType; +import ecdar.backend.BackendHelper; import ecdar.utility.colors.Color; import javafx.application.Platform; import javafx.beans.property.SimpleBooleanProperty; @@ -52,7 +53,11 @@ public void setQuery(Query query) { backendsDropdown.setValue(query.getBackend()); backendsDropdown.valueProperty().addListener((observable, oldValue, newValue) -> { - query.setBackend(newValue); + if (newValue != null) { + query.setBackend(newValue); + } else { + backendsDropdown.setValue(BackendHelper.getDefaultBackendInstance()); + } }); } diff --git a/src/main/resources/ecdar/default_backends.json b/src/main/resources/ecdar/default_backends.json index 89e77fa1..a6b2c35b 100644 --- a/src/main/resources/ecdar/default_backends.json +++ b/src/main/resources/ecdar/default_backends.json @@ -3,7 +3,7 @@ "name": "Reveaal", "isLocal": "true", "isDefault": "true", - "location": "src/Reveaal", + "location": "lib/Reveaal", "portRangeStart": "5032", "portRangeEnd": "5040" }, @@ -11,7 +11,7 @@ "name": "jECDAR", "isLocal": "True", "isDefault": "False", - "location": "src/libs/j-Ecdar.jar", + "location": "lib/j-Ecdar.bat", "portRangeStart": "5042", "portRangeEnd": "5050" } diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 426ff915..3f550b0c 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -232,28 +232,6 @@ -fx-background-insets: 0 0 0 0; } -/*.jfx-slider { - -fx-pref-width: 60; -} - -.jfx-slider > .track { - -fx-background-color: -divider-color; -} - -.jfx-slider > .thumb, .jfx-slider > .animated-thumb, .jfx-slider > .colored-track{ - -fx-background-color: -primary-color; -} - -.jfx-slider > .thumb { - -fx-pref-width: 10; - -fx-background-radius: 3; -} - -.jfx-slider > .slider-value { - -fx-fill: -primary-color-darker; - -fx-stroke: -primary-color-darker; -}*/ - .backend-instances-list { -fx-padding: 5; -fx-border-style: SOLID HIDDEN SOLID HIDDEN; From f001f23272aacbf48bff6264e098871ab573bde8 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 6 Feb 2022 10:36:22 +0100 Subject: [PATCH 25/32] Reset backends to default button added --- .../BackendOptionsDialogController.java | 52 ++++++++++++------- .../ecdar/controllers/EcdarController.java | 4 ++ src/main/resources/ecdar/main.css | 7 +++ .../BackendOptionsDialogPresentation.fxml | 6 ++- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index a0ff87af..43d810fd 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -35,6 +35,7 @@ public class BackendOptionsDialogController implements Initializable { public JFXButton closeButton; public ToggleGroup defaultBackendToggleGroup = new ToggleGroup(); public JFXButton saveButton; + public JFXButton resetBackendsButton; @Override public void initialize(URL location, ResourceBundle resources) { @@ -48,18 +49,10 @@ public void resetBackendOptions() { private void initializeBackendInstanceList() { try { - final JsonArray backends = getSavedBackendInstances(); - - // Instantiate loaded backend instances - ArrayList backendInstances = new ArrayList<>(); - backends.forEach((backend) -> { - BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); - BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); - addBackendInstancePresentationToList(newBackendInstancePresentation); - backendInstances.add(newBackendInstance); - }); - - BackendHelper.updateBackendInstances(backendInstances); + setBackendsFromJsonArray( + JsonParser.parseString( + Ecdar.preferences.get("backend_instances", getDefaultBackends()) + ).getAsJsonArray()); } catch (FileNotFoundException e) { Ecdar.showToast("Unable to load backends from either preferences or JSON with default backends"); e.printStackTrace(); @@ -73,7 +66,7 @@ private void initializeBackendInstanceList() { }); } - private JsonArray getSavedBackendInstances() throws FileNotFoundException { + private String getDefaultBackends() throws FileNotFoundException { File myObj = new File("src/main/resources/ecdar/default_backends.json"); Scanner myReader = new Scanner(myObj); StringBuilder defaultBackendInstanceList = new StringBuilder(); @@ -81,7 +74,20 @@ private JsonArray getSavedBackendInstances() throws FileNotFoundException { defaultBackendInstanceList.append(myReader.nextLine()); } myReader.close(); - return JsonParser.parseString(Ecdar.preferences.get("backend_instances", defaultBackendInstanceList.toString())).getAsJsonArray(); + return defaultBackendInstanceList.toString(); + } + + private void setBackendsFromJsonArray(JsonArray backends) { + ArrayList backendInstances = new ArrayList<>(); + backendInstanceList.getChildren().clear(); + backends.forEach((backend) -> { + BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); + addBackendInstancePresentationToList(newBackendInstancePresentation); + backendInstances.add(newBackendInstance); + }); + + BackendHelper.updateBackendInstances(backendInstances); } private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { @@ -112,7 +118,7 @@ private void moveBackendInstance(BackendInstancePresentation newBackendInstance, * * @return whether any errors were found */ - private boolean backendInstaceListIsErrorFree() { + private boolean backendInstanceListIsErrorFree() { boolean error = true; for (Node child : backendInstanceList.getChildren()) { @@ -244,7 +250,7 @@ private boolean backendInstanceLocationIsErrorFree(BackendInstanceController bac * @return whether the changes could be saved */ public boolean saveChangesToBackendOptions() { - if (this.backendInstaceListIsErrorFree()) { + if (this.backendInstanceListIsErrorFree()) { ArrayList backendInstances = new ArrayList<>(); for (Node backendInstance : backendInstanceList.getChildren()) { if (backendInstance instanceof BackendInstancePresentation) { @@ -261,8 +267,7 @@ public boolean saveChangesToBackendOptions() { Ecdar.preferences.put("backend_instances", jsonArray.toString()); - // There is always a default backend set, so isPresent check is unnecessary - BackendInstance defaultBackend = backendInstances.stream().filter(BackendInstance::isDefault).findFirst().get(); + BackendInstance defaultBackend = backendInstances.stream().filter(BackendInstance::isDefault).findFirst().orElse(backendInstances.get(0)); BackendHelper.setDefaultBackendInstance(defaultBackend); String defaultBackendName = (defaultBackend.getName()); @@ -274,6 +279,17 @@ public boolean saveChangesToBackendOptions() { } } + /** + * Resets the backends to the default backends present in the 'default_backends.json' file + */ + public void resetBackendsToDefault() { + try { + setBackendsFromJsonArray(JsonParser.parseString(getDefaultBackends()).getAsJsonArray()); + } catch (FileNotFoundException e) { + Ecdar.showToast("An error occurred while trying to read the JSON file containing the default backends"); + } + } + private enum ValidationErrorMessages { BACKEND_NAME_EMPTY { @Override diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index b31e6c24..540904e9 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -212,6 +212,10 @@ private void initilizeDialogs() { initializeDialog(queryDialog, queryDialogContainer); initializeDialog(backendOptionsDialog, backendOptionsDialogContainer); + backendOptionsDialog.getController().resetBackendsButton.setOnMouseClicked(event -> { + backendOptionsDialog.getController().resetBackendsToDefault(); + }); + backendOptionsDialog.getController().closeButton.setOnMouseClicked(event -> { backendOptionsDialog.getController().resetBackendOptions(); dialog.close(); diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index 3f550b0c..e3c63ccb 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -247,4 +247,11 @@ .input-violation { -fx-text-fill: red; +} + +.button-danger { + -fx-border-style: SOLID SOLID SOLID SOLID; + -fx-border-color: -red-900; + -fx-border-radius: 0.25em; + -fx-border-width: 0.1em; } \ No newline at end of file diff --git a/src/main/resources/ecdar/presentations/BackendOptionsDialogPresentation.fxml b/src/main/resources/ecdar/presentations/BackendOptionsDialogPresentation.fxml index 595aa7b3..3a29787b 100644 --- a/src/main/resources/ecdar/presentations/BackendOptionsDialogPresentation.fxml +++ b/src/main/resources/ecdar/presentations/BackendOptionsDialogPresentation.fxml @@ -17,7 +17,11 @@ - Backends + + Backends + + + Date: Sun, 6 Feb 2022 10:37:35 +0100 Subject: [PATCH 26/32] Minor clean-up --- src/main/resources/ecdar/main.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/ecdar/main.css b/src/main/resources/ecdar/main.css index e3c63ccb..f8f2b7b2 100644 --- a/src/main/resources/ecdar/main.css +++ b/src/main/resources/ecdar/main.css @@ -204,7 +204,6 @@ .jfx-snackbar-toast { -fx-text-fill: WHITE; - -fx-padding-left: 24; -fx-padding-right: 24; } From 5075c11467c93acb18b302c2efeae1eb19f4afb1 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sat, 12 Feb 2022 11:01:09 +0100 Subject: [PATCH 27/32] Weird error when running all queries multiple times, connections being instantiation of new connection for each execution of a query, and processes not being closed after exit FIXED --- .../java/ecdar/backend/BackendDriver.java | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index ba368b42..75474989 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -126,7 +126,7 @@ public void onCompleted() { } /** - * Close all open backend connection + * Close all open backend connection and kill all locally running processes * * @throws IOException if any of the sockets do not respond */ @@ -139,7 +139,7 @@ private void executeQuery(ExecutableQuery executableQuery) { // Get available connection or start new final BackendConnection backendConnection = openBackendConnections.stream() - .filter((connection) -> connection.getBackendInstance() != null && connection.getBackendInstance().equals(executableQuery.backend)) + .filter((connection) -> connection.getBackendInstance() == null || connection.getBackendInstance().equals(executableQuery.backend)) .findFirst() .orElseGet(() -> startNewBackendConnection(executableQuery.backend)); @@ -176,12 +176,9 @@ public void onNext(Empty value) { @Override public void onError(Throwable t) { - if (executableQuery.queryListener.getQuery().getQueryState() == QueryState.UNKNOWN) - backendConnection.setExecutableQuery(null); - else { - handleBackendError(t, backendConnection); + if (executableQuery.queryListener.getQuery().getQueryState() != QueryState.UNKNOWN) { + handleBackendError(t, executableQuery); error = true; - backendConnection.setExecutableQuery(null); } } @@ -192,24 +189,26 @@ public void onCompleted() { @Override public void onNext(QueryProtos.QueryResponse value) { if (executableQuery.queryListener.getQuery().getQueryState() != QueryState.UNKNOWN) { - handleResponse(backendConnection.getExecutableQuery(), value); + handleResponse(executableQuery, value); } - backendConnection.setExecutableQuery(null); } @Override public void onError(Throwable t) { if (executableQuery.queryListener.getQuery().getQueryState() != QueryState.UNKNOWN) { - handleBackendError(t, backendConnection); + handleBackendError(t, executableQuery); } - backendConnection.setExecutableQuery(null); } @Override public void onCompleted() { + backendConnection.setExecutableQuery(null); } }; - backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS).sendQuery(QueryProtos.Query.newBuilder().setId(0).setQuery(backendConnection.getExecutableQuery().query).build(), responseObserver); + + backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS).sendQuery(QueryProtos.Query.newBuilder().setId(0).setQuery(executableQuery.query).build(), responseObserver); + } else { + backendConnection.setExecutableQuery(null); } } }; @@ -217,19 +216,18 @@ public void onCompleted() { backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS).updateComponents(componentsBuilder.build(), observer); } - private void handleBackendError(Throwable t, BackendConnection backendConnection) { + private void handleBackendError(Throwable t, ExecutableQuery query) { // Each error starts with a capitalized description of the error equal to the gRPC error type encountered String errorType = t.getMessage().split(":\\s+", 2)[0]; - final ExecutableQuery query = backendConnection.getExecutableQuery(); switch (errorType) { case "CANCELLED": - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The query was cancelled")); + query.queryListener.getQuery().setQueryState(QueryState.ERROR); + query.failure.accept(new BackendException.QueryErrorException("The query was cancelled")); break; case "DEADLINE_EXCEEDED": - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The backend did not answer the request in time")); + query.queryListener.getQuery().setQueryState(QueryState.ERROR); + query.failure.accept(new BackendException.QueryErrorException("The backend did not answer the request in time")); new Timer().schedule(new TimerTask() { @Override @@ -240,24 +238,26 @@ public void run() { break; case "UNIMPLEMENTED": - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.SYNTAX_ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The query type is not supported by the backend")); + query.queryListener.getQuery().setQueryState(QueryState.SYNTAX_ERROR); + query.failure.accept(new BackendException.QueryErrorException("The query type is not supported by the backend")); break; case "INTERNAL": - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("Reveaal was unable to execute this query:\n" + t.getMessage().split(": ", 2)[1])); + query.queryListener.getQuery().setQueryState(QueryState.ERROR); + query.failure.accept(new BackendException.QueryErrorException("Reveaal was unable to execute this query:\n" + t.getMessage().split(": ", 2)[1])); break; case "UNKNOWN": - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The backend encountered an unknown error")); + query.queryListener.getQuery().setQueryState(QueryState.ERROR); + query.failure.accept(new BackendException.QueryErrorException("The backend encountered an unknown error")); break; default: - backendConnection.getExecutableQuery().queryListener.getQuery().setQueryState(QueryState.ERROR); - backendConnection.getExecutableQuery().failure.accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); + try { + query.queryListener.getQuery().setQueryState(QueryState.ERROR); + query.failure.accept(new BackendException.QueryErrorException("The query failed and gave the following error: " + errorType)); + } catch (Exception e) { + e.printStackTrace(); + } break; } - - backendConnection.setExecutableQuery(null); } private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryResponse value) { @@ -282,7 +282,7 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe executableQuery.success.accept(false); } } - + private BackendConnection startNewBackendConnection(BackendInstance backend) { Process p = null; String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); @@ -316,7 +316,7 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); - BackendConnection newConnection = new BackendConnection(p, stub); + BackendConnection newConnection = new BackendConnection(backend, p, stub); this.openBackendConnections.add(newConnection); return newConnection; } @@ -341,19 +341,21 @@ private class ExecutableQuery { * Execute the query using the backend driver */ public void execute() { - executeQuery(this); tries++; + executeQuery(this); } } private class BackendConnection { private final Process process; private final EcdarBackendGrpc.EcdarBackendStub stub; + private final BackendInstance backendInstance; private ExecutableQuery executableQuery = null; - BackendConnection(Process process, EcdarBackendGrpc.EcdarBackendStub stub) { + BackendConnection(BackendInstance backendInstance, Process process, EcdarBackendGrpc.EcdarBackendStub stub) { this.process = process; this.stub = stub; + this.backendInstance = backendInstance; } /** @@ -382,10 +384,7 @@ public ExecutableQuery getExecutableQuery() { * or null, if no executable query is currently associated */ public BackendInstance getBackendInstance() { - if (executableQuery == null) { - return null; - } - return executableQuery.backend; + return backendInstance; } /** @@ -416,11 +415,11 @@ public void close() throws IOException { if (openBackendConnections.remove(this)) { System.out.println("Successfully closed connection to backend"); } else { - System.out.println("Tried to remove a connection not present in either connection list"); + System.out.println("Tried to remove a connection not present in the connection list"); } - // If the backend-instance is null, or remote, there will be no process to destroy - if (this.getBackendInstance() != null && !this.getBackendInstance().isLocal()) { + // If the backend-instance is remote, there will not be a process + if (process.isAlive()) { process.destroy(); } } From eade871b4d8365fa043e3fb2c0d19c89cff16e41 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 13 Feb 2022 10:27:07 +0100 Subject: [PATCH 28/32] Clean-up and null-pointer when closing remote connections FIXED --- src/main/java/ecdar/backend/BackendDriver.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 75474989..3a553ecf 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -265,19 +265,15 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); executableQuery.success.accept(true); } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { - System.out.println("Consistency"); executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); executableQuery.success.accept(true); } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { - System.out.println("Determinism"); executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); executableQuery.success.accept(true); } else if (value.hasComponent()) { - System.out.println("Component"); executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); executableQuery.success.accept(true); } else { - System.out.println(value.getError()); executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); executableQuery.success.accept(false); } @@ -419,7 +415,7 @@ public void close() throws IOException { } // If the backend-instance is remote, there will not be a process - if (process.isAlive()) { + if (process != null) { process.destroy(); } } From 88a22cc9339f38ef226bad3cf7c6a1e1ebf3709a Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 16 Feb 2022 15:39:58 +0100 Subject: [PATCH 29/32] WIP: BackendOptions updated to automatically search for default engines --- .../ecdar/abstractions/BackendInstance.java | 2 +- .../java/ecdar/backend/BackendThread.java | 36 ---- src/main/java/ecdar/backend/jEcdarThread.java | 63 ------ .../BackendOptionsDialogController.java | 198 +++++++++++------- .../ecdar/controllers/EcdarController.java | 2 +- .../resources/ecdar/default_backends.json | 18 -- 6 files changed, 128 insertions(+), 191 deletions(-) delete mode 100644 src/main/java/ecdar/backend/BackendThread.java delete mode 100644 src/main/java/ecdar/backend/jEcdarThread.java delete mode 100644 src/main/resources/ecdar/default_backends.json diff --git a/src/main/java/ecdar/abstractions/BackendInstance.java b/src/main/java/ecdar/abstractions/BackendInstance.java index 1e4c5a5a..0848535f 100644 --- a/src/main/java/ecdar/abstractions/BackendInstance.java +++ b/src/main/java/ecdar/abstractions/BackendInstance.java @@ -101,6 +101,6 @@ public void deserialize(final JsonObject json) { @Override public String toString() { - return name; // ToDo NIELS: Ask if port range should be included + return name; } } diff --git a/src/main/java/ecdar/backend/BackendThread.java b/src/main/java/ecdar/backend/BackendThread.java deleted file mode 100644 index c39eb776..00000000 --- a/src/main/java/ecdar/backend/BackendThread.java +++ /dev/null @@ -1,36 +0,0 @@ -package ecdar.backend; - -import ecdar.abstractions.QueryState; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -public abstract class BackendThread extends Thread { - public AtomicBoolean hasBeenCanceled = new AtomicBoolean(); - final String query; - final Consumer success; - final Consumer failure; - final QueryListener queryListener; - - public BackendThread(final String query, - final Consumer success, - final Consumer failure, - final QueryListener queryListener) { - this.query = query; - this.success = success; - this.failure = failure; - this.queryListener = queryListener; - } - - void handleResult(QueryState result, String line) { - if (result.getStatusCode() == QueryState.SUCCESSFUL.getStatusCode()) { - success.accept(true); - } else if (result.getStatusCode() == QueryState.ERROR.getStatusCode()){ - success.accept(false); - } else if (result.getStatusCode() == QueryState.SYNTAX_ERROR.getStatusCode()) { - failure.accept(new BackendException.QueryErrorException(line)); - } else { - failure.accept(new BackendException.BadBackendQueryException(line)); - } - } -} diff --git a/src/main/java/ecdar/backend/jEcdarThread.java b/src/main/java/ecdar/backend/jEcdarThread.java deleted file mode 100644 index e064ffe6..00000000 --- a/src/main/java/ecdar/backend/jEcdarThread.java +++ /dev/null @@ -1,63 +0,0 @@ -package ecdar.backend; - -import ecdar.Ecdar; -import ecdar.abstractions.QueryState; - -import java.io.*; -import java.util.function.Consumer; - -public class jEcdarThread extends BackendThread { - public jEcdarThread(final String query, - final Consumer success, - final Consumer failure, - final QueryListener queryListener) { - super(query, success, failure, queryListener); - } - - public void run() { - ProcessBuilder pb = new ProcessBuilder("java", "-jar", "src/libs/j-Ecdar.jar"); - pb.redirectErrorStream(true); - try { - //Start the j-Ecdar process - Process jEcdarEngineInstance = pb.start(); - - //Communicate with the j-Ecdar process - try ( - var jEcdarReader = new BufferedReader(new InputStreamReader(jEcdarEngineInstance.getInputStream())); - var jEcdarWriter = new BufferedWriter(new OutputStreamWriter(jEcdarEngineInstance.getOutputStream())); - ) { - //Run the query with the j-Ecdar process - jEcdarWriter.write("-rq -json " + Ecdar.projectDirectory.get() + " " + query.replaceAll("\\s", "") + "\n"); // Newline added to signal EOI - jEcdarWriter.flush(); - - //Read the result of the query from the j-Ecdar process - String line; - QueryState result = QueryState.RUNNING; - while ((line = jEcdarReader.readLine()) != null) { - if (hasBeenCanceled.get()) { - cancel(jEcdarEngineInstance); - return; - } - - // Process the query result - if ((line.equals("true") || line.equals("")) && (result.getStatusCode() <= QueryState.SUCCESSFUL.getStatusCode())) { - result = QueryState.SUCCESSFUL; - } else if (line.equals("false") && (result.getStatusCode() <= QueryState.ERROR.getStatusCode())){ - result = QueryState.ERROR; - } else if (result.getStatusCode() <= QueryState.SYNTAX_ERROR.getStatusCode()) { - result = QueryState.SYNTAX_ERROR; - } - - handleResult(result, line); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void cancel(Process jEcdarEngineInstance) { - jEcdarEngineInstance.destroy(); - failure.accept(new BackendException.QueryErrorException("Canceled")); - } -} diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 43d810fd..3ee5d0bc 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -17,7 +17,6 @@ import org.apache.commons.lang3.Range; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.net.InetAddress; import java.net.URL; @@ -26,8 +25,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.List; import java.util.ResourceBundle; -import java.util.Scanner; +import java.util.stream.Collectors; public class BackendOptionsDialogController implements Initializable { public VBox backendInstanceList; @@ -42,52 +42,152 @@ public void initialize(URL location, ResourceBundle resources) { initializeBackendInstanceList(); } - public void resetBackendOptions() { - backendInstanceList.getChildren().clear(); + /** + * Reverts any changes made to the backend options by reloading the options specified in the preference file, + * or to the default, if no backends are present in the preferences file + */ + public void cancelBackendOptionsChanges() { initializeBackendInstanceList(); } + /** + * Saves the changes made to the backend options to the preferences file and returns true + * if no errors where found in the backend instance definitions, otherwise false + * + * @return whether the changes could be saved, + * meaning that no errors where found in the changes made to the backend options + */ + public boolean saveChangesToBackendOptions() { + if (this.backendInstanceListIsErrorFree()) { + ArrayList backendInstances = new ArrayList<>(); + for (Node backendInstance : backendInstanceList.getChildren()) { + if (backendInstance instanceof BackendInstancePresentation) { + backendInstances.add(((BackendInstancePresentation) backendInstance).getController().updateBackendInstance()); + } + } + + BackendHelper.updateBackendInstances(backendInstances); + + JsonArray jsonArray = new JsonArray(); + for (BackendInstance bi : backendInstances) { + jsonArray.add(bi.serialize()); + } + + Ecdar.preferences.put("backend_instances", jsonArray.toString()); + + BackendInstance defaultBackend = backendInstances.stream().filter(BackendInstance::isDefault).findFirst().orElse(backendInstances.get(0)); + BackendHelper.setDefaultBackendInstance(defaultBackend); + + String defaultBackendName = (defaultBackend.getName()); + Ecdar.preferences.put("default_backend", defaultBackendName); + + return true; + } else { + return false; + } + } + + + /** + * Resets the backends to the default backends present in the 'default_backends.json' file + */ + public void resetBackendsToDefault() { + updateBackendsInGUI(getDefaultBackends()); + } + private void initializeBackendInstanceList() { - try { - setBackendsFromJsonArray( - JsonParser.parseString( - Ecdar.preferences.get("backend_instances", getDefaultBackends()) - ).getAsJsonArray()); - } catch (FileNotFoundException e) { - Ecdar.showToast("Unable to load backends from either preferences or JSON with default backends"); - e.printStackTrace(); + ArrayList backends; + + // Load backends from preferences or get default + var savedBackends = Ecdar.preferences.get("backend_instances", null); + if (savedBackends != null) { + backends = getBackendsFromJsonArray( + JsonParser.parseString(savedBackends).getAsJsonArray()); + } else { + backends = getDefaultBackends(); } + // Style add backend button and handle click event HBox.setHgrow(addBackendButton, Priority.ALWAYS); addBackendButton.setMaxWidth(Double.MAX_VALUE); addBackendButton.setOnMouseClicked((event) -> { BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(); addBackendInstancePresentationToList(newBackendInstancePresentation); }); + + updateBackendsInGUI(backends); } - private String getDefaultBackends() throws FileNotFoundException { - File myObj = new File("src/main/resources/ecdar/default_backends.json"); - Scanner myReader = new Scanner(myObj); - StringBuilder defaultBackendInstanceList = new StringBuilder(); - while (myReader.hasNextLine()) { - defaultBackendInstanceList.append(myReader.nextLine()); - } - myReader.close(); - return defaultBackendInstanceList.toString(); + private void updateBackendsInGUI(ArrayList backends) { + backendInstanceList.getChildren().clear(); + + backends.forEach((bi) -> { + BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(bi); + addBackendInstancePresentationToList(newBackendInstancePresentation); + }); + + BackendHelper.updateBackendInstances(backends); } - private void setBackendsFromJsonArray(JsonArray backends) { + private ArrayList getBackendsFromJsonArray(JsonArray backends) { ArrayList backendInstances = new ArrayList<>(); backendInstanceList.getChildren().clear(); backends.forEach((backend) -> { BackendInstance newBackendInstance = new BackendInstance(backend.getAsJsonObject()); - BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(newBackendInstance); - addBackendInstancePresentationToList(newBackendInstancePresentation); backendInstances.add(newBackendInstance); }); - BackendHelper.updateBackendInstances(backendInstances); + return backendInstances; + } + + + private ArrayList getDefaultBackends() { + ArrayList defaultBackends = new ArrayList<>(); + + // Add Reveaal engine + var reveaal = new BackendInstance(); + reveaal.setName("Reveaal"); + reveaal.setLocal(true); + reveaal.setDefault(true); + reveaal.setPortStart(5032); + reveaal.setPortEnd(5040); + + List searchPathForReveaal = List.of( + new File("lib/Reveaal.exe"), new File("lib/Reveaal") + ); + getBackendPathIfExists(reveaal, searchPathForReveaal); + defaultBackends.add(reveaal); + + // Add jECDAR engine + var jEcdar = new BackendInstance(); + jEcdar.setName("jECDAR"); + jEcdar.setLocal(true); + jEcdar.setDefault(false); + jEcdar.setPortStart(5042); + jEcdar.setPortEnd(5050); + + List searchPathForJEcdar = List.of( + new File("lib/j-Ecdar.exe"), new File("lib/j-Ecdar.bat") + ); + getBackendPathIfExists(jEcdar, searchPathForJEcdar); + defaultBackends.add(jEcdar); + + return defaultBackends; + } + + private void getBackendPathIfExists(BackendInstance engine, List searchPathForFile) { + engine.setBackendLocation(""); + + for (var f : searchPathForFile) { + if (f.exists()) { + engine.setBackendLocation(f.getAbsolutePath()); + break; + } + } + + if (engine.getBackendLocation().equals("")) { + throw new RuntimeException("Could not locate file for default engine, checked: " + searchPathForFile.stream().map(File::getPath).collect(Collectors.joining(", "))); + } } private void addBackendInstancePresentationToList(BackendInstancePresentation newBackendInstancePresentation) { @@ -114,7 +214,7 @@ private void moveBackendInstance(BackendInstancePresentation newBackendInstance, } /** - * Marks input fields in the backendInstanceList if any are present and returns whether any were found + * Marks input fields in the backendInstanceList that contains errors and returns whether any errors were found * * @return whether any errors were found */ @@ -244,52 +344,6 @@ private boolean backendInstanceLocationIsErrorFree(BackendInstanceController bac return errorFree; } - /** - * Returns true if no errors where found in the backend instance definitions, otherwise false - * - * @return whether the changes could be saved - */ - public boolean saveChangesToBackendOptions() { - if (this.backendInstanceListIsErrorFree()) { - ArrayList backendInstances = new ArrayList<>(); - for (Node backendInstance : backendInstanceList.getChildren()) { - if (backendInstance instanceof BackendInstancePresentation) { - backendInstances.add(((BackendInstancePresentation) backendInstance).getController().updateBackendInstance()); - } - } - - BackendHelper.updateBackendInstances(backendInstances); - - JsonArray jsonArray = new JsonArray(); - for (BackendInstance bi : backendInstances) { - jsonArray.add(bi.serialize()); - } - - Ecdar.preferences.put("backend_instances", jsonArray.toString()); - - BackendInstance defaultBackend = backendInstances.stream().filter(BackendInstance::isDefault).findFirst().orElse(backendInstances.get(0)); - BackendHelper.setDefaultBackendInstance(defaultBackend); - - String defaultBackendName = (defaultBackend.getName()); - Ecdar.preferences.put("default_backend", defaultBackendName); - - return true; - } else { - return false; - } - } - - /** - * Resets the backends to the default backends present in the 'default_backends.json' file - */ - public void resetBackendsToDefault() { - try { - setBackendsFromJsonArray(JsonParser.parseString(getDefaultBackends()).getAsJsonArray()); - } catch (FileNotFoundException e) { - Ecdar.showToast("An error occurred while trying to read the JSON file containing the default backends"); - } - } - private enum ValidationErrorMessages { BACKEND_NAME_EMPTY { @Override diff --git a/src/main/java/ecdar/controllers/EcdarController.java b/src/main/java/ecdar/controllers/EcdarController.java index da87e3f1..ce529955 100644 --- a/src/main/java/ecdar/controllers/EcdarController.java +++ b/src/main/java/ecdar/controllers/EcdarController.java @@ -223,7 +223,7 @@ private void initilizeDialogs() { }); backendOptionsDialog.getController().closeButton.setOnMouseClicked(event -> { - backendOptionsDialog.getController().resetBackendOptions(); + backendOptionsDialog.getController().cancelBackendOptionsChanges(); dialog.close(); backendOptionsDialog.close(); }); diff --git a/src/main/resources/ecdar/default_backends.json b/src/main/resources/ecdar/default_backends.json deleted file mode 100644 index a6b2c35b..00000000 --- a/src/main/resources/ecdar/default_backends.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "name": "Reveaal", - "isLocal": "true", - "isDefault": "true", - "location": "lib/Reveaal", - "portRangeStart": "5032", - "portRangeEnd": "5040" - }, - { - "name": "jECDAR", - "isLocal": "True", - "isDefault": "False", - "location": "lib/j-Ecdar.bat", - "portRangeStart": "5042", - "portRangeEnd": "5050" - } -] \ No newline at end of file From 9390916d4a89f7ac30ad3d94416782cf1275e970 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Wed, 16 Feb 2022 15:58:12 +0100 Subject: [PATCH 30/32] WIP: Everything is working, but only first port in port range is used for remote port --- .../java/ecdar/abstractions/BackendInstance.java | 13 +++++++++++++ src/main/java/ecdar/backend/BackendDriver.java | 14 +++++++------- .../BackendOptionsDialogController.java | 12 +++++++----- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/ecdar/abstractions/BackendInstance.java b/src/main/java/ecdar/abstractions/BackendInstance.java index 0848535f..3b0eba76 100644 --- a/src/main/java/ecdar/abstractions/BackendInstance.java +++ b/src/main/java/ecdar/abstractions/BackendInstance.java @@ -2,6 +2,7 @@ import com.google.gson.JsonObject; import ecdar.utility.serialize.Serializable; +import javafx.beans.property.SimpleBooleanProperty; public class BackendInstance implements Serializable { private static final String NAME = "name"; @@ -10,6 +11,7 @@ public class BackendInstance implements Serializable { private static final String LOCATION = "location"; private static final String PORT_RANGE_START = "portRangeStart"; private static final String PORT_RANGE_END = "portRangeEnd"; + private static final String LOCKED = "locked"; private String name; private boolean isLocal; @@ -17,6 +19,7 @@ public class BackendInstance implements Serializable { private String backendLocation; private int portStart; private int portEnd; + private SimpleBooleanProperty locked = new SimpleBooleanProperty(false); public BackendInstance() {}; @@ -76,6 +79,14 @@ public int getNumberOfInstances() { return this.portEnd - this.portStart; } + public void lockInstance() { + locked.set(true); + } + + public SimpleBooleanProperty getLockedProperty() { + return locked; + } + @Override public JsonObject serialize() { final JsonObject result = new JsonObject(); @@ -85,6 +96,7 @@ public JsonObject serialize() { result.addProperty(LOCATION, getBackendLocation()); result.addProperty(PORT_RANGE_START, getPortStart()); result.addProperty(PORT_RANGE_END, getPortEnd()); + result.addProperty(LOCKED, getLockedProperty().get()); return result; } @@ -97,6 +109,7 @@ public void deserialize(final JsonObject json) { setBackendLocation(json.getAsJsonPrimitive(LOCATION).getAsString()); setPortStart(json.getAsJsonPrimitive(PORT_RANGE_START).getAsInt()); setPortEnd(json.getAsJsonPrimitive(PORT_RANGE_END).getAsInt()); + if (json.getAsJsonPrimitive(LOCKED).getAsBoolean()) lockInstance(); } @Override diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index 3a553ecf..d7a6b5a9 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -282,15 +282,15 @@ private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryRe private BackendConnection startNewBackendConnection(BackendInstance backend) { Process p = null; String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); - int portNumber = 0; - - try { - portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); - } catch (IllegalStateException e) { - Ecdar.showToast("No available port for " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); - } + int portNumber = backend.getPortStart(); if (backend.isLocal()) { + try { + portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); + } catch (IllegalStateException e) { + Ecdar.showToast("No available port for " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); + } + do { ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber); diff --git a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java index 3ee5d0bc..128ab632 100644 --- a/src/main/java/ecdar/controllers/BackendOptionsDialogController.java +++ b/src/main/java/ecdar/controllers/BackendOptionsDialogController.java @@ -87,7 +87,6 @@ public boolean saveChangesToBackendOptions() { } } - /** * Resets the backends to the default backends present in the 'default_backends.json' file */ @@ -123,6 +122,8 @@ private void updateBackendsInGUI(ArrayList backends) { backends.forEach((bi) -> { BackendInstancePresentation newBackendInstancePresentation = new BackendInstancePresentation(bi); + newBackendInstancePresentation.getController().backendName.disableProperty().bind(bi.getLockedProperty()); + newBackendInstancePresentation.getController().pathToBackend.disableProperty().bind(bi.getLockedProperty()); addBackendInstancePresentationToList(newBackendInstancePresentation); }); @@ -140,7 +141,6 @@ private ArrayList getBackendsFromJsonArray(JsonArray backends) return backendInstances; } - private ArrayList getDefaultBackends() { ArrayList defaultBackends = new ArrayList<>(); @@ -151,11 +151,12 @@ private ArrayList getDefaultBackends() { reveaal.setDefault(true); reveaal.setPortStart(5032); reveaal.setPortEnd(5040); + reveaal.lockInstance(); List searchPathForReveaal = List.of( new File("lib/Reveaal.exe"), new File("lib/Reveaal") ); - getBackendPathIfExists(reveaal, searchPathForReveaal); + getBackendPathIfFileExists(reveaal, searchPathForReveaal); defaultBackends.add(reveaal); // Add jECDAR engine @@ -165,17 +166,18 @@ private ArrayList getDefaultBackends() { jEcdar.setDefault(false); jEcdar.setPortStart(5042); jEcdar.setPortEnd(5050); + jEcdar.lockInstance(); List searchPathForJEcdar = List.of( new File("lib/j-Ecdar.exe"), new File("lib/j-Ecdar.bat") ); - getBackendPathIfExists(jEcdar, searchPathForJEcdar); + getBackendPathIfFileExists(jEcdar, searchPathForJEcdar); defaultBackends.add(jEcdar); return defaultBackends; } - private void getBackendPathIfExists(BackendInstance engine, List searchPathForFile) { + private void getBackendPathIfFileExists(BackendInstance engine, List searchPathForFile) { engine.setBackendLocation(""); for (var f : searchPathForFile) { From c7c2fac0dbb5671d8a532329871621dbe5fd4771 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Fri, 18 Feb 2022 16:45:14 +0100 Subject: [PATCH 31/32] Remote engine connectivity added --- .../java/ecdar/backend/BackendDriver.java | 163 ++++++++++-------- 1 file changed, 93 insertions(+), 70 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index d7a6b5a9..ceaf4633 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -22,6 +22,7 @@ public class BackendDriver { private final List openBackendConnections = new CopyOnWriteArrayList<>(); private final int deadlineForResponses = 20000; private final int rerunQueryDelay = 200; + private final int numberOfRetriesPerQuery = 5; public BackendDriver() { } @@ -29,11 +30,11 @@ public BackendDriver() { /** * Add the query to execution queue with consumers for success and failure, executed when response received from backends * - * @param query the query to be executed - * @param backendInstance name of the backend to execute the query with - * @param success consumer for a successful response - * @param failure consumer for a failure response - * @param queryListener query listener for referencing the query for GUI purposes + * @param query the query to be executed + * @param backendInstance name of the backend to execute the query with + * @param success consumer for a successful response + * @param failure consumer for a failure response + * @param queryListener query listener for referencing the query for GUI purposes */ public void addQueryToExecutionQueue(String query, BackendInstance backendInstance, Consumer success, Consumer failure, QueryListener queryListener) { new Timer().schedule(new TimerTask() { @@ -88,7 +89,8 @@ public void run() { private boolean error = false; @Override - public void onNext(Empty value) {} + public void onNext(Empty value) { + } @Override public void onError(Throwable t) { @@ -146,7 +148,7 @@ private void executeQuery(ExecutableQuery executableQuery) { // If the connection is null, there are no available connections, // and it was not possible to start a new one if (backendConnection == null) { - if (executableQuery.tries < 5) { + if (executableQuery.tries < numberOfRetriesPerQuery) { new Timer().schedule(new TimerTask() { @Override public void run() { @@ -216,6 +218,84 @@ public void onCompleted() { backendConnection.getStub().withDeadlineAfter(deadlineForResponses, TimeUnit.MILLISECONDS).updateComponents(componentsBuilder.build(), observer); } + private BackendConnection startNewBackendConnection(BackendInstance backend) { + Process p = null; + String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); + long portNumber = 0; + + if (backend.isLocal()) { + try { + portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); + } catch (IllegalStateException e) { + Ecdar.showToast("No available port for " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); + } + + do { + ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber); + + try { + p = pb.start(); + } catch (IOException ioException) { + Ecdar.showToast("Unable to start backend instance"); + ioException.printStackTrace(); + return null; + } + // If the process is not alive, it failed while starting up, try again + } while (!p.isAlive()); + } else { + // ToDo NIELS: Needs more testing + // Filter active engines and map their used ports to an int stream + var activeEnginePorts = openBackendConnections.stream() + .filter((bi) -> bi.backendInstance.equals(backend)) + .mapToInt((bi) -> Integer.parseInt(bi.getStub().getChannel().authority().split(":", 2)[1])); + + int currentPort = backend.getPortStart(); + do { + // Find port not already connected to + int tempPortNumber = currentPort; + if (activeEnginePorts.noneMatch((i) -> i == tempPortNumber)) { + portNumber = tempPortNumber; + } else { + currentPort++; + } + } while (portNumber == 0 && currentPort <= backend.getPortEnd()); + + if (currentPort > backend.getPortEnd()) { + Ecdar.showToast("Unable to connect to remote engine: " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); + return null; + } + } + + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) + .usePlaintext() + .keepAliveTime(1000, TimeUnit.MILLISECONDS) + .build(); + + EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); + BackendConnection newConnection = new BackendConnection(backend, p, stub); + this.openBackendConnections.add(newConnection); + return newConnection; + } + + private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryResponse value) { + if (value.hasRefinement() && value.getRefinement().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else if (value.hasComponent()) { + executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); + executableQuery.success.accept(true); + } else { + executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); + executableQuery.success.accept(false); + } + } + private void handleBackendError(Throwable t, ExecutableQuery query) { // Each error starts with a capitalized description of the error equal to the gRPC error type encountered String errorType = t.getMessage().split(":\\s+", 2)[0]; @@ -243,12 +323,16 @@ public void run() { break; case "INTERNAL": query.queryListener.getQuery().setQueryState(QueryState.ERROR); - query.failure.accept(new BackendException.QueryErrorException("Reveaal was unable to execute this query:\n" + t.getMessage().split(": ", 2)[1])); + query.failure.accept(new BackendException.QueryErrorException("The backend was unable to execute this query:\n" + t.getMessage().split(": ", 2)[1])); break; case "UNKNOWN": query.queryListener.getQuery().setQueryState(QueryState.ERROR); query.failure.accept(new BackendException.QueryErrorException("The backend encountered an unknown error")); break; + case "UNAVAILABLE": + query.queryListener.getQuery().setQueryState(QueryState.SYNTAX_ERROR); + query.failure.accept(new BackendException.QueryErrorException("The backend could not be reached")); + break; default: try { query.queryListener.getQuery().setQueryState(QueryState.ERROR); @@ -260,63 +344,6 @@ public void run() { } } - private void handleResponse(ExecutableQuery executableQuery, QueryProtos.QueryResponse value) { - if (value.hasRefinement() && value.getRefinement().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasConsistency() && value.getConsistency().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasDeterminism() && value.getDeterminism().getSuccess()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else if (value.hasComponent()) { - executableQuery.queryListener.getQuery().setQueryState(QueryState.SUCCESSFUL); - executableQuery.success.accept(true); - } else { - executableQuery.queryListener.getQuery().setQueryState(QueryState.ERROR); - executableQuery.success.accept(false); - } - } - - private BackendConnection startNewBackendConnection(BackendInstance backend) { - Process p = null; - String hostAddress = (backend.isLocal() ? "127.0.0.1" : backend.getBackendLocation()); - int portNumber = backend.getPortStart(); - - if (backend.isLocal()) { - try { - portNumber = SocketUtils.findAvailableTcpPort(backend.getPortStart(), backend.getPortEnd()); - } catch (IllegalStateException e) { - Ecdar.showToast("No available port for " + backend.getName() + " with port range " + backend.getPortStart() + " - " + backend.getPortEnd()); - } - - do { - ProcessBuilder pb = new ProcessBuilder(backend.getBackendLocation(), "-p", hostAddress + ":" + portNumber); - - try { - p = pb.start(); - } catch (IOException ioException) { - Ecdar.showToast("Unable to start backend instance"); - ioException.printStackTrace(); - return null; - } - // If the process is not alive, it failed while starting up, try again - } while (!p.isAlive()); - } - - ManagedChannel channel = ManagedChannelBuilder.forTarget(hostAddress + ":" + portNumber) - .usePlaintext() - .keepAliveTime(1000, TimeUnit.MILLISECONDS) - .build(); - - EcdarBackendGrpc.EcdarBackendStub stub = EcdarBackendGrpc.newStub(channel); - - BackendConnection newConnection = new BackendConnection(backend, p, stub); - this.openBackendConnections.add(newConnection); - return newConnection; - } - private class ExecutableQuery { private final String query; private final BackendInstance backend; @@ -408,11 +435,7 @@ public boolean isRunningQuery() { */ public void close() throws IOException { // Remove the connection from the connection list - if (openBackendConnections.remove(this)) { - System.out.println("Successfully closed connection to backend"); - } else { - System.out.println("Tried to remove a connection not present in the connection list"); - } + openBackendConnections.remove(this); // If the backend-instance is remote, there will not be a process if (process != null) { From 6db060d9143e3dfd54b0d79b4e162e91b8180de3 Mon Sep 17 00:00:00 2001 From: Niels Vistisen Date: Sun, 20 Feb 2022 09:52:04 +0100 Subject: [PATCH 32/32] Minor clean-up --- src/main/java/ecdar/backend/BackendDriver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/ecdar/backend/BackendDriver.java b/src/main/java/ecdar/backend/BackendDriver.java index ceaf4633..3d561cc8 100644 --- a/src/main/java/ecdar/backend/BackendDriver.java +++ b/src/main/java/ecdar/backend/BackendDriver.java @@ -243,8 +243,7 @@ private BackendConnection startNewBackendConnection(BackendInstance backend) { // If the process is not alive, it failed while starting up, try again } while (!p.isAlive()); } else { - // ToDo NIELS: Needs more testing - // Filter active engines and map their used ports to an int stream + // Filter active instances of this engine and map their used ports to an int stream var activeEnginePorts = openBackendConnections.stream() .filter((bi) -> bi.backendInstance.equals(backend)) .mapToInt((bi) -> Integer.parseInt(bi.getStub().getChannel().authority().split(":", 2)[1]));