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 via isChanged().

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:

  1. Annotation Detection: ConfigFactory.create() builds the proxy and PropertiesManager extracts the @HotReload annotation from the interface hierarchy.
  2. Resource Registration: HotReloadLogic receives the URI list from @Sources and instantiates WatchableFile instances for physical files or WatchableSystemProperties for system:properties.
  3. Path Selection:
    • SYNC: No background thread is created. PropertiesInvocationHandler.invoke() routes every call through syncReloadCheck() → HotReloadLogic.checkAndReload().
    • ASYNC: PropertiesManager schedules a periodic task via scheduler.scheduleAtFixedRate() that runs checkAndReload() independently.
  4. Change Detection: HotReloadLogic.needsReload() verifies the interval timeout and polls each WatchableResource.isChanged().
  5. Atomic Reload: When changes are detected, HotReloadLogic.checkAndReload() invokes PropertiesManager.reload(), which loads new properties, updates the internal state, and fires ReloadEvent notifications.

Summary

  • Synchronous mode (HotReloadType.SYNC) checks for file changes on every proxy method call via PropertiesInvocationHandler.invoke() and PropertiesManager.syncReloadCheck().
  • Asynchronous mode (HotReloadType.ASYNC) spawns a ScheduledExecutorService thread in PropertiesManager (lines 118‑125) to monitor files at a fixed interval.
  • Use the @HotReload annotation (defined in Config.java lines 205‑246) to configure the interval and mode.
  • Implement Reloadable and add listeners to receive ReloadEvent notifications 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →