How to Listen to Reload Events Using the ReloadListener Interface in Owner

Implement the org.aeonbits.owner.event.ReloadListener interface and register it via addReloadListener() on your configuration instance to receive callbacks after each successful reload.

The Owner configuration library (matteobaccan/owner) provides a robust event system that notifies your application when configuration properties change. By leveraging the ReloadListener interface, you can execute custom logic—such as refreshing caches, logging changes, or triggering notifications—immediately after a reload completes.

Understanding the ReloadListener Interface

The ReloadListener interface, defined in owner/src/main/java/org/aeonbits/owner/event/ReloadListener.java, declares a single callback method:

void reloadPerformed(ReloadEvent event);

Owner invokes this method after the properties have been successfully reloaded, ensuring that any logic inside reloadPerformed can safely read updated values from the configuration object. The ReloadEvent parameter, implemented in owner/src/main/java/org/aeonbits/owner/event/ReloadEvent.java, provides metadata about the reload operation, including the source configuration instance and the timestamp of the event.

Registering a ReloadListener

To begin listening for reload events, create an instance of ReloadListener and register it using the addReloadListener() method available on any configuration object that extends Reloadable.

import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Config;
import org.aeonbits.owner.Reloadable;
import org.aeonbits.owner.event.ReloadListener;
import org.aeonbits.owner.event.ReloadEvent;

@Sources("file:app.properties")
public interface AppConfig extends Config, Reloadable {
    @DefaultValue("localhost")
    String databaseHost();
    
    @DefaultValue("5432")
    Integer databasePort();
}

public class ConfigMonitor {
    public static void main(String[] args) {
        AppConfig config = ConfigFactory.create(AppConfig.class);
        
        // Register anonymous listener
        config.addReloadListener(new ReloadListener() {
            @Override
            public void reloadPerformed(ReloadEvent event) {
                System.out.println("Configuration reloaded at " + event.getTimestamp());
                System.out.println("New database host: " + config.databaseHost());
            }
        });
        
        // Keep application running to receive reload events
    }
}

Internally, ConfigFactory.create() builds a proxy that maintains a thread-safe list of listeners (see PropertiesManager.addReloadListener in the source). When Config.reload() or a hot-reload detection triggers a reload, Owner iterates over that list and invokes listener.reloadPerformed(event).

Accessing ReloadEvent Details

The ReloadEvent object passed to your listener contains two critical pieces of information:

  • event.getConfig(): Returns the configuration instance that was reloaded, allowing you to access updated properties.
  • event.getTimestamp(): Returns the Unix timestamp (milliseconds) when the reload occurred.
config.addReloadListener(event -> {
    Reloadable sourceConfig = event.getConfig();
    long reloadTime = event.getTimestamp();
    
    // Log the change for audit purposes
    logger.info("Config {} reloaded at {}", sourceConfig, new Date(reloadTime));
});

Removing a ReloadListener

When a listener is no longer needed—such as during application shutdown or when a component is destroyed—you should unregister it to prevent memory leaks. Use the removeReloadListener() method:

ReloadListener myListener = event -> { /* ... */ };
config.addReloadListener(myListener);

// Later, when cleaning up:
config.removeReloadListener(myListener);

Practical Example: Hot-Reload with Listener

For file-based configurations using @Config.HotReload, combining automatic reloading with a listener creates a powerful dynamic configuration system. The following example, adapted from owner-examples/owner-examples-hotreload/src/main/java/org/aeonbits/owner/examples/AutoReloadExample.java, demonstrates this pattern:

import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Reloadable;
import org.aeonbits.owner.event.ReloadListener;
import org.aeonbits.owner.event.ReloadEvent;

@Config.HotReload(5)  // Check for changes every 5 seconds
@Config.Sources("file:/etc/myapp/config.properties")
public interface DynamicConfig extends Config, Reloadable {
    @DefaultValue("INFO")
    String logLevel();
    
    @DefaultValue("300")
    Integer connectionTimeout();
}

public class HotReloadMonitor {
    public static void main(String[] args) throws InterruptedException {
        DynamicConfig config = ConfigFactory.create(DynamicConfig.class);
        
        // Register listener to react to file changes
        config.addReloadListener(new ReloadListener() {
            @Override
            public void reloadPerformed(ReloadEvent event) {
                System.out.printf("[%d] Config reloaded! New log level: %s, Timeout: %d%n",
                    event.getTimestamp(),
                    config.logLevel(),
                    config.connectionTimeout());
                
                // Trigger cache invalidation or service reconfiguration here
            }
        });
        
        System.out.println("Monitoring config changes. Edit the file to trigger reload...");
        while (true) {
            Thread.sleep(1000);
        }
    }
}

In this implementation, Owner monitors the specified file every 5 seconds. When a modification is detected, it reloads the properties and immediately notifies all registered ReloadListener instances, allowing the application to adapt to new settings without restart.

Summary

  • Implement org.aeonbits.owner.event.ReloadListener to receive callbacks when configuration reloads complete.
  • Register listeners using addReloadListener() on any configuration instance that implements Reloadable.
  • Access reload metadata through ReloadEvent, including getConfig() and getTimestamp().
  • Remove listeners with removeReloadListener() to prevent memory leaks when components shut down.
  • Listeners work with both programmatic reloads (Config.reload()) and automatic hot-reloads (@Config.HotReload).

Frequently Asked Questions

How do I register multiple ReloadListeners on the same configuration instance?

You can call addReloadListener() multiple times on the same configuration object. Owner maintains a thread-safe list of listeners and invokes each one sequentially after a reload completes. There is no limit to the number of listeners you can register.

Can I remove a ReloadListener after registering it?

Yes. Store a reference to your ReloadListener implementation and pass it to removeReloadListener() when you no longer need notifications. This is important for preventing memory leaks in long-running applications where configuration objects outlive the components that registered the listeners.

What information does the ReloadEvent object contain?

The ReloadEvent object provides two key pieces of metadata: getConfig(), which returns the configuration instance that was reloaded, and getTimestamp(), which returns the Unix timestamp (in milliseconds) when the reload occurred. You can use these to log changes or trigger conditional logic based on when the reload happened.

Does the ReloadListener work with both manual and automatic reloads?

Yes. The ReloadListener interface is invoked whenever a reload completes, regardless of whether it was triggered manually via Config.reload() or automatically through the @Config.HotReload annotation. This ensures consistent behavior across programmatic and file-based configuration updates.

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 →