AbstractLocalEmulatorRuntime.java
package com.cloudforge.core.local;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Optional;
/**
* Shared Docker lifecycle for CloudForge local emulators.
*
* <p>Target modules supply {@link #dockerCreateArgs()} only; container names, health URLs,
* and conflict handling come from {@link LocalEmulatorSpec} in core.</p>
*/
public abstract class AbstractLocalEmulatorRuntime implements LocalEmulatorRuntime {
private final LocalEmulatorSpec spec;
protected AbstractLocalEmulatorRuntime(LocalEmulatorSpec spec) {
this.spec = spec;
}
protected LocalEmulatorSpec spec() {
return spec;
}
/** Arguments after {@code docker run -d} when the container does not yet exist. */
protected abstract List<String> dockerCreateArgs() throws IOException;
@Override
public DeploymentTarget target() {
return spec.target();
}
@Override
public String containerName() {
return spec.containerName();
}
@Override
public Optional<String> conflictingContainerName() {
return Optional.ofNullable(spec.conflictingContainerName());
}
@Override
public URI healthEndpoint() {
return spec.healthEndpoint();
}
@Override
public void start() throws IOException {
stopConflicting();
DockerEmulatorSupport.ensureNetwork(LocalEmulatorDefaults.DOCKER_NETWORK);
if (DockerEmulatorSupport.isContainerRunning(spec.containerName())) {
if (isHealthy()) {
System.out.println(spec.displayName() + " already running and healthy on :"
+ LocalEmulatorDefaults.GATEWAY_PORT);
return;
}
DockerEmulatorSupport.stopContainer(spec.containerName());
}
if (DockerEmulatorSupport.containerExists(spec.containerName())) {
DockerEmulatorSupport.startExistingContainer(spec.containerName());
} else {
DockerEmulatorSupport.runDetached(dockerCreateArgs());
}
DockerEmulatorSupport.waitForHealthy(healthEndpoint());
System.out.println(spec.displayName() + " started: " + healthEndpoint());
onStarted();
}
@Override
public void stop() throws IOException {
DockerEmulatorSupport.stopContainer(spec.containerName());
System.out.println(spec.displayName() + " stopped");
}
@Override
public boolean isRunning() throws IOException {
return DockerEmulatorSupport.isContainerRunning(spec.containerName());
}
@Override
public boolean isHealthy() {
return DockerEmulatorSupport.isHttpHealthy(healthEndpoint());
}
protected void onStarted() throws IOException {
// Optional hook for target-specific post-start logging
}
protected final List<String> baseDockerCreateArgs() {
return List.of(
"run", "-d",
"--name", spec.containerName(),
"--network", LocalEmulatorDefaults.DOCKER_NETWORK,
"-p", LocalEmulatorDefaults.GATEWAY_PORT + ":4566",
"-v", LocalEmulatorPaths.dockerSocketMount());
}
private void stopConflicting() throws IOException {
if (spec.conflictingContainerName() != null && !spec.conflictingContainerName().isBlank()) {
DockerEmulatorSupport.stopContainer(spec.conflictingContainerName());
}
}
}