Fix GUI startup freeze, JSON crash, and WebSocket thread bugs#5
Open
JoOkuma wants to merge 4 commits into
Open
Fix GUI startup freeze, JSON crash, and WebSocket thread bugs#5JoOkuma wants to merge 4 commits into
JoOkuma wants to merge 4 commits into
Conversation
The GUI would freeze on first launch because the JavaFX Application Thread was blocked inside MainApp while CondaEnvironmentFinder waited for user input (lock.wait). Move the conda-path lookup to a daemon thread so the loading screen renders immediately and Swing dialogs can receive events. JavaConnector crashed with JsonSyntaxException when the Python server sent a plain-text error message instead of a JSON object (e.g. an unhandled traceback). Wrap the Gson parse in a try-catch and route non-JSON payloads to the error log. Additional hardening: - UltrackConnector.startServer: guard against a null/empty ultrackPath before launching the subprocess, showing a clear error dialog instead of a silent NullPointerException. - CondaEnvironmentFinder.updateCondaEnvironments: switch from Runtime.exec(String) to ProcessBuilder so conda paths containing spaces are handled correctly on Windows. - ultrack_server.js: guard against undefined fetch response data and add a .catch() so network errors during server polling are logged rather than silently swallowed. Fixes royerlab/ultrack#160, royerlab/ultrack#243, #4
…-1 race
- UltrackConnector.connectToWebsocket: latch.await() was called on the
calling thread; since connectToUltrackWebsocket is invoked from JS
(JavaFX Application Thread), this froze the UI during the WebSocket
handshake — same class of bug as the startup freeze. The await+send
is now done in a daemon background thread.
- JavaConnector.onMessageConsumer: jsonObject.get("err_log"),
"std_log", and "status" had no null checks. If the server sends JSON
that omits any of these keys, a NullPointerException crashed the
message handler silently. Replaced with jsonObject.has() guards.
- JavaConnector.startUltrackServer: when startServer() returned early
because ultrackPath was null, port stayed -1. The method still called
javascriptConnector.call("setPort", -1), causing the JS polling loop
to spin on 127.0.0.1:-1/config/available indefinitely. Added an
early-return guard on port == -1.
- MainApp.onUpdateCondaEnv: InterruptedException was wrapped in
RuntimeException and re-thrown, which crashed the background thread
silently instead of restoring the interrupt flag.
UltrackConnector.connectToWebsocket: if the server isn't listening when c.connect() fires, onOpen is never called so the CountDownLatch stays at 1 and the background connect thread blocks forever. Add a 30-second timeout and an isOpen guard so the thread always exits — either by sending the message, reporting a timeout error, or silently returning when onErrorConsumer has already surfaced the failure. ultrack_server.js: the viewButton fetch had no null guard on the response data and no .catch(), so a non-OK response or network error would silently drop the view request. Mirror the same pattern already applied to the server-polling loop.
MainApp.java - onLoadUltrackPath: keep a reference to the ChangeListener and remove it before adding a new one, so repeated calls (conda env change) don't accumulate stale listeners that re-wire javaConnector on every page load. - Replace 'assert url != null' with a proper null check + error dialog; assertions are disabled at runtime by default and would silently pass. JavaConnector.java - stopUltrackServer: guard against null ultrackConnector so calling stop before the server is ever started doesn't throw NPE. CondaEnvironmentFinder.java - getUltrackPath: convert tail-recursive implementation to an iterative while-loop; users who repeatedly pick an environment without ultrack installed would otherwise overflow the call stack. ultrack_server.js - Declare all implicit globals with let/const so they are properly scoped rather than silently leaking onto the window object: connection_successfull, available_configs, link, config, human_name, inputs, input, value, id, prev, json, images_json, image_options, available, experimentJson, xml. pom.xml / tests - Add JUnit 5 dependency (test scope). - UltrackConnectorTest: isPortOpen utility and null/empty-path startServer guards. - CondaEnvironmentFinderTest: getUltrackPath resolves executable in bin/ultrack and Scripts/ultrack.exe without opening a dialog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three distinct bugs were causing the plugin to be unusable on first launch and during tracking:
GUI freeze on startup (#243, server connection issue #4): The JavaFX Application Thread was blocked inside
MainAppwhileCondaEnvironmentFinderwaited for user input vialock.wait(). Because the JAT was stalled, the loading screen never rendered, and dialogs couldn't receive events — the window appeared frozen.Crash when server sends a plain-text error (error running ultrack plugging inside fiji ultrack#160): Python's
raise_error()sends a raw traceback string over the WebSocket.JavaConnector.onMessageConsumerpassed this directly togson.fromJson(..., JsonObject.class), which threwJsonSyntaxExceptionand crashed the message handler.GUI freeze during WebSocket connect:
UltrackConnector.connectToWebsocketcalledlatch.await()on the calling thread. SinceconnectToUltrackWebsocketis invoked from JavaScript (JavaFX thread), this blocked the JAT during the WebSocket handshake — the same class of bug as Minor refactor #1.Changes
MainApp.javaCondaEnvironmentFinder.getUltrackPath()to a daemon background thread so the JAT is never blocked.onUpdateCondaEnv: restore interrupt flag withThread.currentThread().interrupt()instead of wrapping inRuntimeException.JavaConnector.javagson.fromJson()in a try-catch; route plain-text / malformed responses to the error log instead of crashing.jsonObject.has()guards before accessing"err_log","std_log", and"status"keys to prevent NPE on partial JSON.startUltrackServeragainstport == -1(returned whenultrackPathis null) so the JS polling loop doesn't spin on127.0.0.1:-1/config/availableforever.UltrackConnector.javalatch.await()+c.send()into a daemon background thread so the WebSocket handshake no longer blocks the JavaFX Application Thread.ultrackPathat the start ofstartServer()with a clear error dialog.CondaEnvironmentFinder.javaupdateCondaEnvironmentsfromRuntime.exec(String)toProcessBuilderso conda paths containing spaces are handled correctly on Windows.ultrack_server.jsundefinedfetch response data..catch()to the polling loop so network errors are logged rather than silently swallowed.Related issues
Fixes royerlab/ultrack#160, royerlab/ultrack#243, #4