-
Notifications
You must be signed in to change notification settings - Fork 0
Lifecycle and Reload
Config.open is the only file constructor; everything else is a method on the returned handle.
Config cfg = Config.open(path, codec);
LoadStatus status = cfg.lastLoadStatus();You can also let open pick the codec from the file's extension (via CodecRegistry.defaults()), taking a
String, File or Path:
Config cfg = Config.open("config.yml"); // .yml -> YamlCodec
Config cfg = Config.open(Paths.get("a.toml")); // .toml -> TomlCodecFail-fast — it never guesses. A missing or unregistered extension throws a
CodecExceptioninstead of falling back to a default format. Pass an explicit codec —Config.open(path, codec)— to override.
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 |
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 executorWrites go through a fair per-Config lock and an atomic move (.tmp → target), so a reader never sees a torn
file.
How durably each save() must land is fixed at open time by a third open argument:
Config cfg = Config.open(path, codec, Backend.Durability.FSYNC);Backend.Durability |
Behavior |
|---|---|
OS_CACHE (default) |
returns once the atomic rename is visible; the bytes may still live only in the OS page cache |
FSYNC |
forces the bytes and the rename to the storage device before returning — slower, but survives an OS/power crash |
An
OS_CACHEwrite never corrupts the previous content (the rename is atomic); a crash can only lose the latest write.FSYNCtrades throughput for surviving that crash.
In-memory save principle. Outside a watcher, a
Configlives 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 nextsave()unless youreload()to pick it up. Editing a running app's config without a reload is an anti-pattern.
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?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)By default the watcher diffs a cheap (mtime, size) fingerprint. A second boolean argument also hashes the
file content each poll, catching a same-size edit that lands within one coarse mtime tick:
cfg.withAutoReload(Duration.ofSeconds(2), true); // also detect in-place editsThat costs a full read per poll, so it is opt-in. Leave it
false(the one-arg form) unless edits that keep the byte count identical must be picked up.
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
EveryConfig · br.com.finalcraft:EveryConfig · One config API, every format, comments included · made by Petrus Pradella
Getting Started
Core Concepts
Typed Binding
Operations
Reference
Contributing