Configuring AndHow

How to configure AndHow before AndHow configures you(r application)

Why would an application need to configure AndHow? There are several possible reasons:

  • To pass in command-line arguments

  • To use a custom name for the classpath andhow.properties file

  • To use a properties file on the filesystem (non-classpath), which requires configuring a file path

  • To add, remove or reorder the loaders that load Property values, or configure them

  • To set fixed values for some Properties (mostly done for testing)

AndHow Configuration can only happen before initialization

AndHow ensures that its state and Property values are immutable once initialized, so any attempt to modify AndHow's configuration after initialization results in a RuntimeException. How can you be sure your code to configure AndHow happens before initialization?

The AndHow.findConfig() method

AndHow.findConfig() is the only way to access the configuration AndHow uses for itself. Until initialization, AndHow holds a reference to an AndHowConfiguration instance that can be retrieved via AndHow.findConfig(). All configuration happens via the AndHowConfiguration instance, and after initialization the reference is gone.

The first time findConfig() is called, AndHow scans the classpath for a class implementing AndHowInit (or AndHowTestInit). If an implementation exists, it's getConfiguration() method is called to provide the AndHowConfiguration instance. If no AndHowInit class exists, AndHow starts with a default StdConfig instance. Later calls to findConfig() return the same instance that was found or created in the first call.

As a first step of initialization, AndHow calls findConfig() on itself so it initializes with the configuration that has been built up, created by default, or returned from the AndHowInit class.

Configuring AndHow at a well defined entry point

The simplest way to ensure AndHow configuration happens before Properties are accessed is to have a well defined Entry Point. Common well defined entry points are:

  • The main method of application startup class

  • The ServletContextListener.contextInitialized method in a Servlet application

  • The handle() method of a lambda function

  • Any well documented init method that is invoked prior to all other application code

Generally a deployed application of GUI will have a well defined entry point, while a reusable utility library will not.

Configuration via the AndHowInit interface

AndHow will discover and use a class implementing AndHowInit to configure itself, if it is present, so implementing that interface in your deployable application is the best way to configure AndHow. It ensures your configuration is always used and won't attempt to configure AndHow 'late', after it is already initialized. Example:

public class InsertLoader implements AndHowInit {

	@Override
	public AndHowConfiguration getConfiguration() {
		PropFileOnClasspathLoader pfl = new PropFileOnClasspathLoader();
		pfl.setFilePath("/my.properties");
		
		return AndHow.findConfig()
			.insertLoaderBefore(StdJndiLoader.class, pfl);
	}
}

This example implements the AndHowInit interface to add a new Loader to load "my.properties" from the classpath before the JNDI loader. No other code is required - AndHow will find this class during its initialization process and use the new loader.

Best Practice: Use an AndHowInit class to configure AndHow and include it with your deployable application, not a library or dependency

Only a single AndHowInit class is allowed on the classpath, otherwise configuration would be ambiguous. Thus, don't bundle an AndHowInit class with a distributable library - it is only intended to be used with deployable apps.

If AndHow.findConfig() calls AndHowInit.getConfiguration(), doesn't it cause a loop when findConfig is used inside that method??

Well, sure. Yes it would. To keep the API simple and make findConfig the universal way to access configuration, AndHow detects the re-entrant call to findConfig and simply returns a new StdConfig.

Setting command line arguments

This is a common need and appears in so many examples that its easy to miss that this is actually an example of configurating AndHow.

AndHow can load values from most configuration sources automatically, however, it has no way to intercept the command line arguments passed to the main method - the application has to help. Here is modified version of HelloWorld, updated to load from the command line:

public class HelloWorld {
    private static final StrProp NAME = StrProp.builder().build();
    
    public static void main(String[] args) {
        AndHow.findConfig().setCmdLineArgs(args); // <-- Pass cmd-line args to AndHow
        
        System.out.println("Hello, " + NAME.getValue());
    }
}

In the above example, we know that the only entry point is the main() method, so it is safe to configure AndHow at the top of that method.

findConfig() is called and all the steps outlined above take place: If there is an AndHowInit instance on the classpath, it will be used to supply an AndHowConfiguration instance. If not, a default instance is created. Then, we supply the command line arguments to AndHow's configuration. When its time to initialize (which happens when we access a value on line 7), AndHow provides the command line arguments to a Loader instance that knows how to parse key-value pairs from command-line.

Any attempt to call findConfig() after line 7 would result in a RuntimeException because AndHow has already initialized.

Other examples of Configuring AndHow

There are two other common situations where you need to configuration AndHow:

Modifying the load order, customizing loaders, or adding new loaders is discussed in the Changing the Load Order section.

Setting fixed values is mostly done during testing.

Last updated