EmulatorLifecycle.java
package com.cloudforge.core.local;
import java.io.IOException;
import java.io.UncheckedIOException;
/**
* Applies {@link EmulatorLifecycleAction} to a {@link LocalEmulatorRuntime}.
*
* <p>Companion services (StackPort simulated console + nginx emulator edge) are part of the
* same lifecycle process: started after the emulator, stopped with it (edge only when no
* emulator remains), and reported by {@link EmulatorLifecycleAction#STATUS}. Disable with
* {@code CFC_EMULATOR_COMPANIONS}, {@code CFC_STACKPORT_AUTOSTART}, or {@code CFC_EDGE_AUTOSTART}.
*/
public final class EmulatorLifecycle {
private EmulatorLifecycle() {
}
public static void execute(DeploymentTarget target, EmulatorLifecycleAction action)
throws IOException {
execute(LocalEmulatorRuntimes.forTarget(target), action);
}
public static void execute(LocalEmulatorRuntime runtime, EmulatorLifecycleAction action)
throws IOException {
DeploymentTarget target = runtime.target();
switch (action) {
case START -> {
runtime.start();
startCompanions(target);
}
case STOP -> {
stopStackPort(target);
runtime.stop();
stopEdgeIfIdle();
}
case RESTART -> {
stopStackPort(target);
runtime.restart();
startCompanions(target);
}
case STATUS -> {
logStatus(runtime);
logCompanionStatus(target);
}
}
}
public static void executeUnchecked(DeploymentTarget target, EmulatorLifecycleAction action) {
try {
execute(target, action);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static void logStatus(LocalEmulatorRuntime runtime) throws IOException {
System.out.println(runtime.target() + " container " + runtime.containerName()
+ " running=" + runtime.isRunning()
+ " healthy=" + runtime.isHealthy()
+ " endpoint=" + runtime.healthEndpoint());
}
private static void startCompanions(DeploymentTarget target) throws IOException {
if (!companionsEnabled()) {
System.out.println("Emulator companions skipped (CFC_EMULATOR_COMPANIONS=false)");
return;
}
if (stackPortAutostartEnabled()) {
StackPortLifecycle.execute(target, StackPortLifecycleAction.START);
}
if (EmulatorEdgeLifecycle.autostartEnabled()) {
EmulatorEdgeLifecycle.ensureRunningAndReconcile();
}
}
private static void stopStackPort(DeploymentTarget target) throws IOException {
if (!companionsEnabled() || !stackPortAutostartEnabled()) {
return;
}
StackPortLifecycle.execute(target, StackPortLifecycleAction.STOP);
}
/**
* Edge is shared across MiniStack/LocalStack — stop only when neither emulator is up.
*/
private static void stopEdgeIfIdle() throws IOException {
if (!companionsEnabled() || !EmulatorEdgeLifecycle.autostartEnabled()) {
return;
}
if (anyEmulatorRunning()) {
EmulatorEdgeLifecycle.execute(EmulatorEdgeLifecycleAction.RECONCILE);
return;
}
EmulatorEdgeLifecycle.execute(EmulatorEdgeLifecycleAction.STOP);
}
private static boolean anyEmulatorRunning() {
for (LocalEmulatorRuntime runtime : LocalEmulatorRuntimes.all()) {
try {
if (runtime.isRunning()) {
return true;
}
} catch (IOException ignored) {
// treat as not running
}
}
return false;
}
private static void logCompanionStatus(DeploymentTarget target) {
try {
StackPortRuntime stackPort = StackPortRuntimes.forTarget(target);
System.out.println(target + " StackPort container " + stackPort.containerName()
+ " running=" + stackPort.isRunning()
+ " healthy=" + stackPort.isHealthy()
+ " url=" + stackPort.browserUrl());
} catch (Exception e) {
System.out.println(target + " StackPort: unavailable (" + e.getMessage() + ")");
}
try {
EmulatorEdgeRuntime edge = new DefaultEmulatorEdgeRuntime();
System.out.println("Emulator edge container " + edge.containerName()
+ " running=" + edge.isRunning()
+ " healthy=" + edge.isHealthy()
+ " url=" + edge.browserUrl());
} catch (Exception e) {
System.out.println("Emulator edge: unavailable (" + e.getMessage() + ")");
}
}
private static boolean companionsEnabled() {
return EmulatorEdgeLifecycle.envFlagTrue(LocalEmulatorDefaults.EMULATOR_COMPANIONS_ENV);
}
private static boolean stackPortAutostartEnabled() {
return companionsEnabled()
&& EmulatorEdgeLifecycle.envFlagTrue(LocalEmulatorDefaults.STACKPORT_AUTOSTART_ENV);
}
}