Simplify Java property handling and create a single source of truth!
- Property Aggregator
- Changelog
- Maven dependency
- Usage
- General usage
- Specifying property sources
- Specifying property hierarchies
- Querying properties
- Filtering property keys during the build
- Specifying custom filters during the build
- Retrieving subsets of properties from the final list
- Setting default values
- Logging the final properties
- Checking the size of the final properties
- Retrieving all properties
- Appendix
This project takes care of various properties sources in Java like system and environment variables as well as properties files. It allows you to specify which sources override others, filter them and give them default values in order to have one single aggregated source of application properties.
All changes can be seen in the linked changelog.
<dependency>
<groupId>blog.softwaretester</groupId>
<artifactId>property-aggregator</artifactId>
<version>Check the version number above</version>
</dependency>
The Property Aggregator uses a simple builder pattern to specify property sources and options.
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withEnvironmentProperties()
.withPropertiesFile("path/to/Test1.properties")
.build();
The boolean parameter inside Builder()
determines if logs should be shown or not.
The false
option can be useful if the PropertyAggregator should be a "silent part" of another library.
In this case,
- environment properties (aka environment variables) are loaded first
- a
Test1.properties
file is loaded which overrides existing properties from the step before
You can specify multiple sources like this in any order to create your custom property hierarchies.
When the final build()
method is called, all property sources are combined,
filters are applied and default values are set.
To query for certain properties, use it as shown below:
propertyAggregator.getProperty("property1")
To use system properties as a property source, use the withSystemProperties()
option:
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withSystemProperties()
.build();
To use environment properties as a property source, use the withEnvironmentProperties()
option:
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withEnvironmentProperties()
.build();
To use a properties file as a property source, use the withPropertiesFile()
option:
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withPropertiesFile("path/to/custom.properties")
.build();
If the properties file can be anywhere inside the application's class path, use this method to retrieve them:
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withPropertiesFileInClassPath(pathToProperties)
.build();
The power of Property Aggregator is the ability to specify which properties have a higher order than others.
This enables hierarchies such as:
- the application's standard property file
application.properties
- this can be overwritten with system properties
- this can be overwritten with environment properties.
This example would look like this in code:
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withPropertiesFile("path/to/application.properties")
.withSystemProperties()
.withEnvironmentProperties()
.build();
If properties do not exist in a higher source but only in a lower one, the lower one is added to the final properties. That means that higher sources overwrite properties that already exist in a lower one.
To retrieve a property value by key, just use
String propertyValue =
propertyAggregator.getProperty("propertyKey"));
This returns the property value from the final processed and filtered set of
properties or an appropriate default value if it exists. Otherwise, the
return value will be null
.
Especially when using environment properties, they can be quite large and not all of them may be relevant for your application. It is possible to add a filter in this case to remove all properties whose keys are not required.
List<String> filteredKeys =
List.of("property1", "property2");
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withEnvironmentProperties()
.withFilteredKeys(filteredKeys)
.build();
Here, all environment properties that are not called "property1" or
"property2" are stripped out of the PropertyAggregator
instance.
Note: The filter is applied when the final build()
is called on the
PropertyAggregator
builder. That means that it is applied after all
property sources are combined.
If you want to permanently filter properties in the final list based on custom predicates, you can do it like this:
Predicate<? super Map.Entry<String, String>> myAppFilter =
(Predicate<Map.Entry<String, String>>) entry ->
entry.getKey().startsWith("myApp");
PropertyAggregator propertyAggregator = new PropertyAggregator.Builder(true)
.withPropertiesFile(RESOURCES_DIR + "application.properties")
.withCustomPredicate(myAppFilter)
.build();
This example permanently alters the property list and only keep properties whose key starts with "myApp".
Note: It is possible to use withCustomPredicate
multiple times.
It is possible to get subsets of properties that match custom predicates. One use case for this is having different sets of properties that have to be passed on to application plugins.
Predicate<? super Map.Entry<String, String>> filterXProperties =
(Predicate<Map.Entry<String, String>>) entry ->
entry.getKey().startsWith("X");
Properties properties =
propertyAggregator.getPropertiesWithCustomPredicate(filterXProperties);
This example would return properties that have keys starting with the letter "X".
Setting default values can be beneficial if certain properties must exist and can optionally be overwritten.
Map<String, String> defaults =
Map.of("property1", "default1");
PropertyAggregator propertyAggregator =
new PropertyAggregator.Builder(true)
.withDefaultValues(defaults)
.withPropertiesFile("path/to/application.properties")
.build();
Here, the key "property1" is set with a default value of "default1". If any of the property sources (in this example a properties file) does not include "property1", it is added to the final list of properties with the default value. If, however, "property1" is specified in a property source, the default value is replaced by the actual value.
Note: If you use defaults and filters together, properties with default values are added to the final set regardless of filters!
If you want to check the properties after all applied operations, you can log all properties like this:
propertyAggregator.logFinalProperties();
Note: These logs are always shown regardless of the log settings of the builder!
In order to get the size of processed properties including properties that are only specified via default values, use
propertyAggregator.getPropertiesCount()
This method returns all processed property keys and values that are set by property sources or defaults and are not filtered out:
propertyAggregator.getAllProperties()
Property Aggregator requires Java >= 11 and Maven >= 3.3.9.
It is available in Maven central.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.