Pronto Configuration

Pronto contains classes for automatically satisfying interfaces with configuration information. These classes reside in the com.tomgibara.pronto.config package and sub-packages. The highlights are:

Basic Usage

Access to the configuration functionality of Pronto is available from a singleton factory. This creates Config objects from a ConfigSource (that provides the configuration properties) and an optional class loader (that is used to create object instances). The Config objects created by this factory can be further customized by changing their operational policy before use. Thus an example of creating a Config object is:

import com.tomgibara.pronto.config.ConfigFactory; import com.tomgibara.pronto.config.Config; import com.tomgibara.pronto.config.source.FileConfigSource; Config config = ConfigFactory.getInstance() .newConfig(new FileConfigSource(propsFile), null);

This will create a Config object that draws properties from the file specified by propsFile. By default, the the FileConfigSource will attempt to parse the properties in the format specified by the documentation for java.util.Properties. As with all configuration sources, the FileConfigSource observes when the properties have been changed and provide the config object with an opportunity to make the newest properties available to client code.

Now suppose that we have an interface (defined below) which serves as configuration for our application. We want to read this information in from some configuration source (a file in our case) without needing to write a pojo implementation and code to populate it.

public interface UserSettings { enum Role {admin, manager, developer, user}; /** The user's name in the system. */ String getName(); /** A hash code of the user's password */ long getPasswordHash(); /** Whether the user credentials are currently valid.*/ boolean isValid(); /** The roles to which the user belongs. */ Role[] getRoles(); /** Settings for web access. */ Map getSettings(); /** A URL to the user's home page. */ URL getURL(); /** The user's account number */ String getAccountCode(); }

Then we can very simply satisfy this interface with properties from propsFile with the single line of code below.

UserSettings userSettings = config.adaptSettings(null, false, UserSettings.class)

An entirely imaginary user properties file for this interface would be:

name=tom password-hash=290563099 roles=admin,developer valid=true url=http://www.tomgibara.com settings=theme: green; login=auto

This sample file demonstrates some of the details of how properties are mapped to Java methods. Note the following:

Because the configuration file is polled for updates, changes to the file will be reflected in method calls to the interface at some time after the file has been changed. For example, if we changed the valid property to false, userSettings.isValid() would, after a delay, return false. The delay in the liveness is controlled by the ConfigPolicy associated with the Config object used to create the interface implementation. The default is currently 3 seconds – the value is a trade-off between the speed of property retrieval (which depends on how often the file is checked for changes) and the speed at which changes are reflected in the values returned from interfaces. A number of other operational parameters can be changed via the configuration policy object.

Domains

Properties for multiple interfaces can be combined into a single property source by identifying arranging them into domains. Domains are dot-delimited 'namespaces' in the standard Java style.

Extending the example above with a second interface…

public interface ServerSettings { /** The public domain from which the server is accessible. */ String getPublicDomain(); /** The public port from which the server is accessible. */ Integer getPublicPort(); }

…we can populate an instance of each interface from the following properties file…

#server settings server.public-domain=demo.tomgibara.com server.public-port=80 #admin user settings admin.name=tom admin.password-hash=290563099 admin.roles=admin,developer admin.valid=true admin.url=http://www.tomgibara.com admin.settings=theme: green; login=auto

…by simply specifying the domain from which each implementation will draw its properties:

UserSettings userSettings = config.adaptSettings("admin", false, UserSettings.class); ServerSettings serverSettings = config.adaptSettings("server", false, ServerSettings.class

Any number of different sets of configuration information can be supported in this way and different interfaces can share the same (or overlapping) sets of properties.

Inheritence

It is frequently the case that, where the same (or similar) interfaces are implemented by properties from different domains, it is sensible that they share some properties. This can be achieved by inheriting properties between domains. As the following example demonstrates.

#server settings server.public-domain=demo.tomgibara.com server.public-port=80 #default user settings users.settings=theme: green; login=auto users.roles=user #administrative user settings users.admin.name=tom users.admin.password-hash=290563099 users.admin.roles=admin,developer users.admin.valid=true users.admin.url=http://www.tomgibara.com #user settings for anonymous user users.anon.name=anonymous UserSettings adminUserSettings = config.adaptSettings("users.admin", true, UserSettings.class); UserSettings anonUserSettings = config.adaptSettings("users.anon", true, UserSettings.class); anonUserSettings.getSettings(); //returns the map {green = theme, auto = login}. adminUserSettings.getSettings(); //returns the same map as above - both inherit it. anonUserSettings.getRoles(); //returns array {user} - inheritted. adminUserSettings.getRoles(); //returns array {admin,developer} - not inheritted. anonUserSettings.getName(); returns string "anonymous". adminUserSettings.getName(); returns string "tom", neither inherit.

Sources

Properties can be drawn from a number of different sources. Standard sources may be found in the com.tomgibara.pronto.config.source package. New sources can be created by implementing the simple com.tomgibara.pronto.config.ConfigSource interface. Sources which need to parse properties from resources (such as files and urls) are recommended to use a pluggable implementation of com.tomgibara.pronto.config.source.PropertiesReader for greater flexibility. Implementations of that interface can be supplied to several existing source to control the parsing of properties.