Class KarateLifecycle
Embedding applications get two options, and should pick exactly one:
- call
shutdownAll(Duration)at the point their own shutdown sequence says Karate should be gone (preferred — shutdown ordering stays under the application's control, and the returned summary is inspectable while logging still works); - or call
installShutdownHook()once during startup and let the JVM trigger the drain — fine for CLIs and tests, less so inside a container where the shutdown hook competes with the framework's own.
Whichever is chosen, daemon threads (see ThreadUtils) remain the backstop:
a component that is never stopped will not keep the JVM alive, it just skips the
polite parts — close frames unsent, callbacks dropped, peers seeing abrupt resets.
Phases
The register movesKarateLifecycle.Phase.RUNNING → KarateLifecycle.Phase.SHUTTING_DOWN →
KarateLifecycle.Phase.STOPPED and stays there; reset() is the only way back, for
tests and for applications that deliberately restart Karate in-process. Once shutdown
has begun, register(Stoppable) no longer adds to the register — the late
arrival is stopped on the spot (logged at warn) rather than being quietly leaked past
the drain.
Deadline
Thetimeout handed to shutdownAll(Duration) is a total deadline
for the whole sequence and it is enforced, not merely hoped for: every stop()
runs on an interruptible worker and is abandoned when its slice of the remaining time
runs out. The slice is the remaining time divided by the number of components still to
go, so a single component that hangs is interrupted after its own share and the ones
behind it still get their turn — while components that stop promptly hand their unused
time to the rest.
Shutdown runs in reverse registration order, on the theory that later arrivals tend to depend on earlier ones (a client connected to a server started before it). Every stop is individually guarded, so one component that throws or hangs cannot strand the rest; each outcome is logged and reported in the returned summary.
All methods are thread-safe. Registration is idempotent and identity-based, so a
component that registers twice is listed once, and one whose equals collides
with a sibling's is still tracked separately. A second shutdownAll(Duration)
arriving while one is in flight does not start a second drain — it waits for the one
already running and returns its summary.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumHow stopping one component turned out.static enumWhere the register is in its one-way trip from serving to stopped.static final recordWhat happened to one component duringshutdownAll(Duration). -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final DurationUsed byinstallShutdownHook()andshutdownAll()— modest on purpose, a JVM shutdown hook that overstays gets killed by the OS anyway (and by container stop timeouts). -
Method Summary
Modifier and TypeMethodDescriptionstatic voidRegister a JVM shutdown hook that runsshutdownAll(Duration)withDEFAULT_TIMEOUTand logs the summary.static KarateLifecycle.Phasephase()Where the register currently is — seeKarateLifecycle.Phase.static voidAdd a component to the register — call once it is actually running (bound, connected, spawned), not while it is still being constructed.static voidreset()Return the register toKarateLifecycle.Phase.RUNNINGso it accepts registrations again.running()Everything registered right now, in registration order.static List<KarateLifecycle.StopResult> Stop everything registered, in reverse registration order, bounded byDEFAULT_TIMEOUT.static List<KarateLifecycle.StopResult> shutdownAll(Duration timeout) Stop everything registered, in reverse registration order, and report what happened.static voidunregister(Stoppable stoppable) Remove a component from the register — call from the component's own stop path, so that something which stopped by itself (peer closed the socket, process exited) drops off too.static Stoppablewrap(String name, String kind, ExecutorService executor) Adapt anExecutorServicetoStoppable, for a component whose only long-lived resource is its pool.
-
Field Details
-
DEFAULT_TIMEOUT
Used byinstallShutdownHook()andshutdownAll()— modest on purpose, a JVM shutdown hook that overstays gets killed by the OS anyway (and by container stop timeouts).
-
-
Method Details
-
register
Add a component to the register — call once it is actually running (bound, connected, spawned), not while it is still being constructed. Registering the same instance again is a no-op, and null is ignored.Once shutdown has begun this does not register anything: the component arrived too late to be drained in order, so it is stopped immediately (logged at warn), bounded by whatever is left of the in-progress deadline — or by
DEFAULT_TIMEOUTwhen shutdown has already finished. -
unregister
Remove a component from the register — call from the component's own stop path, so that something which stopped by itself (peer closed the socket, process exited) drops off too. Removing something not registered is a no-op, and null is ignored. -
running
Everything registered right now, in registration order. A snapshot — the returned list is immutable and does not track later registrations, so it is safe to iterate while components start and stop. -
phase
Where the register currently is — seeKarateLifecycle.Phase. -
reset
public static void reset()Return the register toKarateLifecycle.Phase.RUNNINGso it accepts registrations again. Meant for tests, and for an application that deliberately restarts Karate inside a live JVM; a normal shutdown never needs it. Anything left over from the previous life is dropped, not stopped.- Throws:
IllegalStateException- if a shutdown is in flight
-
shutdownAll
Stop everything registered, in reverse registration order, bounded byDEFAULT_TIMEOUT. -
shutdownAll
Stop everything registered, in reverse registration order, and report what happened.timeoutis the total deadline for the sequence. Each component is stopped on an interruptible worker, given the remaining time divided by the number of components left to stop; one that overruns is interrupted, recorded asKarateLifecycle.Outcome.TIMED_OUTand left to the daemon-thread backstop while the sequence moves on. One that throws is recorded asKarateLifecycle.Outcome.FAILEDand logged at warn. The register is emptied and the phase becomesKarateLifecycle.Phase.STOPPED, so anything registering afterwards is stopped on arrival.Concurrency: if a shutdown is already in flight this waits for it and returns that summary rather than starting a second one. The exception is a call made from inside a
stop()(or otherwise from the thread driving the shutdown) — waiting there would be waiting on itself, so it returns the results collected so far and the outer sequence carries on.- Parameters:
timeout- total deadline; null or non-positive meansDEFAULT_TIMEOUT- Returns:
- one
KarateLifecycle.StopResultper component, in the order they were stopped
-
installShutdownHook
public static void installShutdownHook()Register a JVM shutdown hook that runsshutdownAll(Duration)withDEFAULT_TIMEOUTand logs the summary. Opt-in and idempotent — repeat calls install nothing further. Applications that already own a shutdown sequence should callshutdownAll(Duration)from it instead, rather than racing their own hook against this one. -
wrap
Adapt anExecutorServicetoStoppable, for a component whose only long-lived resource is its pool. The returnedstop()callsshutdownNow()and waits (up to a few seconds) for the pool to terminate; it is idempotent, and the executor is exposed viaStoppable.lifecycleExecutor(). Register it like anything else:KarateLifecycle.register(KarateLifecycle.wrap("my-pool", "executor", pool)).
-