The MicroBean Configuration project provides yet another framework for acquiring configuration values from within a Java SE application.
There are many configuration frameworks for Java SE. All focus on making it easy for application developers to ask for configuration values without having to know what systems provide those values.
MicroBean Configuration takes a slightly different approach. The application’s full configuration is considered to be located by its configuration coordinates within configuration space. Within that space reside its actual configuration properties. This terminology is important for understanding how MicroBean Configuration works.
Configuration space is defined as the unbounded universe of configuration properties and their values, for all applications, regardless of which system or systems might define or house them. Dimensions within this space might include locale, region, data center identifier, tenant identifier—these are only examples. Of note is that these are dimensions, not nodes within a hierarchy.
Configuration coordinates are those which semantically locate an application in configuration space. So an application run in the US locale, in the test environment, located in the Western region of a cloud provider, in the Nevada data center and run on behalf of tenant number 34 (to use the examples from the Configuration Space section above) is located in configuration space with, perhaps, the following logical configuration coordinates:
locale=en_US environment=test region=west dataCenter=Nevada tenantId=34
While these coordinates look like configuration values themselves, they are configuration values for locating the application in configuration space, not (normally) the values that the application will be looking up during the course of its execution.
Configuration properties are the names for which (non-hard-coded) values are sought by an application. portNumber, timeoutInSeconds, databaseUrl and userEmail are arbitrary examles of configuration properties. These are, by definition, non-specific identifiers. They gain specificity only when further pinpointed in configuration space by a set of configuration coordinates. That is, databaseUrl is a configuration property that might have many different possible values for many different possible applications. The configuration property databaseUrl used by a particular application is only relevant when it is interpreted in the context of the appropriate set of configuration coordinates. For example, a configuration author may only be able to write a value for the databaseUrl configuration property when she knows that the value is supposed to be for the test environment in the Nevada data center on behalf of tenant 34. Or she may also be able to write a value for the databaseUrl configuration property when she knows that the value is supposed to be for the test environment and the Western region. As you can see, configuration coordinates are not hierarchical, and neither (necessarily) are configuration properties.
Configuration values are two things: the values received by an application situated in configuration space by its configuration coordinates when that application asks for a value for a configuration property, and values that are written in such a way that they may be found by one or more such applications.
For example, a configuration author might write a value, red, for a hypothetical color configuration property, into a system somewhere, once. The author may decide (let’s say) that this value is suited for any application possessing any configuration coordinates. In this case, the value received by an instance of application A identified by one set of configuration coordinates, asking for a value for the color configuration property will be the same as the value received by another instance of A identified by a different set of configuration coordinates asking for a value for the color configuration property. The author thus wrote one configuration value, but from the standpoint of the querying application instances, there are two values in play.
A configuration is simply a collection of some configuration properties and their values designated for one or more points—identified by configuration coordinates—in configuration space.
(Configuration can also mean the actual Configuration object in the framework.)
Applications wishing to use the MicroBean Configuration project create a Configurations object and then call its getValue() methods. The Configurations object is the centerpiece of the MicroBean Configuration framework.
Individual configurations are represented by implementations of the Configuration service provider interface. These implementations are loaded at Configurations creation time by the standard Java SE ServiceLoader infrastructure. To activate a given Configuration implementation, put the name of its class on one line in a file named META-INF/services/org.microbean.configuration.spi.Configuration located on the classpath of the application.
A Configuration implementation is responsible for determining whether it contains a configuration value that is suitable for a supplied set of configuration coordinates and a configuration property. If it does not contain such a value, then it returns null. If it does contain such a value, then it must not only return the String value it houses, but also a set of configuration coordinates for which that value is suitable.
The set of configuration coordinates returned is either exactly the set supplied to the Configuration, or a subset of it. This causes the returned value to be seen as an exact match or a merely suitable match. You can think of matches in terms of specificity: a configuration value that is said to exactly match a configuration property and a set of configuration coordinates is maximally specific; a configuration value that is said to suitable match a configuration property and a set of configuration coordinates is less specific.
Configuration values are, at their root, always Strings. They are converted to other types by Converters. Like Configuration instances, Converters are loaded by the standard Java SE ServiceLoader infrastructure. To activate a given Converter implementation, put the name of its class on one line in a file named META-INF/services/org.microbean.configuration.spi.Converter located on the classpath of the application.
From time to time, a configuration property may have several values that are suitable for it. For example, for a hypothetical configuration property named databaseUrl, there may be a configuration value assigned to it for the configuration coordinates represented by this listing:
…and a different value assigned to it suitable for this listing:
Both sets of configuration coordinates represent suitable matches for application located in configuration space by the following coordinates:
locale=en_US environment=test region=west dataCenter=Nevada tenantId=34
…but neither is maximally specific. So should the application use the value suitable for the first listing, or the value suitable for the second listing?
This is a configuration dispute. Configuration disputes may be resolved by Arbiter instances. Like Configuration instances, Arbiters are loaded by the standard Java SE ServiceLoader infrastructure. To activate a given Arbiter implementation, put the name of its class on one line in a file named META-INF/services/org.microbean.configuration.spi.Arbiter located on the classpath of the application.