CNF-01 is a library that provides immutable configuration for Java projects. Immutability is achieved by converting the configuration source into an immutable Map. The resulting configuration Map can’t be altered even with modifying the underlying source.
-
Provides immutable configurations for:
-
configuration files / path properties (
PathConfiguration
) -
System Properties (
PropertiesConfiguration
) -
environment variables (
EnvironmentConfiguration
) -
command line arguments (
ArgsConfiguration
)
-
-
Default configurations in case the provided configurations from a source are not found or are otherwise broken (
DefaultConfiguration
)
Notable in CNF-01 is that the configurations can not change after initialization.
For example, something might be added to the Java’s System Properties even before calling asMap()
on the PropertiesConfiguration
object. However, this will not have effect on the Configuration
object at all. All the objects are immutable.
When implementing CNF-01 to a project, the point is to keep the main objects immutable and simple by moving the handling of configuration to their own objects.
Follow these steps to implement CNF-01 in a Java project:
-
Identify the object that has to use configurations.
-
You can define an interface for the factory objects that create the configured objects:
public interface Factory<T> { public T object(); }
-
Create a Factory object for the object that has to use configurations.
public final class ExampleFactory implements Factory<Example> { private final Map<String, String> config; public ExampleFactory(final Map<String, String> config) { this.config = config; } @Override public Example object() { // Parsing of values should be done here too if something else than String is needed final String exampleType = config.get("example.type"); final String exampleText = config.get("example.text"); final Example example; if (exampleType.equals("good")) { example = new GoodExample(exampleText); } else { example = new BadExample(exampleText); } return example; } }
-
Utilize the Factory object to get properly initialized objects with the configuration options.
PropertiesConfiguration config = new PropertiesConfiguration(); Map<String, String> configurationMap = config.asMap(); ExampleFactory exampleFactory = new ExampleFactory(configurationMap); Example example = exampleFactory.object();
-
Create additional factories for other objects that require configurations.
All the sources are usable with the same Configuration
-interface. It has a single function:
Map<String, String> asMap() throws ConfigurationException;
In Java, System properties can be utilized with System.setProperty(key, value)
.
System properties can be used as a configuration source in CNF-01 with the PropertiesConfiguration
object. It has two constructors:
public PropertiesConfiguration(); // uses System.getProperties()
public PropertiesConfiguration(Properties properties); // uses any Java Properties given
Most likely the default constructor is what you’ll need, but in some cases the second constructor might become handy.
Here’s an example of using PropertiesConfiguration
:
PropertiesConfiguration config = new PropertiesConfiguration(); // uses System.getProperties()
Map<String, String> result = config.asMap();
One of the supported configuration sources is a File
. These are often marked with a ".properties" file extension but basically any Java’s File
object will do. The properties have to be given in a key=value
format in the File
. For example:
bar=foo
foo=bar
A file can be used as a configuration source with the PathConfiguration
object. It has two constructors:
public PathConfiguration(final String fileName); // Uses the filename
public PathConfiguration(final File file); // Uses Java's File object
Here’s an example of using PathConfiguration
:
Configuration configuration = new PathConfiguration("file/path");
Map<String, String> configMap = new HashMap<>();
try {
configMap = configuration.asMap();
} catch (ConfigurationException e) {
// Handle the exception...
}
PathConfiguration
throws an exception if the File
is not found.
Read Default Configuration section to see how default configurations can be used to avert the need for the try-catch.
Command line arguments (or any String[] args
) can be utilized as a configuration source with the ArgsConfiguration
object.
Here’s an example of using ArgsConfiguration
:
public static void main(String[] args) {
Configuration configuration = new ArgsConfiguration(args);
Map<String, String> configMap = new HashMap<>();
try {
configMap = configuration.asMap();
} catch (ConfigurationException e) {
// Handle the exception...
}
}
ArgsConfiguration
throws an exception if the Strings in the array don’t follow the key=value
format.
Read Default Configuration section to see how default configurations can be used to avert the need for the try-catch.
EnvironmentConfiguration
object supports Java’s System.getenv()
, meaning the system’s environment variables. The constructor takes no arguments.
Here’s an example of using EnvironmentConfiguration
:
Configuration configuration = new EnvironmentConfiguration();
Map<String, String> configMap = configuration.asMap();
Default configurations can be used in case the asMap()
function throws ConfigurationException
. If the function throws an exception, the defaults are used instead. Only PathConfiguration
and ArgsConfiguration
can currently throw an exception.
DefaultConfiguration
follows the pattern of composable decorators introduced in Elegant Objects. Therefore, it takes another Configuration
object as an argument in the constructor. The second argument is an ImmutableMap
which is in the CNF-01 library as well.
Here’s an example of how to use DefaultConfiguration
when paired with PathConfiguration
:
Map<String, String> map = new HashMap<>();
map.put("foo", "bar");
ImmutableMap<String, String> defaults = new ImmutabilitySupportedMap<>(map).toImmutableMap();
DefaultConfiguration defaultConfiguration = new DefaultConfiguration(
new PathConfiguration("invalid.path"), // uses PathConfiguration that will throw an exception
defaults
);
Map<String, String> result = defaultConfiguration.asMap();
You can involve yourself with our project by opening an issue or submitting a pull request.
Contribution requirements:
-
All changes must be accompanied by a new or changed test. If you think testing is not required in your pull request, include a sufficient explanation as why you think so.
-
Security checks must pass
-
Pull requests must align with the principles and values of extreme programming.
-
Pull requests must follow the principles of Object Thinking and Elegant Objects (EO).
Read more in our Contributing Guideline.
Contributors must sign Teragrep Contributor License Agreement before a pull request is accepted to organization’s repositories.
You need to submit the CLA only once. After submitting the CLA you can contribute to all Teragrep’s repositories.