Skip to content

Captures

Not everything that matters happens inside your application. An exchange rate moves, a partner posts a webhook, a supplier’s API changes a price. Those are facts too — and the usual answer is a scheduled job somewhere that polls, maps, and appends, which nobody owns and nobody notices when it stops.

A capture is that job, declared rather than written, and run by the kernel.

Implement ICapture and discovery saves it with the kernel on connect and starts it, so the source is live as soon as your application is:

import io.cratis.chronicle.captures.ICapture
class ExchangeRates : ICapture {
override val id = "exchange-rates"
override val declaration = """
capture ExchangeRates
from api "https://api.example.com/rates" every 5 minutes
append RateObserved
set currency to base
set rate to value
"""
}

The body is a Capture Declaration Language document. The language belongs to the kernel, which is what parses and runs it — see the Chronicle documentation for its full shape.

Set startOnRegistration to false to have the capture saved but held — an environment where the source is not reachable, or one where starting it is an operator’s decision.

A declaration the kernel cannot parse is reported to standard error naming the capture, the line and the column. One bad capture does not take the rest of your application’s registration down with it.

When the declaration is not known at build time

Section titled “When the declaration is not known at build time”

store.captures is the whole surface, for declarations that come from somewhere else — an editor, configuration, an operator:

// Check as it is typed, without saving anything
val problems = store.captures.validate(declaration)
// Save, then start
when (val result = store.captures.save("exchange-rates", declaration)) {
is CaptureDeclarationResult.Accepted -> store.captures.start(result.capture.id)
is CaptureDeclarationResult.Rejected -> result.messages.forEach(::println)
}
MemberDoes
getAll()Every capture the store holds, running or not
observeAll()The same, re-emitted on every change
save(id, declaration)Saves under an id, replacing what was there
validate(declaration)Checks a declaration without saving anything
start(id)Starts it appending
stop(id)Stops it. It stays saved
delete(id)Removes it entirely

A declaration is a piece of text, so the first thing that can go wrong is that the kernel cannot make sense of it. That is an ordinary outcome of writing in a language rather than an exception, so save returns Accepted or Rejected and every message names a line and column.

Messages do not by themselves mean rejection — a declaration can be accepted with something worth telling you. isSuccess, or the shape of the result, is what says whether it took.

Events a capture already appended are facts. Stopping or deleting the capture changes nothing about them — they stay exactly where they are, and everything projected from them stays as it was.

From Java, use CapturesServiceJavaBridge.