80: Introduce an explicit Hub-managed JavaFX runtime service
This commit is contained in:
@@ -3,8 +3,8 @@ package com.r35157.evelyn.emc.impl.ref;
|
||||
import com.fanitas.evelyn.core.Evelyn;
|
||||
import com.fanitas.evelyn.core.EvelynStatusIndexPoint;
|
||||
import com.r35157.evelyn.emc.EvelynMissionControl;
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.nenjim.process.NenjimProcess;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
@@ -34,16 +34,20 @@ public final class EvelynMissionControlImpl implements
|
||||
|
||||
public EvelynMissionControlImpl(
|
||||
@NotNull Evelyn evelynServiceProd,
|
||||
@NotNull Evelyn evelynServiceTest
|
||||
@NotNull Evelyn evelynServiceTest,
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService
|
||||
) {
|
||||
this.evelynProd = Objects.requireNonNull(evelynServiceProd, "evelynServiceProd");
|
||||
this.evelynTest = Objects.requireNonNull(evelynServiceTest, "evelynServiceTest");
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
refreshTimeline.setCycleCount(Timeline.INDEFINITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
}
|
||||
|
||||
private void showWindow() {
|
||||
@@ -311,6 +315,7 @@ public final class EvelynMissionControlImpl implements
|
||||
|
||||
private final Evelyn evelynProd;
|
||||
private final Evelyn evelynTest;
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
private final Timeline refreshTimeline = new Timeline(new KeyFrame(
|
||||
Duration.seconds(CHART_REFRESH_SECONDS),
|
||||
event -> refreshCharts()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.r35157.libs.javafx;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
||||
public final class JavaFxRuntime {
|
||||
|
||||
public static void runLater(Runnable runnable) {
|
||||
initialize();
|
||||
Platform.runLater(runnable);
|
||||
}
|
||||
|
||||
private static synchronized void initialize() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.startup(() -> Platform.setImplicitExit(false));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private JavaFxRuntime() {
|
||||
}
|
||||
|
||||
private static boolean initialized;
|
||||
}
|
||||
@@ -1,14 +1,24 @@
|
||||
package com.r35157.nenjim.composer.impl.ref;
|
||||
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.nenjim.composer.NenjimComposer;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import javafx.stage.Stage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NenjimComposerImpl implements NenjimComposer {
|
||||
|
||||
public NenjimComposerImpl(
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService) {
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
}
|
||||
|
||||
private void showWindow() {
|
||||
@@ -16,4 +26,6 @@ public final class NenjimComposerImpl implements NenjimComposer {
|
||||
window.setTitle("Nenjim Composer");
|
||||
window.show();
|
||||
}
|
||||
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ import com.r35157.libs.solana.impl.ref.SolanaBlockChainImpl;
|
||||
import com.r35157.libs.valuetypes.basic.TradingPair;
|
||||
import com.r35157.service.notification.BoundNotificationService;
|
||||
import com.r35157.service.notification.impl.discord.DiscordNotificationServiceImpl;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import com.r35157.service.javafx.impl.ref.JavaFxRuntimeServiceImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -95,6 +97,7 @@ public final class NenjimHubImpl implements NenjimHub {
|
||||
startProcess(registryService, EVELYN_SERVICE_PROD_ID);
|
||||
startProcess(registryService, EVELYN_SERVICE_TEST_ID);
|
||||
startProcess(registryService, EVELYN_IOU_BURNER_PROD_ID);
|
||||
startProcess(registryService, JAVAFX_RUNTIME_SERVICE_ID);
|
||||
startProcess(registryService, EVELYN_MISSION_CONTROL_ID);
|
||||
startProcess(registryService, PROCESS_MANAGER_ID);
|
||||
|
||||
@@ -277,12 +280,22 @@ public final class NenjimHubImpl implements NenjimHub {
|
||||
|
||||
private void initializeApplications(
|
||||
NenjimRegistryServiceManager registryServiceManager) {
|
||||
missionControl = new EvelynMissionControlImpl(evelynServiceProd, evelynServiceTest);
|
||||
javaFxRuntimeService = new JavaFxRuntimeServiceImpl();
|
||||
registryServiceManager.registerComponent(
|
||||
JAVAFX_RUNTIME_SERVICE_ID,
|
||||
javaFxRuntimeService);
|
||||
|
||||
missionControl = new EvelynMissionControlImpl(
|
||||
evelynServiceProd,
|
||||
evelynServiceTest,
|
||||
javaFxRuntimeService);
|
||||
registryServiceManager.registerComponent(
|
||||
EVELYN_MISSION_CONTROL_ID,
|
||||
missionControl);
|
||||
|
||||
processManager = new NenjimProcessManagerImpl(this);
|
||||
processManager = new NenjimProcessManagerImpl(
|
||||
this,
|
||||
javaFxRuntimeService);
|
||||
registryServiceManager.registerComponent(
|
||||
PROCESS_MANAGER_ID,
|
||||
processManager);
|
||||
@@ -364,6 +377,8 @@ public final class NenjimHubImpl implements NenjimHub {
|
||||
new NenjimComponentId("evelyn.iou-burner.prod");
|
||||
private static final NenjimComponentId EVELYN_MISSION_CONTROL_ID =
|
||||
new NenjimComponentId("evelyn.mission-control.default");
|
||||
private static final NenjimComponentId JAVAFX_RUNTIME_SERVICE_ID =
|
||||
new NenjimComponentId("nenjim.javafx-runtime.service");
|
||||
private static final NenjimComponentId PROCESS_MANAGER_ID =
|
||||
new NenjimComponentId("nenjim.process-manager.default");
|
||||
private static final ΩRaydiumLiquidityPoolIdΩ EVE_USDT_RAYDIUM_POOL_ID =
|
||||
@@ -404,6 +419,7 @@ public final class NenjimHubImpl implements NenjimHub {
|
||||
private EvelynIOUBurnerService evelynIOUBurnerServiceProd;
|
||||
|
||||
// Application components
|
||||
private JavaFxRuntimeService javaFxRuntimeService;
|
||||
private EvelynMissionControl missionControl;
|
||||
private NenjimProcessManager processManager;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.r35157.nenjim.npm.impl.ref;
|
||||
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.nenjim.hubd.NenjimHub;
|
||||
import com.r35157.nenjim.npm.NenjimProcessManager;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.SelectionMode;
|
||||
@@ -18,8 +18,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public final class NenjimProcessManagerImpl implements NenjimProcessManager {
|
||||
|
||||
public NenjimProcessManagerImpl(@NotNull NenjimHub nenjimHub) {
|
||||
public NenjimProcessManagerImpl(
|
||||
@NotNull NenjimHub nenjimHub,
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService) {
|
||||
this.nenjimHub = Objects.requireNonNull(nenjimHub, "nenjimHub");
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,7 +34,7 @@ public final class NenjimProcessManagerImpl implements NenjimProcessManager {
|
||||
"Nenjim Process Manager start has already been attempted");
|
||||
}
|
||||
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
|
||||
Thread pollingWorker = new Thread(
|
||||
this::pollStartedProcesses,
|
||||
@@ -69,7 +74,7 @@ public final class NenjimProcessManagerImpl implements NenjimProcessManager {
|
||||
.stream()
|
||||
.map(componentId -> componentId.value())
|
||||
.toList();
|
||||
JavaFxRuntime.runLater(
|
||||
javaFxRuntimeService.runLater(
|
||||
() -> replaceDisplayedSnapshot(processIds));
|
||||
} catch (RuntimeException failure) {
|
||||
log.error(
|
||||
@@ -94,6 +99,7 @@ public final class NenjimProcessManagerImpl implements NenjimProcessManager {
|
||||
NenjimProcessManagerImpl.class);
|
||||
private static final Duration POLL_INTERVAL = Duration.ofSeconds(3);
|
||||
private final NenjimHub nenjimHub;
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
private final AtomicBoolean startAttempted = new AtomicBoolean();
|
||||
private ListView<String> processList;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
package com.r35157.nenjim.ntt.impl.ref;
|
||||
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.nenjim.ntt.NenjimTestTool;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import javafx.stage.Stage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NenjimTestToolImpl implements NenjimTestTool {
|
||||
|
||||
public NenjimTestToolImpl(
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService) {
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
}
|
||||
|
||||
private void showWindow() {
|
||||
@@ -16,4 +26,6 @@ public final class NenjimTestToolImpl implements NenjimTestTool {
|
||||
window.setTitle("Nenjim Test Tool");
|
||||
window.show();
|
||||
}
|
||||
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.r35157.service.javafx;
|
||||
|
||||
import com.r35157.nenjim.component.NenjimComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/** Access to the JVM-wide JavaFX Application Thread. */
|
||||
public interface JavaFxRuntimeService extends NenjimComponent {
|
||||
void runLater(@NotNull Runnable runnable);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.r35157.service.javafx.impl.ref;
|
||||
|
||||
import com.r35157.nenjim.process.NenjimProcess;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import javafx.application.Platform;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/** Hub-managed owner of the single JavaFX toolkit used by this JVM. */
|
||||
public final class JavaFxRuntimeServiceImpl implements
|
||||
JavaFxRuntimeService,
|
||||
NenjimProcess {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!startAttempted.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException(
|
||||
"JavaFX runtime service start has already been attempted");
|
||||
}
|
||||
|
||||
CountDownLatch startupCompleted = new CountDownLatch(1);
|
||||
AtomicReference<Throwable> startupFailure = new AtomicReference<>();
|
||||
|
||||
try {
|
||||
Platform.startup(() -> {
|
||||
try {
|
||||
Platform.setImplicitExit(false);
|
||||
} catch (Throwable failure) {
|
||||
startupFailure.set(failure);
|
||||
} finally {
|
||||
startupCompleted.countDown();
|
||||
}
|
||||
});
|
||||
} catch (RuntimeException | Error failure) {
|
||||
throw failedStartup(failure);
|
||||
}
|
||||
|
||||
awaitStartupCompletion(startupCompleted);
|
||||
|
||||
Throwable failure = startupFailure.get();
|
||||
if (failure != null) {
|
||||
throw failedStartup(failure);
|
||||
}
|
||||
available = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runLater(@NotNull Runnable runnable) {
|
||||
Objects.requireNonNull(runnable, "runnable");
|
||||
if (!available) {
|
||||
throw new IllegalStateException(
|
||||
"JavaFX runtime service has not started successfully");
|
||||
}
|
||||
Platform.runLater(runnable);
|
||||
}
|
||||
|
||||
private static void awaitStartupCompletion(
|
||||
CountDownLatch startupCompleted) {
|
||||
try {
|
||||
startupCompleted.await();
|
||||
} catch (InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(
|
||||
"Interrupted while waiting for JavaFX runtime startup",
|
||||
interrupted);
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalStateException failedStartup(Throwable failure) {
|
||||
return new IllegalStateException(
|
||||
"JavaFX runtime service failed to start",
|
||||
failure);
|
||||
}
|
||||
|
||||
private final AtomicBoolean startAttempted = new AtomicBoolean();
|
||||
private volatile boolean available;
|
||||
}
|
||||
@@ -1,14 +1,24 @@
|
||||
package com.r35157.stm.impl.ref;
|
||||
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import com.r35157.stm.SodaTaskManager;
|
||||
import javafx.stage.Stage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class SodaTaskManagerImpl implements SodaTaskManager {
|
||||
|
||||
public SodaTaskManagerImpl(
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService) {
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
}
|
||||
|
||||
private void showWindow() {
|
||||
@@ -16,4 +26,6 @@ public final class SodaTaskManagerImpl implements SodaTaskManager {
|
||||
window.setTitle("Soda Task Manager");
|
||||
window.show();
|
||||
}
|
||||
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
package com.r35157.suwimo.hub.client.impl.j2fx;
|
||||
|
||||
import com.r35157.libs.javafx.JavaFxRuntime;
|
||||
import com.r35157.service.javafx.JavaFxRuntimeService;
|
||||
import com.r35157.suwimo.hub.client.SuwimoClient;
|
||||
import javafx.stage.Stage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class SuwimoClientImpl implements SuwimoClient {
|
||||
|
||||
public SuwimoClientImpl(
|
||||
@NotNull JavaFxRuntimeService javaFxRuntimeService) {
|
||||
this.javaFxRuntimeService = Objects.requireNonNull(
|
||||
javaFxRuntimeService,
|
||||
"javaFxRuntimeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JavaFxRuntime.runLater(this::showWindow);
|
||||
javaFxRuntimeService.runLater(this::showWindow);
|
||||
}
|
||||
|
||||
private void showWindow() {
|
||||
@@ -16,4 +26,6 @@ public final class SuwimoClientImpl implements SuwimoClient {
|
||||
window.setTitle("Suwimo Client");
|
||||
window.show();
|
||||
}
|
||||
|
||||
private final JavaFxRuntimeService javaFxRuntimeService;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user