Class Parsley

java.lang.Object
io.github.tobyjamesclements.parsley.api.Parsley
All Implemented Interfaces:
AutoCloseable

public final class Parsley extends Object implements AutoCloseable
A running set of processes, each executing under causal delivery order.

Every ProcessDefinition passed to start(ParsleyConfig, ProcessDefinition...) runs as its own Kafka Streams application under exactly_once_v2 and read_committed. A process that cannot uphold the delivery guarantee stops rather than weakening it, and stays stopped until an operator intervenes.

See Also:
  • Method Details

    • start

      public static Parsley start(ParsleyConfig config, ProcessDefinition... processes)
      Validates every definition, resolves every received and sent topic, establishes each process's initial read positions, then starts every process and returns.

      Every declared topic must already exist; nothing is created. The start is all-or-nothing: a refusal for any process leaves nothing running and is thrown. The call returns once each process's Kafka Streams application has been started, which is the beginning of its first rebalance, not the end of it — poll status() or wait with awaitStopped(). A refusal raised inside task initialisation on the host's threads surfaces through status().

      Parameters:
      config - broker connection, application identity and metadata budget
      processes - the processes to run, at least one
      Returns:
      a handle owning the running processes
      Throws:
      ParsleyFailClosedException - if a process cannot start without breaching the guarantee, for example when messages remain held on a channel the definition no longer receives
      IllegalArgumentException - if config, processes or an element is null, or the definitions conflict or name no process
      IllegalStateException - if the cluster could not be queried, or a declared topic does not exist
    • healthy

      public boolean healthy()
      Reports whether every process is still running.
      Returns:
      false once any process has stopped, deliberately or otherwise
      See Also:
    • status

      public Map<String, ProcessStatus> status()
      Reports the current state of each process, keyed by process name.

      This is the diagnosis surface when healthy() turns false: ProcessStatus.refusalReason() distinguishes a deliberate stop from a failure.

      Returns:
      a snapshot of every process, never empty
    • awaitStopped

      public void awaitStopped() throws InterruptedException
      Waits until a process stops, or this handle is closed.

      start(ParsleyConfig, ProcessDefinition...) returns as soon as every process has been started, so an application whose work is its processes has nothing else to do but wait here. The wait ends when any process stops — deliberately, to preserve the guarantee, or otherwise — which is the moment to read status() and act, or when another thread calls close(). The wait ends as soon as the stop is known; the host's own shutdown may still be completing, so status() can report the process as running for a moment longer before it settles on the stopped state and its reason.

      Throws:
      InterruptedException - if the waiting thread is interrupted
    • awaitStopped

      public boolean awaitStopped(Duration timeout) throws InterruptedException
      Waits, for at most timeout, until a process stops or this handle is closed.
      Parameters:
      timeout - how long to wait
      Returns:
      true if a process stopped or the handle was closed within the timeout, false if every process was still running when it elapsed
      Throws:
      InterruptedException - if the waiting thread is interrupted
      IllegalArgumentException - if timeout is null or negative
    • close

      public void close()
      Stops every process and releases its resources.

      Each resource is released independently, so a failure to release one cannot strand the others. Closing is bounded in time.

      Specified by:
      close in interface AutoCloseable