How to Implement Hot Reload with Async and Sync Modes in OWNER API
Use the @HotReload annotation with type = HotReloadType.SYNC (default) to check for configuration changes on every method invocation, or type = HotReloadType.ASYNC to spawn a background thread that monitors files at a fixed interval.
The OWNER API is a Java configuration library that maps property files to type-safe interfaces. Implementing hot reload with async and sync modes in OWNER API allows applications to detect file changes automatically without restarting the JVM, choosing between on-demand checks or continuous background monitoring based on your latency requirements.
Understanding Hot Reload Modes
OWNER provides two distinct strategies for detecting external configuration changes. The mode determines when the library interrogates the underlying WatchableResource instances (files or system properties) and triggers a reload.
Synchronous Mode (Default)
In Synchronous mode (HotReloadType.SYNC), the library checks for modifications every time you invoke a method on the configuration proxy. According to the source code in PropertiesInvocationHandler.java (lines 57‑60), the invoke() method automatically delegates to propertiesManager.syncReloadCheck() before processing the property lookup.
This means every call to cfg.someProperty() triggers PropertiesManager.syncReloadCheck() (lines 357‑360), which asks HotReloadLogic.isSync() and HotReloadLogic.checkAndReload() to verify if the underlying files have changed.
Asynchronous Mode
In Asynchronous mode (HotReloadType.ASYNC), OWNER delegates monitoring to a background thread. During PropertiesManager construction (lines 118‑125), the code inspects hotReloadLogic.isAsync() and schedules a ScheduledExecutorService task:
scheduler.scheduleAtFixedRate(
() -> hotReloadLogic.checkAndReload(),
hotReload.value(),
hotReload.value(),
hotReload.unit());
This task runs independently of your application code, checking resources at the interval specified by the @HotReload annotation even when the configuration object sits idle.
Core Components
Four primary classes collaborate to implement hot reload capabilities.
@HotReload Annotation
The @HotReload annotation (defined in Config.java, lines 205‑246) declares the monitoring interval (value and unit) and the reload strategy (type). It attaches to your configuration interface and is read during proxy instantiation by PropertiesManager.
@Retention(RUNTIME)
@Target(TYPE)
public @interface HotReload {
int value() default 5;
TimeUnit unit() default TimeUnit.SECONDS;
HotReloadType type() default HotReloadType.SYNC;
}
HotReloadLogic
HotReloadLogic (lines 25‑130 in HotReloadLogic.java) encapsulates the watch list and reload decision logic. It maintains a collection of WatchableResource objects and exposes two critical methods:
checkAndReload(): Called either by the synchronous path or the async scheduler to trigger a reload when necessary.needsReload(): Compares the current timestamp against the last check time and interrogates each registered resource viaisChanged().
PropertiesManager
PropertiesManager owns the runtime state and the HotReloadLogic instance. When the @HotReload annotation is present, the constructor (lines 108‑129) initializes the logic and, for async mode, schedules the background task. It also provides syncReloadCheck() (lines 357‑360) as the entry point for synchronous verification.
PropertiesInvocationHandler
Every configuration proxy routes method calls through PropertiesInvocationHandler.invoke() (lines 57‑60). This handler guarantees that synchronous reload checks execute before property resolution, ensuring the configuration is fresh on every access when using SYNC mode.
Implementation Examples
Synchronous Hot Reload
Synchronous mode requires no explicit setup beyond the annotation. Every method call implicitly checks for file changes.
import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Config.HotReload;
import org.aeonbits.owner.Config.Sources;
@Sources("file:conf/app.properties")
@HotReload(2) // checks every 2 seconds, SYNC is implicit
interface AppConfig extends Config {
@DefaultValue("8080")
int port();
String dbUrl();
}
// Usage
AppConfig cfg = ConfigFactory.create(AppConfig.class);
System.out.println(cfg.port()); // triggers syncReloadCheck() first
Each invocation of cfg.port() forces PropertiesInvocationHandler to call propertiesManager.syncReloadCheck(), which delegates to HotReloadLogic.checkAndReload() if the configured interval has elapsed.
Asynchronous Hot Reload
Asynchronous mode decouples monitoring from application logic, ideal for high-throughput scenarios where you cannot tolerate the latency of file system checks on every property access.
import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Config.HotReload;
import org.aeonbits.owner.Config.HotReloadType;
import org.aeonbits.owner.Config.Sources;
import org.aeonbits.owner.Reloadable;
import java.util.concurrent.TimeUnit;
@Sources("file:conf/app.properties")
@HotReload(value = 5, unit = TimeUnit.SECONDS, type = HotReloadType.ASYNC)
interface AsyncAppConfig extends Config, Reloadable {
String dbUrl();
}
// Usage
AsyncAppConfig cfg = ConfigFactory.create(AsyncAppConfig.class);
cfg.addReloadListener(event ->
System.out.println("Configuration reloaded at " + event.getTime()));
// Background thread checks the file every 5 seconds automatically
The PropertiesManager constructor schedules a ScheduledExecutorService task (lines 118‑125) that invokes hotReloadLogic.checkAndReload() at the specified interval, regardless of whether the application calls configuration methods.
Listening to Reload Events
To react to configuration updates, implement the Reloadable interface and register listeners. The PropertiesManager.reload() method fires ReloadEvent instances to all registered listeners after the internal Properties map is replaced.
cfg.addReloadListener(event -> {
System.out.println("Reload detected: " + event.getTime());
// Re-initialize dependent services here
});
How It Works Under the Hood
The reload lifecycle follows a deterministic sequence implemented in matteobaccan/owner:
- Annotation Detection:
ConfigFactory.create()builds the proxy andPropertiesManagerextracts the@HotReloadannotation from the interface hierarchy. - Resource Registration:
HotReloadLogicreceives the URI list from@Sourcesand instantiatesWatchableFileinstances for physical files orWatchableSystemPropertiesforsystem:properties. - Path Selection:
- SYNC: No background thread is created.
PropertiesInvocationHandler.invoke()routes every call throughsyncReloadCheck()→HotReloadLogic.checkAndReload(). - ASYNC:
PropertiesManagerschedules a periodic task viascheduler.scheduleAtFixedRate()that runscheckAndReload()independently.
- SYNC: No background thread is created.
- Change Detection:
HotReloadLogic.needsReload()verifies the interval timeout and polls eachWatchableResource.isChanged(). - Atomic Reload: When changes are detected,
HotReloadLogic.checkAndReload()invokesPropertiesManager.reload(), which loads new properties, updates the internal state, and firesReloadEventnotifications.
Summary
- Synchronous mode (
HotReloadType.SYNC) checks for file changes on every proxy method call viaPropertiesInvocationHandler.invoke()andPropertiesManager.syncReloadCheck(). - Asynchronous mode (
HotReloadType.ASYNC) spawns aScheduledExecutorServicethread inPropertiesManager(lines 118‑125) to monitor files at a fixed interval. - Use the
@HotReloadannotation (defined inConfig.javalines 205‑246) to configure the interval and mode. - Implement
Reloadableand add listeners to receiveReloadEventnotifications when the configuration updates. - The core logic resides in
HotReloadLogic.java(lines 25‑130), which manages the watch list and reload decisions.
Frequently Asked Questions
How do I enable hot reload in OWNER API without blocking my application threads?
Use asynchronous mode by setting type = HotReloadType.ASYNC in the @HotReload annotation. According to the source code in PropertiesManager.java (lines 118‑125), this schedules a background ScheduledExecutorService task that checks files periodically without interfering with your main execution path.
What is the default hot reload behavior if I only specify the interval?
The default type is Synchronous (HotReloadType.SYNC). If you omit the type attribute, OWNER checks for configuration changes every time you access a property method, as implemented in PropertiesInvocationHandler.invoke() (lines 57‑60).
Can I receive notifications when OWNER reloads the configuration?
Yes. Have your configuration interface extend Reloadable and call addReloadListener(). The listener receives a ReloadEvent after PropertiesManager.reload() completes and the internal properties map is updated, allowing you to refresh dependent application state.
How does OWNER determine if a property file has changed?
HotReloadLogic.needsReload() (defined in HotReloadLogic.java lines 25‑130) tracks the last check timestamp and interrogates each registered WatchableResource (files or system properties) via isChanged(). Only if the interval has elapsed and a resource reports modification does the library trigger PropertiesManager.reload().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →