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-properties
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-properties
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-properties-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 ApplicationProperties
type defines all the methods required to provide the “all-in-one” functionality described above. The ApplicationProperties
class implements the actual functionality as seen in the image below and the ApplicationPropertiesSugar
class provides the syntactic sugar for ease of use.
The diagram above illustrates the “onion” alike scheme which the ApplicationProperties
class implements. The ApplicationProperties
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-properties-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 thePropertiesPrecedenceComposite
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 theArgsProperties
and theArgsParserProperties
types with their properties representation of thecommand line arguments
. -
System: Second in the precedence chain are the
system properties
represented by theSystemProperties
type and with its properties representation of thesystem properties
. -
Env: Third in the precedence chain are the
environment variables
represented by theEnvironmentProperties
type with their properties representation of theenvironment variables
. -
Resources: Last come zero to many explicitly added
ResourceProperties
such asYamlProperties
orTomlPropertiesBuilder
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
ApplicationProperties
.
Getting started
To get up and running, include the following dependency (without the three dots “…”) in your pom.xml
:
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-properties-ext-runtime</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.8</version>
</dependency>
...
</dependencies>
Using the “all-in-one” runtime properties
Using the ApplicationProperties
out of the box is quite simple, below we use syntactic sugar to create a ApplicationProperties
instance and then print out the properties contained in there right from the start:
1
2
3
4
5
6
7
8
9
10
11
import static org.refcodes.properties.ext.application.ApplicationPropertiesSugar.*;
// ...
public class Foo {
public Foo() {
ApplicationProperties theProperties = fromApplicationProperties();
for ( String eKey : theProperties.keySet() ) {
LOGGER.info( eKey + " = " + theProperties.get( eKey ) );
}
}
// ...
}
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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
/eclipse/home = /usr/lib/eclipse
/file/encoding = UTF-8
/file/encoding/pkg = sun.io
/file/separator = /
/gdk/core/device/events = 1
/gdk/scale = 1
/java/class/version = 53.0
/java/home = /usr/lib/jvm/java-9-jdk
/java/io/tmpdir = /tmp
/java/library/path = /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
/java/runtime/name = Java(TM) SE Runtime Environment
/java/runtime/version = 9.0.4+11
/java/version = 9.0.4
/java/vm/compressedOopsMode = Zero based
/java/vm/info = mixed mode
/java/vm/name = Java HotSpot(TM) 64-Bit Server VM
/java/vm/vendor = Oracle Corporation
/java/vm/version = 9.0.4+11
/xdg/session/type = x11
/xdg/vtnr = 2
/xmodifiers = @im=ibus
...
How is this possible? As shown by the illustration above, the ApplicationProperties
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 ApplicationProperties
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
2
3
4
5
6
7
8
9
import static org.refcodes.properties.ext.application.ApplicationPropertiesSugar.*;
// ...
public class Foo {
public Foo() throws IOException, ParseException {
ApplicationProperties theProperties = withRuntimeObfuscateMode( SystemContext.HOST );
theProperties.withFilePath( "application.config" );
}
// ...
}
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 ApplicationProperties
as follows:
1
2
3
4
5
6
7
8
9
import static org.refcodes.properties.ext.application.ApplicationPropertiesSugar.*;
// ...
public class Foo {
public static void main( String[] args ) throws IOException, ParseException {
ApplicationProperties theProperties = withArgs( args );
// ...
}
// ...
}
If you want to apply a a special syntax
to your command line arguments
, you may also go as follows:
1
2
3
4
5
6
7
8
9
10
import static org.refcodes.properties.ext.application.ApplicationPropertiesSugar.*;
// ...
public class Foo {
public static void main( String[] args ) throws IOException, ParseException {
ArgsSyntax theArgsSyntax = ...
ApplicationProperties theProperties = withRootCondition( theArgsSyntax ).withArgs( args );
// ...
}
// ...
}
On how to write your
syntax
for yourcommand line arguments
, please refer to the blog on therefcodes-cli
artifact.
Further reading
See the blog post on the refcodes-properties
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-cli
.