All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.mike10004.containment.lifecycle.ContainerLifecycles Maven / Gradle / Ivy

There is a newer version: 0.6
Show newest version
package io.github.mike10004.containment.lifecycle;

import io.github.mike10004.containment.ContainerCreator;
import io.github.mike10004.containment.ContainerParametry;
import io.github.mike10004.containment.RunningContainer;
import io.github.mike10004.containment.StartableContainer;
import io.github.mike10004.containment.StartedContainer;
import io.github.mike10004.containment.dockerjava.DjContainerCreator;
import io.github.mike10004.containment.dockerjava.DjManualContainerMonitor;
import io.github.mike10004.containment.dockerjava.DjShutdownHookContainerMonitor;
import io.github.mike10004.containment.dockerjava.DockerClientBuilder;

import java.util.StringJoiner;

import static java.util.Objects.requireNonNull;

/**
 * Class that contains utility methods for building container lifecycles.
 *
 * 

We define management of a container to mean cleaning up containers created and/or started * by this library. Global management means to add a shutdown hook that performs cleanup. * There are two ways to globally manage a container: one is to construct the container * with a {@link io.github.mike10004.containment.dockerjava.DjContainerMonitor monitor} * that adds the shutdown hook, and the other is to manage the lifecycle of the container * as a resource created by * {@link LifecycledResourceBuilder#buildResourceDecommissionedOnJvmTermination(Lifecycle)}. *

*/ public class ContainerLifecycles { private ContainerLifecycles() {} private static class ContainerCreatorStage extends DecoupledLifecycle { public ContainerCreatorStage(Commissioner commissioner) { super(commissioner, new AutoCloseableDecommissioner<>()); } @Override public String toString() { return String.format("ContainerCreatorStage@%08x", hashCode()); } } private static class StartableContainerStage extends DecoupledLifecycleStage { public StartableContainerStage(ContainerParametry containerParametry) { super(creator -> creator.create(containerParametry), new AutoCloseableDecommissioner<>()); } @Override public String toString() { return String.format("StartableContainerStage@%08x", hashCode()); } } private static class SimpleStartedContainerStage extends DecoupledLifecycleStage { public SimpleStartedContainerStage() { super(StartableContainer::start, AutoCloseableDecommissioner.byTransform(StartedContainer.class::cast)); } @Override public String toString() { return String.format("SimpleStartedContainerStage@%08x", hashCode()); } } private static class ActionStageResult { public final C container; public final T content; protected ActionStageResult(C container, T content) { this.container = requireNonNull(container); this.content = content; } } private static final class PreStartResult extends ActionStageResult { public PreStartResult(StartableContainer container, T content) { super(container, content); } } private static final class PostStartResult extends ActionStageResult { public PostStartResult(StartedContainer container, T content) { super(container, content); } } private static class ContainerPreStartStage implements LifecycleStage, PreStartResult> { private final ContainerPreStartAction innerStage; public ContainerPreStartStage(ContainerPreStartAction innerStage) { this.innerStage = requireNonNull(innerStage); } public ContainerPreStartStage(ContainerInitialPreStartAction innerStage) { this((container, requirement) -> innerStage.perform(container)); } @Override public String toString() { return new StringJoiner(", ", ContainerPreStartStage.class.getSimpleName() + "[", "]") .toString(); } @Override public PreStartResult commission(PreStartResult requirement) throws Exception { V content = innerStage.perform(requirement.container, requirement.content); return new PreStartResult<>(requirement.container, content); } @Override public void decommission() { // no op } } private static class ContainerPostStartStage implements LifecycleStage, PostStartResult> { private final ContainerPostStartAction action; public ContainerPostStartStage(ContainerPostStartAction action) { this.action = action; } public ContainerPostStartStage(ContainerInitialPostStartAction action) { this((container, requirement) -> action.perform(container)); } @Override public PostStartResult commission(PostStartResult requirement) throws Exception { V content = action.perform(requirement.container, requirement.content); return new PostStartResult<>(requirement.container, content); } @Override public void decommission() { // no op } } /** * Creates a new builder of container lifecycle instances. * @param ctor constructor of the {@link ContainerCreator} instance * @return a new builder */ public static PreCreate builder(ContainerCreatorFactory ctor) { return new PreCreateImpl(ctor); } /** * Returns a service that can be used to build lifecycles of containers that are not managed. * Creating and starting unmanaged containers has system-wide effects that persist after JVM * termination. (The "system" in this context is the computer on which the JVM is running.) * Therefore, to be a good citizen of the system, you must explicitly clean up any unmanaged * containers that you create and/or start. * *

If you are building a lifecycled resource and you will execute the stages of the lifecycle, * either explicitly with * {@link LifecycledResourceBuilder#buildResource(Lifecycle)} * or implicitly with * {@link LifecycledResourceBuilder#buildResourceDecommissionedOnJvmTermination(Lifecycle)} decommission-on-shutdown}, * then it is fine to leave your containers unmanaged (because execution of the lifecycle effectively * cleans up the container). *

* @return a pre-create service */ public static PreCreate builderOfLifecyclesOfUnmanagedContainers() { ContainerCreatorFactory ctor = new GlobalContainerCreatorFactory(DjContainerCreator::new, clientConfig -> new DjManualContainerMonitor()); return new PreCreateImpl(ctor); } /** * Returns a service that can be used to build lifecycles of containers that are managed globally. * The creation and starting of a globally-managed container causes a JVM shutdown hook to be * registered to clean up the container on JVM termination. Cleanup means stopping a started * container and removing a created container. * @return a pre-create service */ public static PreCreate builderOfLifecyclesOfGloballyManagedContainers() { ContainerCreatorFactory ctor = new LocalContainerCreatorFactory(DjContainerCreator::new, clientConfig -> new DjShutdownHookContainerMonitor(() -> DockerClientBuilder.getInstance(clientConfig).build())); return new PreCreateImpl(ctor); } /** * Parent interface for services that can produce complete lifecycle instances. * @param

resource type */ public interface LifecycleFinisher

{ Lifecycle

finish(); Lifecycle finishWithContainer(); } /** * Interface of a service that allows definition of container parametry * when building a container lifecycle. */ public interface PreCreate { PreStartInitial creating(ContainerParametry containerParametry); } /** * Interface of a service that supports defining actions that can be defined * in the pre-start stage before any other actions have been defined. */ public interface PreStartInitial extends LifecycleFinisher {

PreStartSubsequent

pre(ContainerInitialPreStartAction

action); PreStartSubsequent runPre(ContainerPreStartRunnable runnable);

PostStart

post(ContainerInitialPostStartAction

action); PostStart runPost(ContainerPostStartRunnable runnable); } /** * Interface of a service that supports defining actions that can be defined * in the pre-start stage * @param

resource type produced if lifecycle were finished now */ public interface PreStartSubsequent

extends LifecycleFinisher

{ PreStartSubsequent pre(ContainerPreStartAction action); PreStartSubsequent

runPre(ContainerPreStartRunnable runnable); PostStart post(ContainerPostStartAction action); PostStart

runPost(ContainerPostStartRunnable runnable); } /** * Interface of a service that supports defining post-start actions. * @param

resource type produced if lifecycle were finished now */ public interface PostStart

extends LifecycleFinisher

{ // post PostStart post(ContainerPostStartAction action); PostStart

runPost(ContainerPostStartRunnable runnable); } private static class PreCreateImpl extends BuilderBase implements PreCreate { public PreCreateImpl(ContainerCreatorFactory ctor) { super(LifecycleStack.startingAt(new ContainerCreatorStage(ctor::instantiate))); } @Override public PreStartInitial creating(ContainerParametry containerParametry) { return new PreStartInitialImpl(stacker.andThen(new StartableContainerStage(containerParametry))); } } private static abstract class BuilderBase { protected final LifecycleStackElement stacker; protected BuilderBase(LifecycleStackElement stacker) { this.stacker = requireNonNull(stacker); } protected static LifecycleStage, PostStartResult> transitionPreToPost() { DecoupledLifecycleStage.Commissioner, PostStartResult> tCommissioner = new DecoupledLifecycleStage.Commissioner, PostStartResult>() { @Override public PostStartResult commission(PreStartResult requirement) throws Exception { StartedContainer startedContainer = requirement.container.start(); return new PostStartResult<>(startedContainer, requirement.content); } }; DecoupledLifecycleStage.Decommissioner> tDecommissioner = AutoCloseableDecommissioner.byTransform(postStartResult -> postStartResult.container); return new DecoupledLifecycleStage<>(tCommissioner, tDecommissioner); } protected static LifecycleStage> transitionStartableToPre() { return new LifecycleStage>() { @Override public PreStartResult commission(StartableContainer requirement) { return new PreStartResult<>(requirement, null); } @Override public void decommission() { } }; } public static LifecycleStage, U> transitionFinishing() { return new LifecycleStage, U>() { @Override public U commission(PostStartResult requirement) { return requirement.content; } @Override public void decommission() { } }; } } private static class PreStartInitialImpl extends BuilderBase implements PreStartInitial { public PreStartInitialImpl(LifecycleStackElement stackElement) { super(stackElement); } @Override public Lifecycle finish() { return finishWithContainer(); } @Override public PreStartSubsequent runPre(ContainerPreStartRunnable runnable) { return pre(ContainerRunnables.asInitialAction(runnable)); } @Override public PostStart runPost(ContainerPostStartRunnable runnable) { return post(ContainerRunnables.asInitialAction(runnable)); } @Override public

PreStartSubsequent

pre(ContainerInitialPreStartAction

action) { LifecycleStackElement> transition = stacker.andThen(transitionStartableToPre()); LifecycleStage, PreStartResult

> stageWrapper = new ContainerPreStartStage<>(action); LifecycleStackElement> pStacker = transition.andThen(stageWrapper); return new PreStartSubsequentImpl<>(pStacker); } @Override public

PostStart

post(ContainerInitialPostStartAction

action) { return new PostStartImpl<>(stacker .andThen(transitionStartableToPre()) .andThen(transitionPreToPost()) .andThen(new ContainerPostStartStage<>(action)) ); } @Override public Lifecycle finishWithContainer() { return stacker.andThen(new SimpleStartedContainerStage()).toSequence(); } } private static class PreStartSubsequentImpl extends BuilderBase> implements PreStartSubsequent { public PreStartSubsequentImpl(LifecycleStackElement> stacker) { super(stacker); } @Override public Lifecycle finish() { return stacker.andThen(transitionPreToPost()) .andThen(transitionFinishing()).toSequence(); } @Override public Lifecycle finishWithContainer() { return post((container, x) -> container).finish(); } @Override public PostStart runPost(ContainerPostStartRunnable runnable) { return post(ContainerRunnables.asPassThru(runnable)); } @Override public PreStartSubsequent runPre(ContainerPreStartRunnable runnable) { return pre(ContainerRunnables.asPassThru(runnable)); } @Override public PreStartSubsequent pre(ContainerPreStartAction action) { return new PreStartSubsequentImpl<>(stacker.andThen(new ContainerPreStartStage<>(action))); } @Override public PostStart post(ContainerPostStartAction action) { return new PostStartImpl<>(stacker .andThen(transitionPreToPost()) .andThen(new ContainerPostStartStage<>(action))); } } private static class PostStartImpl extends BuilderBase> implements PostStart { public PostStartImpl(LifecycleStackElement> b) { super(b); } @Override public Lifecycle finish() { return stacker.andThen(BuilderBase.transitionFinishing()).toSequence(); } @Override public PostStart post(ContainerPostStartAction action) { return new PostStartImpl<>(stacker.andThen(new ContainerPostStartStage<>(action))); } @Override public PostStart runPost(ContainerPostStartRunnable runnable) { return post(ContainerRunnables.asPassThru(runnable)); } @Override public Lifecycle finishWithContainer() { return post((container, requirement) -> container).finish(); } } @Override public String toString() { return String.format("Container%s", super.toString()); } }