Skip to content

Lifecycle and Reload

Petrus Pradella edited this page Jun 27, 2026 · 4 revisions

Lifecycle, Reload & Watching

Config.open is the only file constructor; everything else is a method on the returned handle.

Open

Config cfg = Config.open(path, codec);
LoadStatus status = cfg.lastLoadStatus();

Config.open never throws on a bad file — a corrupt config must not block startup:

Situation Result lastLoadStatus()
File absent empty tree; first save() creates it ABSENT
Zero-byte file empty tree EMPTY
Parse failure file copied to .bak, empty tree (file not overwritten) PARSE_FAILED_BACKED_UP
Clean parse tree loaded OK

Save

cfg.save();           // encode (with comments + key order if the codec carries them) + atomic write
cfg.saveIfDirty();    // no I/O when nothing changed since the last load/save
cfg.saveAsync();      // CompletableFuture<Void> on a shared daemon executor

Writes go through a fair per-Config lock and an atomic move (.tmp → target), so a reader never sees a torn file.

In-memory save principle. Outside a watcher, a Config lives entirely in memory: save() never reads the file first — it dumps the in-memory tree. If something edits the file on disk while your app holds the config, that edit is overwritten on the next save() unless you reload() to pick it up. Editing a running app's config without a reload is an anti-pattern.

Reload

cfg.reload();
Situation Result lastLoadStatus()
Clean parse replaces the live tree OK
File absent keeps the live tree ABSENT
Parse failure keeps the live tree, no backup, no overwrite PARSE_FAILED_KEPT
cfg.isDivergedFromDisk();   // true after a PARSE_FAILED_KEPT reload (memory differs from a broken file)
cfg.getLastModified();      // last-write timestamp
cfg.hasBeenModified();      // changed on disk since the last load/save?

Watching (auto-reload)

Poll the file on a daemon thread and refresh the tree when it changes on disk:

cfg.onReload(() -> log.info("config reloaded"))
   .withAutoReload(Duration.ofSeconds(2));   // positive duration; closes any prior watcher

// ... later
cfg.stopAutoReload();   // halt polling (a self-save does NOT trigger a self-reload)

Close

cfg.close();   // idempotent; stops the watcher. Config is AutoCloseable.
try (Config cfg = Config.open(path, codec)) {
    cfg.setValue("x", 1);
    cfg.save();
}   // auto-closed

→ See also The Dynamic API · Codecs & Formats

Clone this wiki locally