From 7e2a79335c0127278a13a1f878a8dea6999fc5c7 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Tue, 27 Nov 2018 12:05:21 -0800 Subject: [PATCH 1/6] corefx - updates to allow creation and saving of custom node types, the gui controls the daemon, instead of pulling, also added icons directory --- corefx/src/main/java/com/core/Controller.java | 41 +++++++++- .../java/com/core/client/ICoreClient.java | 4 +- .../com/core/client/rest/CoreRestClient.java | 8 +- .../src/main/java/com/core/data/NodeType.java | 22 +++--- .../main/java/com/core/ui/GraphToolbar.java | 9 ++- .../core/ui/dialogs/NodeServicesDialog.java | 12 ++- .../core/ui/dialogs/NodeTypeCreateDialog.java | 74 +++++++++++++++++++ .../com/core/ui/dialogs/NodeTypesDialog.java | 48 ++++++++---- .../main/java/com/core/utils/ConfigUtils.java | 46 ++++++++++++ .../java/com/core/utils/Configuration.java | 1 + .../java/com/core/utils/NodeTypeConfig.java | 8 +- .../fxml/node_type_create_dialog.fxml | 18 +++++ 12 files changed, 251 insertions(+), 40 deletions(-) create mode 100644 corefx/src/main/java/com/core/ui/dialogs/NodeTypeCreateDialog.java create mode 100644 corefx/src/main/resources/fxml/node_type_create_dialog.fxml diff --git a/corefx/src/main/java/com/core/Controller.java b/corefx/src/main/java/com/core/Controller.java index 5f1b36c6..4afff3bf 100644 --- a/corefx/src/main/java/com/core/Controller.java +++ b/corefx/src/main/java/com/core/Controller.java @@ -8,6 +8,7 @@ import com.core.ui.*; import com.core.ui.dialogs.*; import com.core.utils.ConfigUtils; import com.core.utils.Configuration; +import com.core.utils.NodeTypeConfig; import com.core.websocket.CoreWebSocket; import com.jfoenix.controls.JFXProgressBar; import javafx.application.Application; @@ -79,6 +80,7 @@ public class Controller implements Initializable { private GeoDialog geoDialog = new GeoDialog(this); private ConnectDialog connectDialog = new ConnectDialog(this); private GuiPreferencesDialog guiPreferencesDialog = new GuiPreferencesDialog(this); + private NodeTypeCreateDialog nodeTypeCreateDialog = new NodeTypeCreateDialog(this); public void connectToCore(String coreUrl) { coreWebSocket.stop(); @@ -100,6 +102,7 @@ public class Controller implements Initializable { Map> serviceGroups = coreClient.getServices(); logger.info("core services: {}", serviceGroups); nodeServicesDialog.setServices(serviceGroups); + nodeTypeCreateDialog.setServices(serviceGroups); logger.info("initial core session join"); List sessions = coreClient.getSessions(); @@ -169,9 +172,11 @@ public class Controller implements Initializable { // update other components for new session graphToolbar.setRunButton(coreClient.isRunning()); hooksDialog.updateHooks(); - nodeTypesDialog.updateDefaultServices(); - // display first mobility script in player + // update session default services + setCoreDefaultServices(); + + // display first mobility script in player, if needed Map mobilityConfigMap = coreClient.getMobilityConfigs(); Optional nodeIdOptional = mobilityConfigMap.keySet().stream().findFirst(); if (nodeIdOptional.isPresent()) { @@ -230,6 +235,30 @@ public class Controller implements Initializable { return result; } + private void setCoreDefaultServices() { + try { + Map> defaults = configuration.getNodeTypeConfigs().stream() + .collect(Collectors.toMap(NodeTypeConfig::getModel, NodeTypeConfig::getServices)); + coreClient.setDefaultServices(defaults); + } catch (IOException ex) { + Toast.error("Error updating core default services", ex); + } + } + + public void updateNodeTypes() { + graphToolbar.setupNodeTypes(); + Map> defaults = configuration.getNodeTypeConfigs().stream() + .collect(Collectors.toMap(NodeTypeConfig::getModel, + nodeTypeConfig -> new HashSet<>(nodeTypeConfig.getServices()))); + nodeServicesDialog.setDefaultServices(defaults); + setCoreDefaultServices(); + try { + ConfigUtils.save(configuration); + } catch (IOException ex) { + Toast.error("Error saving configuration", ex); + } + } + public void deleteNode(CoreNode node) { networkGraph.removeNode(node); CoreNode mobilityNode = mobilityDialog.getNode(); @@ -253,6 +282,7 @@ public class Controller implements Initializable { locationDialog.setOwner(window); connectDialog.setOwner(window); guiPreferencesDialog.setOwner(window); + nodeTypeCreateDialog.setOwner(window); } @FXML @@ -401,6 +431,13 @@ public class Controller implements Initializable { logger.info("controller initialize"); swingNode.setContent(networkGraph.getGraphViewer()); + // set node types / default services + graphToolbar.setupNodeTypes(); + Map> defaults = configuration.getNodeTypeConfigs().stream() + .collect(Collectors.toMap(NodeTypeConfig::getModel, + nodeTypeConfig -> new HashSet<>(nodeTypeConfig.getServices()))); + nodeServicesDialog.setDefaultServices(defaults); + // set graph toolbar borderPane.setLeft(graphToolbar); diff --git a/corefx/src/main/java/com/core/client/ICoreClient.java b/corefx/src/main/java/com/core/client/ICoreClient.java index b1a4eabb..af7c3013 100644 --- a/corefx/src/main/java/com/core/client/ICoreClient.java +++ b/corefx/src/main/java/com/core/client/ICoreClient.java @@ -31,7 +31,9 @@ public interface ICoreClient { Map> getServices() throws IOException; - Map> defaultServices() throws IOException; + Map> getDefaultServices() throws IOException; + + boolean setDefaultServices(Map> defaults) throws IOException; CoreService getService(CoreNode node, String serviceName) throws IOException; diff --git a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java index c70e356e..15719e90 100644 --- a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java +++ b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java @@ -129,12 +129,18 @@ public class CoreRestClient implements ICoreClient { } @Override - public Map> defaultServices() throws IOException { + public Map> getDefaultServices() throws IOException { String url = getUrl(String.format("sessions/%s/services/default", sessionId)); GetDefaultServices getDefaultServices = WebUtils.getJson(url, GetDefaultServices.class); return getDefaultServices.getDefaults(); } + @Override + public boolean setDefaultServices(Map> defaults) throws IOException { + String url = getUrl(String.format("sessions/%s/services/default", sessionId)); + return WebUtils.postJson(url, defaults); + } + @Override public CoreService getService(CoreNode node, String serviceName) throws IOException { String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName)); diff --git a/corefx/src/main/java/com/core/data/NodeType.java b/corefx/src/main/java/com/core/data/NodeType.java index a4a69057..1e43d60f 100644 --- a/corefx/src/main/java/com/core/data/NodeType.java +++ b/corefx/src/main/java/com/core/data/NodeType.java @@ -5,9 +5,7 @@ import lombok.EqualsAndHashCode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @Data @@ -24,6 +22,7 @@ public class NodeType { @EqualsAndHashCode.Include private final int id; private final int value; + private final List services = new ArrayList<>(); private String display; private String model; private String icon; @@ -39,13 +38,10 @@ public class NodeType { // EMANE_NET = 14; static { - addNodeType(new NodeType(DEFAULT, "host", "Host", "/icons/host-100.png")); - addNodeType(new NodeType(DEFAULT, "PC", "PC", "/icons/pc-100.png")); - addNodeType(new NodeType(DEFAULT, "mdr", "MDR", "/icons/router-100.png")); - addNodeType(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png")); - addNodeType(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png")); - addNodeType(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png")); - addNodeType(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png")); + add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png")); + add(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png")); + add(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png")); + add(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png")); } @@ -57,10 +53,14 @@ public class NodeType { this.icon = icon; } - public static void addNodeType(NodeType nodeType) { + public static void add(NodeType nodeType) { ID_LOOKUP.put(nodeType.getId(), nodeType); } + public static void remove(NodeType nodeType) { + ID_LOOKUP.remove(nodeType.getId()); + } + public static NodeType get(Integer id) { return ID_LOOKUP.get(id); } diff --git a/corefx/src/main/java/com/core/ui/GraphToolbar.java b/corefx/src/main/java/com/core/ui/GraphToolbar.java index f0e69b9f..c24df381 100644 --- a/corefx/src/main/java/com/core/ui/GraphToolbar.java +++ b/corefx/src/main/java/com/core/ui/GraphToolbar.java @@ -63,8 +63,6 @@ public class GraphToolbar extends VBox { setupPickingButton(); setupEditingButton(); setupDrawingButton(); - - setupNodeTypes(); setupNodesButton(); setupDevicesButton(); @@ -119,7 +117,12 @@ public class GraphToolbar extends VBox { }); } - private void setupNodeTypes() { + public void setupNodeTypes() { + // clear existing configuration + labelMap.clear(); + nodesList.getItems().clear(); + devicesList.getItems().clear(); + for (NodeType nodeType : NodeType.getAll()) { ImageView icon = new ImageView(nodeType.getIcon()); icon.setFitWidth(NODES_ICON_SIZE); diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java index fed4e7fe..dad760f3 100644 --- a/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java @@ -19,7 +19,6 @@ public class NodeServicesDialog extends StageDialog { private static final Logger logger = LogManager.getLogger(); private final Map> serviceItemGroups = new HashMap<>(); private final Map serviceItemMap = new HashMap<>(); - // TODO: get this from core itself private final Map> defaultServices = new HashMap<>(); private CoreNode node; private int index = 0; @@ -47,12 +46,6 @@ public class NodeServicesDialog extends StageDialog { }); addCancelButton(); - defaultServices.put("mdr", new HashSet<>(Arrays.asList("zebra", "OSPFv3MDR", "IPForward"))); - defaultServices.put("PC", new HashSet<>(Arrays.asList("DefaultRoute"))); - defaultServices.put("prouter", new HashSet<>(Arrays.asList("zebra", "OSPFv2", "OSPFv3", "IPForward"))); - defaultServices.put("router", new HashSet<>(Arrays.asList("zebra", "OSPFv2", "OSPFv3", "IPForward"))); - defaultServices.put("host", new HashSet<>(Arrays.asList("DefaultRoute", "SSH"))); - groupListView.getSelectionModel().selectedItemProperty().addListener((ov, previous, current) -> { if (current == null) { return; @@ -80,6 +73,11 @@ public class NodeServicesDialog extends StageDialog { }); } + public void setDefaultServices(Map> defaultServices) { + this.defaultServices.clear(); + this.defaultServices.putAll(defaultServices); + } + public void setServices(Map> serviceGroups) { serviceItemGroups.clear(); diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeTypeCreateDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeTypeCreateDialog.java new file mode 100644 index 00000000..e1cd164e --- /dev/null +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeTypeCreateDialog.java @@ -0,0 +1,74 @@ +package com.core.ui.dialogs; + +import com.core.Controller; +import com.core.data.NodeType; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXListView; +import com.jfoenix.controls.JFXTextField; +import javafx.fxml.FXML; +import javafx.scene.control.SelectionMode; +import lombok.Data; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Data +public class NodeTypeCreateDialog extends StageDialog { + private static final Logger logger = LogManager.getLogger(); + @FXML private JFXListView servicesListView; + @FXML private JFXTextField modelTextField; + @FXML private JFXTextField displayTextField; + private Runnable onCreateHandler; + + public NodeTypeCreateDialog(Controller controller) { + super(controller, "/fxml/node_type_create_dialog.fxml"); + setTitle("Create Node Configuration"); + + JFXButton saveButton = createButton("Create"); + saveButton.setOnAction(event -> { + onCreateHandler.run(); + close(); + }); + addCancelButton(); + + servicesListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + displayTextField.focusedProperty().addListener((obs, prev, current) -> { + if (!current) { + return; + } + + String model = modelTextField.getText(); + if (!model.isEmpty()) { + displayTextField.setText(model.substring(0, 1).toUpperCase() + model.substring(1)); + } + }); + } + + public NodeType getCreatedNodeType() { + NodeType nodeType = new NodeType(NodeType.DEFAULT, modelTextField.getText(), displayTextField.getText(), + "/icons/host-100.png"); + nodeType.getServices().addAll(servicesListView.getSelectionModel().getSelectedItems()); + return nodeType; + } + + public void setServices(Map> serviceGroups) { + List services = new ArrayList<>(); + for (List groupServices : serviceGroups.values()) { + services.addAll(groupServices); + } + services.sort(String::compareTo); + servicesListView.getItems().setAll(services); + } + + + public void showDialog(Runnable runnable) { + onCreateHandler = runnable; + modelTextField.setText(""); + displayTextField.setText(""); + servicesListView.getSelectionModel().clearSelection(); + show(); + } +} diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java index ae816966..aa2a0fb6 100644 --- a/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java @@ -4,6 +4,7 @@ import com.core.Controller; import com.core.data.CoreNode; import com.core.data.NodeType; import com.core.ui.Toast; +import com.core.utils.NodeTypeConfig; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXTextField; @@ -15,8 +16,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.File; -import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,7 +24,6 @@ public class NodeTypesDialog extends StageDialog { private static final Logger logger = LogManager.getLogger(); private final Map nodeTypeMap = new HashMap<>(); private NodeType selectedNodeType; - private Map> defaultServices = new HashMap<>(); @FXML private JFXListView listView; @FXML private JFXTextField modelTextField; @FXML private JFXTextField displayTextField; @@ -40,7 +38,8 @@ public class NodeTypesDialog extends StageDialog { public NodeTypesDialog(Controller controller) { super(controller, "/fxml/node_types_dialog.fxml"); setTitle("Node Configuration"); - addCancelButton(); + JFXButton closeButton = createButton("Close"); + closeButton.setOnAction(event -> close()); listView.getSelectionModel().selectedItemProperty().addListener((ov, prev, current) -> { if (current == null) { @@ -53,14 +52,14 @@ public class NodeTypesDialog extends StageDialog { iconTextField.setText(nodeType.getIcon()); iconImage.setImage(new Image(nodeType.getIcon())); selectedNodeType = nodeType; - List services = defaultServices.getOrDefault(nodeType.getModel(), Collections.emptyList()); + List services = nodeType.getServices(); nodeServicesListView.getItems().setAll(services); }); iconButton.setOnAction(event -> { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select Icon"); - fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + fileChooser.setInitialDirectory(new File(getController().getConfiguration().getIconPath())); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png")); File file = fileChooser.showOpenDialog(controller.getWindow()); if (file != null) { @@ -84,15 +83,38 @@ public class NodeTypesDialog extends StageDialog { controller.getGraphToolbar().updateNodeType(selectedNodeType.getId(), iconPath); Toast.info(String.format("Node %s Updated", selectedNodeType.getDisplay())); }); + + deleteButton.setOnAction(event -> { + String display = listView.getSelectionModel().getSelectedItem(); + NodeType nodeType = nodeTypeMap.get(display); + NodeType.remove(nodeType); + listView.getItems().remove(display); + NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType); + getController().getConfiguration().getNodeTypeConfigs().remove(nodeTypeConfig); + getController().updateNodeTypes(); + }); + + addButton.setOnAction(event -> { + NodeTypeCreateDialog nodeTypeCreateDialog = getController().getNodeTypeCreateDialog(); + nodeTypeCreateDialog.showDialog(() -> { + NodeType nodeType = nodeTypeCreateDialog.getCreatedNodeType(); + NodeType.add(nodeType); + nodeTypeMap.put(nodeType.getDisplay(), nodeType); + listView.getItems().add(nodeType.getDisplay()); + NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType); + getController().getConfiguration().getNodeTypeConfigs().add(nodeTypeConfig); + getController().updateNodeTypes(); + }); + }); } - public void updateDefaultServices() { - try { - defaultServices = getCoreClient().defaultServices(); - listView.getSelectionModel().selectFirst(); - } catch (IOException ex) { - Toast.error("Error getting default services", ex); - } + private NodeTypeConfig createNodeTypeConfig(NodeType nodeType) { + return new NodeTypeConfig( + nodeType.getModel(), + nodeType.getDisplay(), + nodeType.getIcon(), + nodeType.getServices() + ); } public void showDialog() { diff --git a/corefx/src/main/java/com/core/utils/ConfigUtils.java b/corefx/src/main/java/com/core/utils/ConfigUtils.java index 85635a3a..9b2f99c6 100644 --- a/corefx/src/main/java/com/core/utils/ConfigUtils.java +++ b/corefx/src/main/java/com/core/utils/ConfigUtils.java @@ -1,5 +1,6 @@ package com.core.utils; +import com.core.data.NodeType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -9,6 +10,9 @@ import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public final class ConfigUtils { private static final Logger logger = LogManager.getLogger(); @@ -18,6 +22,7 @@ public final class ConfigUtils { private static final Path CONFIG_FILE = Paths.get(HOME.toString(), CONFIG_FILE_NAME); private static final Path XML_DIR = Paths.get(HOME.toString(), "xml"); private static final Path MOBILITY_DIR = Paths.get(HOME.toString(), "mobility"); + private static final Path ICON_DIR = Paths.get(HOME.toString(), "icons"); private ConfigUtils() { @@ -35,6 +40,27 @@ public final class ConfigUtils { return JsonUtils.read(new FileInputStream(CONFIG_FILE.toFile()), Configuration.class); } + private static List createDefaults() throws IOException { + return Arrays.asList( + createDefault("host", "Host", "/icons/host-100.png", Arrays.asList( + "DefaultRoute", "SSH" + )), + createDefault("PC", "PC", "/icons/pc-100.png", + Collections.singletonList("DefaultRoute")), + createDefault("mdr", "MDR", "/icons/router-100.png", Arrays.asList( + "zebra", "OSPFv3MDR", "IPForward" + )) + ); + } + + private static NodeTypeConfig createDefault(String model, String display, String icon, + List services) throws IOException { + String fileName = Paths.get(icon).getFileName().toString(); + Path iconPath = Paths.get(ICON_DIR.toString(), fileName); + Files.copy(ConfigUtils.class.getResourceAsStream(icon), iconPath); + return new NodeTypeConfig(model, display, iconPath.toUri().toString(), services); + } + public static Configuration load() { try { if (!HOME.toFile().exists()) { @@ -42,6 +68,7 @@ public final class ConfigUtils { Files.createDirectory(HOME); Files.createDirectory(XML_DIR); Files.createDirectory(MOBILITY_DIR); + Files.createDirectory(ICON_DIR); } Configuration configuration; @@ -51,11 +78,30 @@ public final class ConfigUtils { configuration = readConfig(); configuration.setXmlPath(XML_DIR.toString()); configuration.setMobilityPath(MOBILITY_DIR.toString()); + configuration.setIconPath(ICON_DIR.toString()); + configuration.setNodeTypeConfigs(createDefaults()); save(configuration); } else { configuration = readConfig(); } + // initialize node types + for (NodeTypeConfig nodeTypeConfig : configuration.getNodeTypeConfigs()) { + NodeType nodeType = new NodeType( + NodeType.DEFAULT, + nodeTypeConfig.getModel(), + nodeTypeConfig.getDisplay(), + nodeTypeConfig.getIcon() + ); + nodeTypeConfig.getServices().sort(String::compareTo); + nodeType.getServices().addAll(nodeTypeConfig.getServices()); + NodeType.add(nodeType); + } + + // override configuration from command line + String coreRest = System.getProperty("core-rest"); + configuration.setCoreRest(coreRest); + return configuration; } catch (IOException ex) { logger.error("error reading config file"); diff --git a/corefx/src/main/java/com/core/utils/Configuration.java b/corefx/src/main/java/com/core/utils/Configuration.java index 6dd2f352..e7815166 100644 --- a/corefx/src/main/java/com/core/utils/Configuration.java +++ b/corefx/src/main/java/com/core/utils/Configuration.java @@ -12,6 +12,7 @@ public class Configuration { private String coreRest; private String xmlPath; private String mobilityPath; + private String iconPath; private String shellCommand; private List nodeTypeConfigs = new ArrayList<>(); } diff --git a/corefx/src/main/java/com/core/utils/NodeTypeConfig.java b/corefx/src/main/java/com/core/utils/NodeTypeConfig.java index 7735130f..dc4a29da 100644 --- a/corefx/src/main/java/com/core/utils/NodeTypeConfig.java +++ b/corefx/src/main/java/com/core/utils/NodeTypeConfig.java @@ -1,16 +1,20 @@ package com.core.utils; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.ArrayList; import java.util.List; @Data +@EqualsAndHashCode(onlyExplicitlyIncluded = true) @NoArgsConstructor +@AllArgsConstructor public class NodeTypeConfig { + @EqualsAndHashCode.Include private String model; private String display; private String icon; - private List services = new ArrayList<>(); + private List services; } diff --git a/corefx/src/main/resources/fxml/node_type_create_dialog.fxml b/corefx/src/main/resources/fxml/node_type_create_dialog.fxml new file mode 100644 index 00000000..dfe8c26d --- /dev/null +++ b/corefx/src/main/resources/fxml/node_type_create_dialog.fxml @@ -0,0 +1,18 @@ + + + + + + + + + + + + From 8423bae0aff00b15a8cdb8678a3e1a729dd114f3 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Tue, 27 Nov 2018 13:37:15 -0800 Subject: [PATCH 2/6] corefx - updated node types to use sets and have the controller maintain default service mapping for other components to leverage --- corefx/src/main/java/com/core/Controller.java | 15 ++++----------- .../main/java/com/core/client/ICoreClient.java | 3 ++- .../com/core/client/rest/CoreRestClient.java | 7 ++----- .../src/main/java/com/core/data/NodeType.java | 2 +- .../com/core/ui/dialogs/NodeServicesDialog.java | 8 +------- .../com/core/ui/dialogs/NodeTypesDialog.java | 6 ++++-- .../main/java/com/core/utils/ConfigUtils.java | 17 +++++++---------- .../java/com/core/utils/NodeTypeConfig.java | 4 ++-- 8 files changed, 23 insertions(+), 39 deletions(-) diff --git a/corefx/src/main/java/com/core/Controller.java b/corefx/src/main/java/com/core/Controller.java index 4afff3bf..537e7e18 100644 --- a/corefx/src/main/java/com/core/Controller.java +++ b/corefx/src/main/java/com/core/Controller.java @@ -51,6 +51,7 @@ public class Controller implements Initializable { private Application application; private Stage window; private Configuration configuration; + private Map> defaultServices = new HashMap<>(); // core client utilities private ICoreClient coreClient = new CoreRestClient(); @@ -237,9 +238,7 @@ public class Controller implements Initializable { private void setCoreDefaultServices() { try { - Map> defaults = configuration.getNodeTypeConfigs().stream() - .collect(Collectors.toMap(NodeTypeConfig::getModel, NodeTypeConfig::getServices)); - coreClient.setDefaultServices(defaults); + coreClient.setDefaultServices(defaultServices); } catch (IOException ex) { Toast.error("Error updating core default services", ex); } @@ -247,10 +246,6 @@ public class Controller implements Initializable { public void updateNodeTypes() { graphToolbar.setupNodeTypes(); - Map> defaults = configuration.getNodeTypeConfigs().stream() - .collect(Collectors.toMap(NodeTypeConfig::getModel, - nodeTypeConfig -> new HashSet<>(nodeTypeConfig.getServices()))); - nodeServicesDialog.setDefaultServices(defaults); setCoreDefaultServices(); try { ConfigUtils.save(configuration); @@ -433,10 +428,8 @@ public class Controller implements Initializable { // set node types / default services graphToolbar.setupNodeTypes(); - Map> defaults = configuration.getNodeTypeConfigs().stream() - .collect(Collectors.toMap(NodeTypeConfig::getModel, - nodeTypeConfig -> new HashSet<>(nodeTypeConfig.getServices()))); - nodeServicesDialog.setDefaultServices(defaults); + defaultServices = configuration.getNodeTypeConfigs().stream() + .collect(Collectors.toMap(NodeTypeConfig::getModel, NodeTypeConfig::getServices)); // set graph toolbar borderPane.setLeft(graphToolbar); diff --git a/corefx/src/main/java/com/core/client/ICoreClient.java b/corefx/src/main/java/com/core/client/ICoreClient.java index af7c3013..5635e615 100644 --- a/corefx/src/main/java/com/core/client/ICoreClient.java +++ b/corefx/src/main/java/com/core/client/ICoreClient.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; public interface ICoreClient { void setUrl(String url); @@ -33,7 +34,7 @@ public interface ICoreClient { Map> getDefaultServices() throws IOException; - boolean setDefaultServices(Map> defaults) throws IOException; + boolean setDefaultServices(Map> defaults) throws IOException; CoreService getService(CoreNode node, String serviceName) throws IOException; diff --git a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java index 15719e90..55f3cbff 100644 --- a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java +++ b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java @@ -9,10 +9,7 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Data public class CoreRestClient implements ICoreClient { @@ -136,7 +133,7 @@ public class CoreRestClient implements ICoreClient { } @Override - public boolean setDefaultServices(Map> defaults) throws IOException { + public boolean setDefaultServices(Map> defaults) throws IOException { String url = getUrl(String.format("sessions/%s/services/default", sessionId)); return WebUtils.postJson(url, defaults); } diff --git a/corefx/src/main/java/com/core/data/NodeType.java b/corefx/src/main/java/com/core/data/NodeType.java index 1e43d60f..4af7dc3d 100644 --- a/corefx/src/main/java/com/core/data/NodeType.java +++ b/corefx/src/main/java/com/core/data/NodeType.java @@ -22,7 +22,7 @@ public class NodeType { @EqualsAndHashCode.Include private final int id; private final int value; - private final List services = new ArrayList<>(); + private final Set services = new TreeSet<>(); private String display; private String model; private String icon; diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java index dad760f3..c8e4b1a0 100644 --- a/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeServicesDialog.java @@ -19,7 +19,6 @@ public class NodeServicesDialog extends StageDialog { private static final Logger logger = LogManager.getLogger(); private final Map> serviceItemGroups = new HashMap<>(); private final Map serviceItemMap = new HashMap<>(); - private final Map> defaultServices = new HashMap<>(); private CoreNode node; private int index = 0; @FXML private GridPane gridPane; @@ -73,11 +72,6 @@ public class NodeServicesDialog extends StageDialog { }); } - public void setDefaultServices(Map> defaultServices) { - this.defaultServices.clear(); - this.defaultServices.putAll(defaultServices); - } - public void setServices(Map> serviceGroups) { serviceItemGroups.clear(); @@ -135,7 +129,7 @@ public class NodeServicesDialog extends StageDialog { Set nodeServices = node.getServices(); if (nodeServices.isEmpty()) { - nodeServices = defaultServices.get(node.getModel()); + nodeServices = getController().getDefaultServices().get(node.getModel()); } for (List items : serviceItemGroups.values()) { diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java index aa2a0fb6..80810696 100644 --- a/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeTypesDialog.java @@ -17,8 +17,8 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.Set; public class NodeTypesDialog extends StageDialog { private static final Logger logger = LogManager.getLogger(); @@ -52,7 +52,7 @@ public class NodeTypesDialog extends StageDialog { iconTextField.setText(nodeType.getIcon()); iconImage.setImage(new Image(nodeType.getIcon())); selectedNodeType = nodeType; - List services = nodeType.getServices(); + Set services = nodeType.getServices(); nodeServicesListView.getItems().setAll(services); }); @@ -90,6 +90,7 @@ public class NodeTypesDialog extends StageDialog { NodeType.remove(nodeType); listView.getItems().remove(display); NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType); + getController().getDefaultServices().remove(nodeTypeConfig.getModel()); getController().getConfiguration().getNodeTypeConfigs().remove(nodeTypeConfig); getController().updateNodeTypes(); }); @@ -102,6 +103,7 @@ public class NodeTypesDialog extends StageDialog { nodeTypeMap.put(nodeType.getDisplay(), nodeType); listView.getItems().add(nodeType.getDisplay()); NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType); + getController().getDefaultServices().put(nodeTypeConfig.getModel(), nodeTypeConfig.getServices()); getController().getConfiguration().getNodeTypeConfigs().add(nodeTypeConfig); getController().updateNodeTypes(); }); diff --git a/corefx/src/main/java/com/core/utils/ConfigUtils.java b/corefx/src/main/java/com/core/utils/ConfigUtils.java index 9b2f99c6..d5c0fec7 100644 --- a/corefx/src/main/java/com/core/utils/ConfigUtils.java +++ b/corefx/src/main/java/com/core/utils/ConfigUtils.java @@ -10,9 +10,7 @@ import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; public final class ConfigUtils { private static final Logger logger = LogManager.getLogger(); @@ -42,19 +40,19 @@ public final class ConfigUtils { private static List createDefaults() throws IOException { return Arrays.asList( - createDefault("host", "Host", "/icons/host-100.png", Arrays.asList( + createDefault("host", "Host", "/icons/host-100.png", new TreeSet<>(Arrays.asList( "DefaultRoute", "SSH" - )), + ))), createDefault("PC", "PC", "/icons/pc-100.png", - Collections.singletonList("DefaultRoute")), - createDefault("mdr", "MDR", "/icons/router-100.png", Arrays.asList( + new TreeSet<>(Collections.singletonList("DefaultRoute"))), + createDefault("mdr", "MDR", "/icons/router-100.png", new TreeSet<>(Arrays.asList( "zebra", "OSPFv3MDR", "IPForward" - )) + ))) ); } private static NodeTypeConfig createDefault(String model, String display, String icon, - List services) throws IOException { + Set services) throws IOException { String fileName = Paths.get(icon).getFileName().toString(); Path iconPath = Paths.get(ICON_DIR.toString(), fileName); Files.copy(ConfigUtils.class.getResourceAsStream(icon), iconPath); @@ -93,7 +91,6 @@ public final class ConfigUtils { nodeTypeConfig.getDisplay(), nodeTypeConfig.getIcon() ); - nodeTypeConfig.getServices().sort(String::compareTo); nodeType.getServices().addAll(nodeTypeConfig.getServices()); NodeType.add(nodeType); } diff --git a/corefx/src/main/java/com/core/utils/NodeTypeConfig.java b/corefx/src/main/java/com/core/utils/NodeTypeConfig.java index dc4a29da..4b61999f 100644 --- a/corefx/src/main/java/com/core/utils/NodeTypeConfig.java +++ b/corefx/src/main/java/com/core/utils/NodeTypeConfig.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.List; +import java.util.Set; @Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) @@ -16,5 +16,5 @@ public class NodeTypeConfig { private String model; private String display; private String icon; - private List services; + private Set services; } From f75f6f9ec019bf7093f78705e653a37b432af316 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Tue, 27 Nov 2018 13:58:56 -0800 Subject: [PATCH 3/6] corefx - removed edit button, set to edit mode on node/device button click instead --- .../main/java/com/core/ui/GraphToolbar.java | 52 ++++++++++--------- .../main/resources/fxml/graph_toolbar.fxml | 1 - 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/corefx/src/main/java/com/core/ui/GraphToolbar.java b/corefx/src/main/java/com/core/ui/GraphToolbar.java index c24df381..ff4f5dd1 100644 --- a/corefx/src/main/java/com/core/ui/GraphToolbar.java +++ b/corefx/src/main/java/com/core/ui/GraphToolbar.java @@ -45,7 +45,6 @@ public class GraphToolbar extends VBox { private boolean isEditing = false; @FXML private JFXButton runButton; @FXML private JFXButton pickingButton; - @FXML private JFXButton editingButton; @FXML private JFXButton drawingButton; @FXML private ComboBox graphModeCombo; @FXML private JFXButton nodesButton; @@ -61,7 +60,6 @@ public class GraphToolbar extends VBox { stopIcon.setSize(ICON_SIZE); setupPickingButton(); - setupEditingButton(); setupDrawingButton(); setupNodesButton(); setupDevicesButton(); @@ -82,24 +80,20 @@ public class GraphToolbar extends VBox { controller.getBottom().getChildren().remove(controller.getAnnotationToolbar()); controller.getBorderPane().setRight(null); setSelected(true, pickingButton); - setSelected(false, editingButton, drawingButton, selectedEditButton); + setSelected(false, drawingButton, selectedEditButton); isEditing = false; }); } - private void setupEditingButton() { - SVGGlyph editIcon = IconUtils.get("mode_edit"); - editIcon.setSize(ICON_SIZE); - editingButton.setGraphic(editIcon); - editingButton.setTooltip(new Tooltip("Edit Graph")); - editingButton.setOnAction(event -> { - controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.EDITING); - controller.getBottom().getChildren().remove(controller.getAnnotationToolbar()); - controller.getBorderPane().setRight(null); - setSelected(true, editingButton, selectedEditButton); - setSelected(false, drawingButton, pickingButton); - isEditing = true; - }); + private void setEditMode() { + controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.EDITING); + controller.getBottom().getChildren().remove(controller.getAnnotationToolbar()); + controller.getBorderPane().setRight(null); + if (selectedEditButton != null) { + setSelected(true, selectedEditButton); + } + setSelected(false, drawingButton, pickingButton); + isEditing = true; } private void setupDrawingButton() { @@ -112,7 +106,7 @@ public class GraphToolbar extends VBox { controller.getBottom().getChildren().add(controller.getAnnotationToolbar()); controller.getBorderPane().setRight(null); setSelected(true, drawingButton); - setSelected(false, editingButton, pickingButton, selectedEditButton); + setSelected(false, pickingButton, selectedEditButton); isEditing = false; }); } @@ -165,7 +159,9 @@ public class GraphToolbar extends VBox { JFXButton previous = selectedEditButton; selectedEditButton = button; if (isEditing) { - setSelected(false, previous); + if (previous != null) { + setSelected(false, previous); + } setSelected(true, selectedEditButton); } } @@ -187,8 +183,11 @@ public class GraphToolbar extends VBox { }); JFXPopup popup = new JFXPopup(nodesList); - nodesButton.setOnAction(event -> popup.show(nodesButton, JFXPopup.PopupVPosition.TOP, - JFXPopup.PopupHPosition.LEFT, nodesButton.getWidth(), 0)); + nodesButton.setOnAction(event -> { + setEditMode(); + popup.show(nodesButton, JFXPopup.PopupVPosition.TOP, + JFXPopup.PopupHPosition.LEFT, nodesButton.getWidth(), 0); + }); } private void setupDevicesButton() { @@ -208,8 +207,11 @@ public class GraphToolbar extends VBox { }); JFXPopup popup = new JFXPopup(devicesList); - devicesButton.setOnAction(event -> popup.show(devicesButton, JFXPopup.PopupVPosition.TOP, - JFXPopup.PopupHPosition.LEFT, devicesButton.getWidth(), 0)); + devicesButton.setOnAction(event -> { + setEditMode(); + popup.show(devicesButton, JFXPopup.PopupVPosition.TOP, + JFXPopup.PopupHPosition.LEFT, devicesButton.getWidth(), 0); + }); } @FXML @@ -272,7 +274,8 @@ public class GraphToolbar extends VBox { if (isRunning) { Platform.runLater(() -> { pickingButton.fire(); - editingButton.setDisable(true); + devicesButton.setDisable(true); + nodesButton.setDisable(true); runButton.pseudoClassStateChanged(START_CLASS, false); runButton.pseudoClassStateChanged(STOP_CLASS, true); if (runButton.getGraphic() != stopIcon) { @@ -282,7 +285,8 @@ public class GraphToolbar extends VBox { }); } else { Platform.runLater(() -> { - editingButton.setDisable(false); + devicesButton.setDisable(false); + nodesButton.setDisable(false); runButton.pseudoClassStateChanged(START_CLASS, true); runButton.pseudoClassStateChanged(STOP_CLASS, false); if (runButton.getGraphic() != startIcon) { diff --git a/corefx/src/main/resources/fxml/graph_toolbar.fxml b/corefx/src/main/resources/fxml/graph_toolbar.fxml index 70d3c35d..f393d122 100644 --- a/corefx/src/main/resources/fxml/graph_toolbar.fxml +++ b/corefx/src/main/resources/fxml/graph_toolbar.fxml @@ -8,7 +8,6 @@ - From c4195185592b4a78dbf12f0659317c4a8dbb4b15 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Wed, 28 Nov 2018 10:08:48 -0800 Subject: [PATCH 4/6] corefx - update to display default services for nodes in details pane --- .../main/java/com/core/ui/NodeDetails.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/corefx/src/main/java/com/core/ui/NodeDetails.java b/corefx/src/main/java/com/core/ui/NodeDetails.java index fa3059dd..19f3b350 100644 --- a/corefx/src/main/java/com/core/ui/NodeDetails.java +++ b/corefx/src/main/java/com/core/ui/NodeDetails.java @@ -22,6 +22,8 @@ import javafx.scene.layout.GridPane; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.Set; + public class NodeDetails extends ScrollPane { private static final Logger logger = LogManager.getLogger(); private static final int START_INDEX = 1; @@ -100,19 +102,19 @@ public class NodeDetails extends ScrollPane { addInterface(coreInterface, linkedNode); } - - if (!node.getServices().isEmpty()) { - addSeparator(); - addLabel("Services"); - JFXListView listView = new JFXListView<>(); - listView.setMouseTransparent(true); - listView.setFocusTraversable(false); - for (String service : node.getServices()) { - listView.getItems().add(service); - } - gridPane.add(listView, 0, index++, 2, 1); + // display custom or default node services + Set services = node.getServices(); + if (services.isEmpty()) { + services = controller.getDefaultServices().get(node.getModel()); } - + addSeparator(); + addLabel("Services"); + JFXListView listView = new JFXListView<>(); + listView.setMouseTransparent(true); + listView.setFocusTraversable(false); + listView.getItems().setAll(services); + gridPane.add(listView, 0, index++, 2, 1); + JFXScrollPane.smoothScrolling(scrollPane); } From 4f5ac43a0680dd7e41dd99d74fb61d45093430fd Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Wed, 28 Nov 2018 11:46:13 -0800 Subject: [PATCH 5/6] corefx - updated context menu logic to be contained within classes, fixed issue with service display in details panel for nodes with no default services --- corefx/src/main/java/com/core/Controller.java | 6 +- .../core/graph/AbstractNodeContextMenu.java | 22 ++++++ .../core/graph/CorePopupGraphMousePlugin.java | 68 ++++--------------- .../java/com/core/graph/EmaneContextMenu.java | 23 +++++++ .../java/com/core/graph/GraphContextMenu.java | 22 ++++++ .../java/com/core/graph/LinkContextMenu.java | 21 ++++++ .../java/com/core/graph/NodeContextMenu.java | 21 ++++++ .../java/com/core/graph/WlanContextMenu.java | 23 +++++++ .../main/java/com/core/ui/NodeDetails.java | 21 +++--- .../com/core/ui/dialogs/NodeEmaneDialog.java | 5 +- 10 files changed, 162 insertions(+), 70 deletions(-) create mode 100644 corefx/src/main/java/com/core/graph/AbstractNodeContextMenu.java create mode 100644 corefx/src/main/java/com/core/graph/EmaneContextMenu.java create mode 100644 corefx/src/main/java/com/core/graph/GraphContextMenu.java create mode 100644 corefx/src/main/java/com/core/graph/LinkContextMenu.java create mode 100644 corefx/src/main/java/com/core/graph/NodeContextMenu.java create mode 100644 corefx/src/main/java/com/core/graph/WlanContextMenu.java diff --git a/corefx/src/main/java/com/core/Controller.java b/corefx/src/main/java/com/core/Controller.java index 537e7e18..3c611921 100644 --- a/corefx/src/main/java/com/core/Controller.java +++ b/corefx/src/main/java/com/core/Controller.java @@ -184,8 +184,10 @@ public class Controller implements Initializable { Integer nodeId = nodeIdOptional.get(); MobilityConfig mobilityConfig = mobilityConfigMap.get(nodeId); CoreNode node = networkGraph.getVertex(nodeId); - mobilityPlayer.show(node, mobilityConfig); - Platform.runLater(() -> bottom.getChildren().add(mobilityPlayer)); + if (node != null) { + mobilityPlayer.show(node, mobilityConfig); + Platform.runLater(() -> bottom.getChildren().add(mobilityPlayer)); + } } } diff --git a/corefx/src/main/java/com/core/graph/AbstractNodeContextMenu.java b/corefx/src/main/java/com/core/graph/AbstractNodeContextMenu.java new file mode 100644 index 00000000..d04097c2 --- /dev/null +++ b/corefx/src/main/java/com/core/graph/AbstractNodeContextMenu.java @@ -0,0 +1,22 @@ +package com.core.graph; + +import com.core.Controller; +import com.core.data.CoreNode; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.MenuItem; + +abstract class AbstractNodeContextMenu extends GraphContextMenu { + final CoreNode coreNode; + + AbstractNodeContextMenu(Controller controller, CoreNode coreNode) { + super(controller); + this.coreNode = coreNode; + } + + void addMenuItem(String text, EventHandler handler) { + MenuItem menuItem = new MenuItem(text); + menuItem.setOnAction(handler); + getItems().add(menuItem); + } +} diff --git a/corefx/src/main/java/com/core/graph/CorePopupGraphMousePlugin.java b/corefx/src/main/java/com/core/graph/CorePopupGraphMousePlugin.java index 5803e7bc..3b96c9fa 100644 --- a/corefx/src/main/java/com/core/graph/CorePopupGraphMousePlugin.java +++ b/corefx/src/main/java/com/core/graph/CorePopupGraphMousePlugin.java @@ -9,17 +9,12 @@ import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.visualization.control.EditingPopupGraphMousePlugin; import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; import javafx.scene.control.ContextMenu; -import javafx.scene.control.MenuItem; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.List; public class CorePopupGraphMousePlugin extends EditingPopupGraphMousePlugin { private static final Logger logger = LogManager.getLogger(); @@ -45,15 +40,13 @@ public class CorePopupGraphMousePlugin extends EditingPopupGraphMousePlugi final CoreNode node = pickSupport.getVertex(graphLayout, p.getX(), p.getY()); final CoreLink link = pickSupport.getEdge(graphLayout, p.getX(), p.getY()); - ContextMenu contextMenu = new ContextMenu(); - - // edge picked + final ContextMenu contextMenu; if (node != null) { - List menuItems = handleNodeContext(node); - contextMenu.getItems().addAll(menuItems); + contextMenu = handleNodeContext(node); } else if (link != null) { - List menuItems = handleLinkContext(link); - contextMenu.getItems().addAll(menuItems); + contextMenu = new LinkContextMenu(controller, link); + } else { + contextMenu = new ContextMenu(); } if (!contextMenu.getItems().isEmpty()) { @@ -63,60 +56,23 @@ public class CorePopupGraphMousePlugin extends EditingPopupGraphMousePlugi } } - private MenuItem createMenuItem(String text, EventHandler handler) { - MenuItem menuItem = new MenuItem(text); - menuItem.setOnAction(handler); - return menuItem; - } - - private List handleNodeContext(final CoreNode node) { - boolean isRunning = controller.getCoreClient().isRunning(); - - List menuItems = new ArrayList<>(); - + private ContextMenu handleNodeContext(final CoreNode node) { + ContextMenu contextMenu = new ContextMenu(); switch (node.getType()) { case NodeType.DEFAULT: - menuItems.add(createMenuItem("Services", - event -> controller.getNodeServicesDialog().showDialog(node))); + contextMenu = new NodeContextMenu(controller, node); break; case NodeType.WLAN: - menuItems.add(createMenuItem("WLAN Settings", - event -> controller.getNodeWlanDialog().showDialog(node))); - menuItems.add(createMenuItem("Mobility", - event -> controller.getMobilityDialog().showDialog(node))); - menuItems.add(createMenuItem("Link MDRs", - event -> networkGraph.linkMdrs(node))); + contextMenu = new WlanContextMenu(controller, node); break; case NodeType.EMANE: - menuItems.add(createMenuItem("EMANE Settings", - event -> controller.getNodeEmaneDialog().showDialog(node))); - menuItems.add(createMenuItem("Mobility", - event -> controller.getMobilityDialog().showDialog(node))); - menuItems.add(createMenuItem("Link MDRs", - event -> networkGraph.linkMdrs(node))); + contextMenu = new EmaneContextMenu(controller, node); break; default: + logger.warn("no context menu for node: {}", node.getType()); break; } - if (!isRunning) { - menuItems.add(createMenuItem("Delete Node", - event -> controller.deleteNode(node))); - } - - return menuItems; - } - - private List handleLinkContext(final CoreLink link) { - boolean isRunning = controller.getCoreClient().isRunning(); - - List menuItems = new ArrayList<>(); - - if (!isRunning) { - menuItems.add(createMenuItem("Delete Link", - event -> networkGraph.removeLink(link))); - } - - return menuItems; + return contextMenu; } } diff --git a/corefx/src/main/java/com/core/graph/EmaneContextMenu.java b/corefx/src/main/java/com/core/graph/EmaneContextMenu.java new file mode 100644 index 00000000..be567d0c --- /dev/null +++ b/corefx/src/main/java/com/core/graph/EmaneContextMenu.java @@ -0,0 +1,23 @@ +package com.core.graph; + +import com.core.Controller; +import com.core.data.CoreNode; + +class EmaneContextMenu extends AbstractNodeContextMenu { + EmaneContextMenu(Controller controller, CoreNode coreNode) { + super(controller, coreNode); + setup(); + } + + private void setup() { + addMenuItem("EMANE Settings", + event -> controller.getNodeEmaneDialog().showDialog(coreNode)); + if (!controller.getCoreClient().isRunning()) { + addMenuItem("Mobility", + event -> controller.getMobilityDialog().showDialog(coreNode)); + addMenuItem("Link MDRs", + event -> controller.getNetworkGraph().linkMdrs(coreNode)); + addMenuItem("Delete Node", event -> controller.deleteNode(coreNode)); + } + } +} diff --git a/corefx/src/main/java/com/core/graph/GraphContextMenu.java b/corefx/src/main/java/com/core/graph/GraphContextMenu.java new file mode 100644 index 00000000..c93ecba9 --- /dev/null +++ b/corefx/src/main/java/com/core/graph/GraphContextMenu.java @@ -0,0 +1,22 @@ +package com.core.graph; + +import com.core.Controller; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; + +abstract class GraphContextMenu extends ContextMenu { + final Controller controller; + + GraphContextMenu(Controller controller) { + super(); + this.controller = controller; + } + + void addMenuItem(String text, EventHandler handler) { + MenuItem menuItem = new MenuItem(text); + menuItem.setOnAction(handler); + getItems().add(menuItem); + } +} diff --git a/corefx/src/main/java/com/core/graph/LinkContextMenu.java b/corefx/src/main/java/com/core/graph/LinkContextMenu.java new file mode 100644 index 00000000..d9b11115 --- /dev/null +++ b/corefx/src/main/java/com/core/graph/LinkContextMenu.java @@ -0,0 +1,21 @@ +package com.core.graph; + +import com.core.Controller; +import com.core.data.CoreLink; + +class LinkContextMenu extends GraphContextMenu { + final CoreLink coreLink; + + LinkContextMenu(Controller controller, CoreLink coreLink) { + super(controller); + this.coreLink = coreLink; + setup(); + } + + private void setup() { + if (!controller.getCoreClient().isRunning()) { + addMenuItem("Delete Link", + event -> controller.getNetworkGraph().removeLink(coreLink)); + } + } +} diff --git a/corefx/src/main/java/com/core/graph/NodeContextMenu.java b/corefx/src/main/java/com/core/graph/NodeContextMenu.java new file mode 100644 index 00000000..a328b3da --- /dev/null +++ b/corefx/src/main/java/com/core/graph/NodeContextMenu.java @@ -0,0 +1,21 @@ +package com.core.graph; + +import com.core.Controller; +import com.core.data.CoreNode; + +class NodeContextMenu extends AbstractNodeContextMenu { + NodeContextMenu(Controller controller, CoreNode coreNode) { + super(controller, coreNode); + setup(); + } + + private void setup() { + if (controller.getCoreClient().isRunning()) { + addMenuItem("Manage Services", event -> { + }); + } else { + addMenuItem("Services", event -> controller.getNodeServicesDialog().showDialog(coreNode)); + addMenuItem("Delete Node", event -> controller.deleteNode(coreNode)); + } + } +} diff --git a/corefx/src/main/java/com/core/graph/WlanContextMenu.java b/corefx/src/main/java/com/core/graph/WlanContextMenu.java new file mode 100644 index 00000000..6fe29132 --- /dev/null +++ b/corefx/src/main/java/com/core/graph/WlanContextMenu.java @@ -0,0 +1,23 @@ +package com.core.graph; + +import com.core.Controller; +import com.core.data.CoreNode; + +class WlanContextMenu extends AbstractNodeContextMenu { + WlanContextMenu(Controller controller, CoreNode coreNode) { + super(controller, coreNode); + setup(); + } + + private void setup() { + addMenuItem("WLAN Settings", + event -> controller.getNodeWlanDialog().showDialog(coreNode)); + if (!controller.getCoreClient().isRunning()) { + addMenuItem("Mobility", + event -> controller.getMobilityDialog().showDialog(coreNode)); + addMenuItem("Link MDRs", + event -> controller.getNetworkGraph().linkMdrs(coreNode)); + addMenuItem("Delete Node", event -> controller.deleteNode(coreNode)); + } + } +} diff --git a/corefx/src/main/java/com/core/ui/NodeDetails.java b/corefx/src/main/java/com/core/ui/NodeDetails.java index 19f3b350..c1e9751c 100644 --- a/corefx/src/main/java/com/core/ui/NodeDetails.java +++ b/corefx/src/main/java/com/core/ui/NodeDetails.java @@ -22,6 +22,7 @@ import javafx.scene.layout.GridPane; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.Collections; import java.util.Set; public class NodeDetails extends ScrollPane { @@ -105,16 +106,18 @@ public class NodeDetails extends ScrollPane { // display custom or default node services Set services = node.getServices(); if (services.isEmpty()) { - services = controller.getDefaultServices().get(node.getModel()); + services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet()); } - addSeparator(); - addLabel("Services"); - JFXListView listView = new JFXListView<>(); - listView.setMouseTransparent(true); - listView.setFocusTraversable(false); - listView.getItems().setAll(services); - gridPane.add(listView, 0, index++, 2, 1); - + if (!services.isEmpty()) { + addSeparator(); + addLabel("Services"); + JFXListView listView = new JFXListView<>(); + listView.setMouseTransparent(true); + listView.setFocusTraversable(false); + listView.getItems().setAll(services); + gridPane.add(listView, 0, index++, 2, 1); + } + JFXScrollPane.smoothScrolling(scrollPane); } diff --git a/corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java b/corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java index 80a04ae5..8c34c399 100644 --- a/corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java +++ b/corefx/src/main/java/com/core/ui/dialogs/NodeEmaneDialog.java @@ -38,9 +38,8 @@ public class NodeEmaneDialog extends StageDialog { } public void setModels(List models) { - modelCombo.getItems().clear(); - models.stream().sorted() - .forEach(model -> modelCombo.getItems().add(model)); + models.sort(String::compareTo); + modelCombo.getItems().setAll(models); modelCombo.getSelectionModel().selectFirst(); } From 0805552aea7e17d3963410ba4946f2a25ef04ae6 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Wed, 28 Nov 2018 17:16:00 -0800 Subject: [PATCH 6/6] corefx - added start/stop/restart/validate service to running node context menu --- .../java/com/core/client/ICoreClient.java | 8 ++ .../com/core/client/rest/CoreRestClient.java | 28 ++++++ .../java/com/core/graph/NodeContextMenu.java | 99 ++++++++++++++++++- 3 files changed, 133 insertions(+), 2 deletions(-) diff --git a/corefx/src/main/java/com/core/client/ICoreClient.java b/corefx/src/main/java/com/core/client/ICoreClient.java index 5635e615..283ed107 100644 --- a/corefx/src/main/java/com/core/client/ICoreClient.java +++ b/corefx/src/main/java/com/core/client/ICoreClient.java @@ -42,6 +42,14 @@ public interface ICoreClient { String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException; + boolean startService(CoreNode node, String serviceName) throws IOException; + + boolean stopService(CoreNode node, String serviceName) throws IOException; + + boolean restartService(CoreNode node, String serviceName) throws IOException; + + boolean validateService(CoreNode node, String serviceName) throws IOException; + boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException; List getEmaneConfig(CoreNode node) throws IOException; diff --git a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java index 55f3cbff..b79c690f 100644 --- a/corefx/src/main/java/com/core/client/rest/CoreRestClient.java +++ b/corefx/src/main/java/com/core/client/rest/CoreRestClient.java @@ -159,6 +159,34 @@ public class CoreRestClient implements ICoreClient { return WebUtils.getJson(url, String.class, args); } + @Override + public boolean startService(CoreNode node, String serviceName) throws IOException { + String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/start", sessionId, node.getId(), + serviceName)); + return WebUtils.putJson(url); + } + + @Override + public boolean stopService(CoreNode node, String serviceName) throws IOException { + String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/stop", sessionId, node.getId(), + serviceName)); + return WebUtils.putJson(url); + } + + @Override + public boolean restartService(CoreNode node, String serviceName) throws IOException { + String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/restart", sessionId, node.getId(), + serviceName)); + return WebUtils.putJson(url); + } + + @Override + public boolean validateService(CoreNode node, String serviceName) throws IOException { + String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/validate", sessionId, node.getId(), + serviceName)); + return WebUtils.putJson(url); + } + @Override public boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException { String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(), diff --git a/corefx/src/main/java/com/core/graph/NodeContextMenu.java b/corefx/src/main/java/com/core/graph/NodeContextMenu.java index a328b3da..d50cd58a 100644 --- a/corefx/src/main/java/com/core/graph/NodeContextMenu.java +++ b/corefx/src/main/java/com/core/graph/NodeContextMenu.java @@ -2,17 +2,112 @@ package com.core.graph; import com.core.Controller; import com.core.data.CoreNode; +import com.core.ui.Toast; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.util.Collections; +import java.util.Set; class NodeContextMenu extends AbstractNodeContextMenu { + private static final Logger logger = LogManager.getLogger(); + NodeContextMenu(Controller controller, CoreNode coreNode) { super(controller, coreNode); setup(); } + private MenuItem createStartItem(String service) { + MenuItem menuItem = new MenuItem("Start"); + menuItem.setOnAction(event -> { + try { + boolean result = controller.getCoreClient().startService(coreNode, service); + if (result) { + Toast.success("Started " + service); + } else { + Toast.error("Failure to start " + service); + } + } catch (IOException ex) { + Toast.error("Error starting " + service, ex); + } + }); + return menuItem; + } + + private MenuItem createStopItem(String service) { + MenuItem menuItem = new MenuItem("Stop"); + menuItem.setOnAction(event -> { + try { + boolean result = controller.getCoreClient().stopService(coreNode, service); + if (result) { + Toast.success("Stopped " + service); + } else { + Toast.error("Failure to stop " + service); + } + } catch (IOException ex) { + Toast.error("Error stopping " + service, ex); + } + }); + return menuItem; + } + + private MenuItem createRestartItem(String service) { + MenuItem menuItem = new MenuItem("Restart"); + menuItem.setOnAction(event -> { + try { + boolean result = controller.getCoreClient().restartService(coreNode, service); + if (result) { + Toast.success("Restarted " + service); + } else { + Toast.error("Failure to restart " + service); + } + } catch (IOException ex) { + Toast.error("Error restarting " + service, ex); + } + }); + return menuItem; + } + + private MenuItem createValidateItem(String service) { + MenuItem menuItem = new MenuItem("Validate"); + menuItem.setOnAction(event -> { + try { + boolean result = controller.getCoreClient().validateService(coreNode, service); + if (result) { + Toast.success("Validated " + service); + } else { + Toast.error("Validation failed for " + service); + } + } catch (IOException ex) { + Toast.error("Error validating " + service, ex); + } + }); + return menuItem; + } + private void setup() { if (controller.getCoreClient().isRunning()) { - addMenuItem("Manage Services", event -> { - }); + Set services = coreNode.getServices(); + if (services.isEmpty()) { + services = controller.getDefaultServices().getOrDefault(coreNode.getModel(), Collections.emptySet()); + } + + if (!services.isEmpty()) { + Menu menu = new Menu("Manage Services"); + for (String service : services) { + Menu serviceMenu = new Menu(service); + MenuItem startItem = createStartItem(service); + MenuItem stopItem = createStopItem(service); + MenuItem restartItem = createRestartItem(service); + MenuItem validateItem = createValidateItem(service); + serviceMenu.getItems().addAll(startItem, stopItem, restartItem, validateItem); + menu.getItems().add(serviceMenu); + } + getItems().add(menu); + } } else { addMenuItem("Services", event -> controller.getNodeServicesDialog().showDialog(coreNode)); addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));