All-in-one Java configuration properties at hand

All-in-one Java configuration properties at hand

Learn on how to use the “all-in-one” Java configuration properties giving you all the power of the various configuration features with a single line of code.

Managing properties from multiple sources such as files, input streams or URLs, with a precedence applied on them, enriched with profile functionality, obfuscated and taking environment variables, command line arguments or system properties into account as well as being being observable regarding updates:

All this functionality is covered by artifacts such as refcodes-configuration with its extensions for obfuscation, console or observer functionality.

Each part of functionality mentioned above has its own coherent implementation combinable with each other. This gives you much power to construct exactly the behavior you need.

In my blog post Dead simple Java application configuration I already outlined the ease of use of the refcodes-configuration artifact alongside the observer extension. With the blog post Automatically obfuscate your Java application’s configuration I described on how to secure your properties files using the obfuscation extension.

This blog post now introduces the refcodes-configuration-ext-runtime artifact, providing you with “all-in-one” runtime properties harnessing all the configuration properties’s functionality with just one line of code.

“All-in-one” runtime properties

The RuntimeProperties type defines all the methods required to provide the “all-in-one” functionality described above. The RuntimePropertiesImpl class implements the actual functionality as seen in the image below and the RuntimePropertiesSugar class provides the syntactic sugar for ease of use.

Runtime properties onion

Die diagram above illustrates the “onion” alike scheme which the RuntimePropertiesImpl class implements. The RuntimeProperties manage various therein contained Properties instances of various sub-types, applying the obfuscation mechanism to each “onion” layer, thereby harnessing the functionality provided by the refcodes-configuration-ext-obfuscation artifact:

  • Profile: First of all, the managed properties are being applied a profile projection as of the ProfilePropertiesProjection type. In case any “profiles” have been defined in a property called “/runtime/profiles”, then these profiles are applied on the final representation of the properties.

  • Precedence: Any Properties instances wrapped by the PropertiesPrecedenceComposite are applied with a precedence mechanism, defining which properties sources wins over which other properties source in case of colliding properties keys.

  • Args: First in the precedence chain are the command line arguments represented by the ArgsProperties and the ArgsParserProperties types with their properties representation of the command line arguments.

  • System: Second in the precedence chain are the system properties represented by the SystemProperties type and with its properties representation of the system properties.

  • Env: Third in the precedence chain are the environment variables represented by the EnvironmentProperties type with their properties representation of the environment variables.

  • Resources: Last come zero to many explicitly added ResourceProperties such as YamlProperties or TomlPropertiesBuilder with a precedence according to the order they have been added.

See the section on “Further reading” for details on the above mentioned “onion” layers building up the RuntimeProperties.

Getting started

To get up and running, include the following dependency (without the three dots “…”) in your pom.xml:

1 <dependencies>
2   ...
3   <dependency>
4     <artifactId>refcodes-configuration-ext-runtime</artifactId>
5     <groupId>org.refcodes</groupId>
6     <version>1.2.9</version>
7   </dependency>
8   ...
9 </dependencies>

Using the “all-in-one” runtime properties

Using the RuntimeProperties out of the box is quite simple, below we use syntactic sugar to create a RuntimeProperties instance and then print out the properties contained in there right from the start:

 1 import static org.refcodes.configuration.ext.runtime.RuntimePropertiesSugar.*;
 2 // ...
 3 public class Foo {
 4   public Foo() {
 5     RuntimeProperties theProperties = fromRuntimeProperties();
 6     for ( String eKey : theProperties.keySet() ) {
 7       LOGGER.info( eKey + " = " + theProperties.get( eKey ) );
 8     }
 9   }
10   // ...
11 }

You will notice that even though you did not add any properties yourself, there are lots of properties already accessible. An excerpt may look similar to the listing below:

 1 ...
 2 /eclipse/home = /usr/lib/eclipse
 3 /file/encoding = UTF-8          
 4 /file/encoding/pkg = sun.io     
 5 /file/separator = /             
 6 /gdk/core/device/events = 1     
 7 /gdk/scale = 1                  
 8 /java/class/version = 53.0                                                 
 9 /java/home = /usr/lib/jvm/java-9-jdk                                       
10 /java/io/tmpdir = /tmp                                                     
11 /java/library/path = /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
12 /java/runtime/name = Java(TM) SE Runtime Environment                       
13 /java/runtime/version = 9.0.4+11                                           
14 /java/version = 9.0.4                                                      
15 /java/vm/compressedOopsMode = Zero based                                   
16 /java/vm/info = mixed mode                                                 
17 /java/vm/name = Java HotSpot(TM) 64-Bit Server VM                          
18 /java/vm/vendor = Oracle Corporation                                       
19 /java/vm/version = 9.0.4+11
20 /xdg/session/type = x11
21 /xdg/vtnr = 2          
22 /xmodifiers = @im=ibus
23 ...

How is this possible? As shown by the illustration above, the RuntimeProperties instance takes several sources of properties into account. By default, Java’s system properties and your operating systems’s environment variables are included.

Those properties are transformed to their path representation to enable unified access to all kinds of properties sources. For example a system property with name java.version will be unified to the path /java/version or an environment variable with name JAVA_HOME will be transformed to the path /java/home.

Regarding the “path” paradigm, please read my blog post The canonical model, an ace upon your sleeve.

Colliding paths are resolved as of their precedence. For details on the precedence in case of colliding property names from different sources, please consult the diagram above.

Obfuscation properties from a file

Now you can enrich your RuntimeProperties with other properties sources. In the example below, we load the properties from a file and want specially marked properties to be encrypted (and decrypted) on a host level:

1 import static org.refcodes.configuration.ext.runtime.RuntimePropertiesSugar.*;
2 // ...
3 public class Foo {
4   public Foo() throws IOException, ParseException {
5     RuntimeProperties theProperties = withRuntimeObfuscateMode( SystemContext.HOST );
6     theProperties.withFilePath( "application.config" );
7   }
8   // ...
9 }

In addition to the system properties and the environment variables being automatically added, we added the properties from a file (see the ConfigLocator on how the properties file is discovered and where it is to be placed).

See the blog post Automatically obfuscate your Java application’s configuration for details on how to use obfuscation.

Adding command line arguments

You may directly add properties from the command line by constructing the RuntimeProperties as follows:

1 import static org.refcodes.configuration.ext.runtime.RuntimePropertiesSugar.*;
2 // ...
3 public class Foo {
4   public static void main( String[] args ) throws IOException, ParseException {
5     RuntimeProperties theProperties = withArgs( args );
6     // ...
7   }
8   // ...
9 }

If you want to apply a a special syntax to your command line arguments, you may also go as follows:

 1 import static org.refcodes.configuration.ext.runtime.RuntimePropertiesSugar.*;
 2 // ...
 3 public class Foo {
 4   public static void main( String[] args ) throws IOException, ParseException {
 5     Condition theArgsSyntax = ... 
 6     RuntimeProperties theProperties = withRootCondition( theArgsSyntax ).withArgs( args );
 7     // ...
 8   }
 9   // ...
10 }

On how to write your syntax for your command line arguments, please refer to the blog on the refcodes-console artifact.

Further reading

See the blog post on the refcodes-configuration artifact for further details on the usage of this artifact. Also see the blog posts Dead simple Java application configuration, Automatically obfuscate your Java application’s configuration and All-in-one Java configuration properties at hand. For examples and usage, please take a look at the according Unit-Tests here. On chaos-based encryption, see also the refcodes-security artifact and the according Chaos-based encryption blog post. On how to write your command line arguments parser, please refer to the blog post refcodes-console.

comments powered by Disqus