From f24276a6c2132ae8403b45b2d7f43360d85e616d Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Mon, 3 Apr 2023 11:49:23 -0700 Subject: [PATCH] MATP-1106 Photon Shutdown Hang --- .../java/org/marketcetera/ui/DragResizer.java | 115 ------------- .../java/org/marketcetera/ui/Draggable.java | 152 ------------------ .../java/org/marketcetera/ui/LoginView.java | 4 +- .../java/org/marketcetera/ui/PhotonApp.java | 115 ++++++------- .../marketcetera/ui/PrimaryController.java | 12 -- .../marketcetera/ui/SecondaryController.java | 12 -- 6 files changed, 60 insertions(+), 350 deletions(-) delete mode 100644 photon/src/main/java/org/marketcetera/ui/DragResizer.java delete mode 100644 photon/src/main/java/org/marketcetera/ui/Draggable.java delete mode 100644 photon/src/main/java/org/marketcetera/ui/PrimaryController.java delete mode 100644 photon/src/main/java/org/marketcetera/ui/SecondaryController.java diff --git a/photon/src/main/java/org/marketcetera/ui/DragResizer.java b/photon/src/main/java/org/marketcetera/ui/DragResizer.java deleted file mode 100644 index 29191c7ddd..0000000000 --- a/photon/src/main/java/org/marketcetera/ui/DragResizer.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.marketcetera.ui; - - -import javafx.event.EventHandler; -import javafx.scene.Cursor; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Region; - -/** - * {@link DragResizer} can be used to add mouse listeners to a {@link Region} - * and make it resizable by the user by clicking and dragging the border in the - * same way as a window. - *

- * Only height resizing is currently implemented. Usage:

DragResizer.makeResizable(myAnchorPane);
- * - * @author atill - * - */ -public class DragResizer { - - /** - * The margin around the control that a user can click in to start resizing - * the region. - */ - private static final int RESIZE_MARGIN = 5; - - private final Region region; - - private double y; - - private boolean initMinHeight; - - private boolean dragging; - - private DragResizer(Region aRegion) { - region = aRegion; - } - - public static void makeResizable(Region region) { - final DragResizer resizer = new DragResizer(region); - - region.setOnMousePressed(new EventHandler() { - @Override - public void handle(MouseEvent event) { - resizer.mousePressed(event); - }}); - region.setOnMouseDragged(new EventHandler() { - @Override - public void handle(MouseEvent event) { - resizer.mouseDragged(event); - }}); - region.setOnMouseMoved(new EventHandler() { - @Override - public void handle(MouseEvent event) { - resizer.mouseOver(event); - }}); - region.setOnMouseReleased(new EventHandler() { - @Override - public void handle(MouseEvent event) { - resizer.mouseReleased(event); - }}); - } - - protected void mouseReleased(MouseEvent event) { - dragging = false; - region.setCursor(Cursor.DEFAULT); - } - - protected void mouseOver(MouseEvent event) { - if(isInDraggableZone(event) || dragging) { - region.setCursor(Cursor.S_RESIZE); - } - else { - region.setCursor(Cursor.DEFAULT); - } - } - - protected boolean isInDraggableZone(MouseEvent event) { - return event.getY() > (region.getHeight() - RESIZE_MARGIN); - } - - protected void mouseDragged(MouseEvent event) { - if(!dragging) { - return; - } - - double mousey = event.getY(); - - double newHeight = region.getMinHeight() + (mousey - y); - - region.setMinHeight(newHeight); - - y = mousey; - } - - protected void mousePressed(MouseEvent event) { - - // ignore clicks outside of the draggable margin - if(!isInDraggableZone(event)) { - return; - } - - dragging = true; - - // make sure that the minimum height is set to the current height once, - // setting a min height that is smaller than the current height will - // have no effect - if (!initMinHeight) { - region.setMinHeight(region.getHeight()); - initMinHeight = true; - } - - y = event.getY(); - } -} diff --git a/photon/src/main/java/org/marketcetera/ui/Draggable.java b/photon/src/main/java/org/marketcetera/ui/Draggable.java deleted file mode 100644 index a44d673e0a..0000000000 --- a/photon/src/main/java/org/marketcetera/ui/Draggable.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.marketcetera.ui; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javafx.event.EventHandler; -import javafx.scene.Node; -import javafx.scene.input.MouseEvent; - -/** - * Generalised implementation of 'Draggability' of a {@link Node}. The Draggable class is used as a 'namespace' for the internal - * class/interfaces/enum. - * @author phill - * - */ -public class Draggable { - public enum Event { - None, DragStart, Drag, DragEnd - } - - /** - * Marker for an entity that has draggable nature. - * @author phill - */ - public interface Interface { - public abstract Draggable.Nature getDraggableNature(); - } - - public interface Listener { - public void accept(Nature draggableNature, Event dragEvent); - } - - /** - * Class that encapsulates the draggable nature of a node. - *
    - *
  • EventNode: the event that receives the drag events
  • - *
  • One or more DragNodes: that move in response to the drag events. The EventNode is usually (but not always) a - * DragNode
  • - *
  • Listeners: listen for the drag events
  • - *
- * @author phill - * - */ - public static final class Nature implements EventHandler { - private double lastMouseX = 0, lastMouseY = 0; // scene coords - - private boolean dragging = false; - - private final boolean enabled = true; - private final Node eventNode; - private final List dragNodes = new ArrayList<>(); - private final List dragListeners = new ArrayList<>(); - - public Nature(final Node node) { - this(node, node); - } - - public Nature(final Node eventNode, final Node... dragNodes) { - this.eventNode = eventNode; - this.dragNodes.addAll(Arrays.asList(dragNodes)); - this.eventNode.addEventHandler(MouseEvent.ANY, this); - } - - public final boolean addDraggedNode(final Node node) { - if (!this.dragNodes.contains(node)) { - return this.dragNodes.add(node); - } - return false; - } - - public final boolean addListener(final Listener listener) { - return this.dragListeners.add(listener); - } - - public final void detatch() { - this.eventNode.removeEventFilter(MouseEvent.ANY, this); - } - - public final List getDragNodes() { - return new ArrayList<>(this.dragNodes); - } - - public final Node getEventNode() { - return this.eventNode; - } - - @Override - public final void handle(final MouseEvent event) { - if (MouseEvent.MOUSE_PRESSED == event.getEventType()) { - if (this.enabled && this.eventNode.contains(event.getX(), event.getY())) { - this.lastMouseX = event.getSceneX(); - this.lastMouseY = event.getSceneY(); - event.consume(); - } - } else if (MouseEvent.MOUSE_DRAGGED == event.getEventType()) { - if (!this.dragging) { - this.dragging = true; - for (final Listener listener : this.dragListeners) { - listener.accept(this, Draggable.Event.DragStart); - } - } - if (this.dragging) { - final double deltaX = event.getSceneX() - this.lastMouseX; - final double deltaY = event.getSceneY() - this.lastMouseY; - - for (final Node dragNode : this.dragNodes) { - final double initialTranslateX = dragNode.getTranslateX(); - final double initialTranslateY = dragNode.getTranslateY(); - dragNode.setTranslateX(initialTranslateX + deltaX); - dragNode.setTranslateY(initialTranslateY + deltaY); - } - - this.lastMouseX = event.getSceneX(); - this.lastMouseY = event.getSceneY(); - - event.consume(); - for (final Listener listener : this.dragListeners) { - listener.accept(this, Draggable.Event.Drag); - } - } - } else if (MouseEvent.MOUSE_RELEASED == event.getEventType()) { - if (this.dragging) { - event.consume(); - this.dragging = false; - for (final Listener listener : this.dragListeners) { - listener.accept(this, Draggable.Event.DragEnd); - } - } - } - - } - - public final boolean removeDraggedNode(final Node node) { - return this.dragNodes.remove(node); - } - - public final boolean removeListener(final Listener listener) { - return this.dragListeners.remove(listener); - } - - /** - * When the initial mousePressed is missing we can supply the first coordinates programmatically. - * @param lastMouseX - * @param lastMouseY - */ - public final void setLastMouse(final double lastMouseX, final double lastMouseY) { - this.lastMouseX = lastMouseX; - this.lastMouseY = lastMouseY; - } - } -} diff --git a/photon/src/main/java/org/marketcetera/ui/LoginView.java b/photon/src/main/java/org/marketcetera/ui/LoginView.java index 07c3017dde..dd8fbb5fd5 100644 --- a/photon/src/main/java/org/marketcetera/ui/LoginView.java +++ b/photon/src/main/java/org/marketcetera/ui/LoginView.java @@ -16,7 +16,6 @@ import org.springframework.stereotype.Component; import io.grpc.StatusRuntimeException; -import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.event.ActionEvent; import javafx.geometry.Insets; @@ -171,8 +170,7 @@ private void onLogin(ActionEvent inEvent) */ private void onCloseRequest(WindowEvent inEvent) { - // shutdown the whole app - Platform.exit(); + PhotonApp.getApp().doAppShutdown(); } /** * main scene of the dialog diff --git a/photon/src/main/java/org/marketcetera/ui/PhotonApp.java b/photon/src/main/java/org/marketcetera/ui/PhotonApp.java index 8ce11caffb..dd3b61ed5e 100644 --- a/photon/src/main/java/org/marketcetera/ui/PhotonApp.java +++ b/photon/src/main/java/org/marketcetera/ui/PhotonApp.java @@ -3,12 +3,9 @@ import java.awt.Taskbar; import java.awt.Taskbar.Feature; import java.awt.Toolkit; -import java.io.IOException; -import java.util.Properties; import org.marketcetera.ui.events.LoginEvent; import org.marketcetera.ui.events.LogoutEvent; -import org.marketcetera.ui.service.DisplayLayoutService; import org.marketcetera.ui.service.PhotonNotificationService; import org.marketcetera.ui.service.SessionUser; import org.marketcetera.ui.service.StyleService; @@ -23,9 +20,7 @@ import javafx.application.Application; import javafx.application.Platform; -import javafx.fxml.FXMLLoader; import javafx.geometry.Orientation; -import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.Separator; @@ -51,6 +46,15 @@ public class PhotonApp extends Application { + /** + * Main application entry point. + * + * @param args a String[] value + */ + public static void main(String[] args) + { + launch(); + } /* (non-Javadoc) * @see javafx.application.Application#init() */ @@ -58,11 +62,11 @@ public class PhotonApp public void init() throws Exception { + instance = this; super.init(); applicationContext = new AnnotationConfigApplicationContext("org.marketcetera","com.marketcetera"); webMessageService = applicationContext.getBean(UiMessageService.class); styleService = applicationContext.getBean(StyleService.class); - displayLayoutService = applicationContext.getBean(DisplayLayoutService.class); webMessageService.register(this); } /* (non-Javadoc) @@ -106,13 +110,7 @@ public void start(Stage inPrimaryStage) } } inPrimaryStage.setOnCloseRequest(closeEvent -> { - isShuttingDown = true; - webMessageService.post(new LogoutEvent()); - try { - ((ConfigurableApplicationContext)applicationContext).close(); - } catch (Exception ignored) {} - Platform.exit(); - System.exit(0); + doAppShutdown(); }); VBox.setVgrow(menuLayout, Priority.NEVER); @@ -130,6 +128,19 @@ public void start(Stage inPrimaryStage) inPrimaryStage.show(); doLogin(); } + /** + * Shutdown the app. + */ + public void doAppShutdown() + { + isShuttingDown = true; + webMessageService.post(new LogoutEvent()); + try { + ((ConfigurableApplicationContext)applicationContext).close(); + } catch (Exception ignored) {} + Platform.exit(); + System.exit(0); + } /** * Receive LoginEvent values. * @@ -173,6 +184,33 @@ public void onLogout(LogoutEvent inEvent) } }); } + /** + * Get the primary application stage. + * + * @return a Stage value + */ + public static Stage getPrimaryStage() + { + return primaryStage; + } + /** + * Get the main workspace. + * + * @return a Pane value + */ + public static Pane getWorkspace() + { + return workspace; + } + /** + * Get the main singleton application instance. + * + * @return a PhotonApp value + */ + public static PhotonApp getApp() + { + return instance; + } /** * Initialize the workspace footer. */ @@ -213,6 +251,9 @@ private void initializeFooter() clockLabel, userLabel); } + /** + * Build and show the main application menu. + */ private void showMenu() { SessionUser currentUser = SessionUser.getCurrent(); @@ -230,53 +271,15 @@ private void showMenu() } applicationMenu.refreshMenuPermissions(); } + /** + * Perform the login sequence, including showing the login dialog to the user. + */ private void doLogin() { LoginView loginView = applicationContext.getBean(LoginView.class); loginView.showAndWait(); showMenu(); } - static void setRoot(String fxml) - throws IOException - { - scene.setRoot(loadFXML(fxml)); - } - - private static Parent loadFXML(String fxml) - throws IOException - { - FXMLLoader fxmlLoader = new FXMLLoader(PhotonApp.class.getResource(fxml + ".fxml")); - return fxmlLoader.load(); - } - public static Stage getPrimaryStage() - { - return primaryStage; - } - public static Pane getWorkspace() - { - return workspace; - } - public static void main(String[] args) - { - launch(); - } - private Properties displayProperties; - /** - * base key for {@see UserAttributeType} display layout properties - */ - private static final String propId = PhotonApp.class.getSimpleName(); - /** - * workspace width key name - */ - private static final String workspaceWidthProp = propId + "_workspaceWidth"; - /** - * workspace height key name - */ - private static final String workspaceHeightProp = propId + "_workspaceHeight"; - /** - * workspace layout key name - */ - private static final String mainWorkspaceLayoutKey = propId + "_workspaceDisplayLayout"; /** * footer holder for the server connection status image */ @@ -313,7 +316,7 @@ public static void main(String[] args) private ToolBar footerToolBar; private PhotonNotificationService notificationService; /** - * provides access to display layout services + * singleton applcation isntance */ - private DisplayLayoutService displayLayoutService; + private static PhotonApp instance; } diff --git a/photon/src/main/java/org/marketcetera/ui/PrimaryController.java b/photon/src/main/java/org/marketcetera/ui/PrimaryController.java deleted file mode 100644 index 609bf40a4e..0000000000 --- a/photon/src/main/java/org/marketcetera/ui/PrimaryController.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.marketcetera.ui; - -import java.io.IOException; -import javafx.fxml.FXML; - -public class PrimaryController { - - @FXML - private void switchToSecondary() throws IOException { - PhotonApp.setRoot("secondary"); - } -} diff --git a/photon/src/main/java/org/marketcetera/ui/SecondaryController.java b/photon/src/main/java/org/marketcetera/ui/SecondaryController.java deleted file mode 100644 index 9cc72bd1f3..0000000000 --- a/photon/src/main/java/org/marketcetera/ui/SecondaryController.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.marketcetera.ui; - -import java.io.IOException; -import javafx.fxml.FXML; - -public class SecondaryController { - - @FXML - private void switchToPrimary() throws IOException { - PhotonApp.setRoot("primary"); - } -} \ No newline at end of file