README
The
REFCODES.ORG
codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.
What is this repository for?
This artifact provides a canonical model for processing properties from various different formats (*.yaml, *.ini, *.toml, *.json, *.xml or *.properties) and locations (JAR files, file system files, streams, HTTP resources or GIT repositories) and for converting properties to and from POJOs (plain java objects) and for applying various transformations and views to the properties.
How do I get set up?
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</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.8</version>
</dependency>
...
</dependencies>
The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.
If you also want to observe your properties (e.g. listen for create
, update
or delete
operations), you may instead add the following dependency to your pom.xml
:
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-properties-ext-observer</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.8</version>
</dependency>
...
</dependencies>
The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.
Jump start
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import static org.refcodes.properties.PropertiesSugar.*;
// ...
public class Foo {
public void bar() throws IOException, ParseException {
// ...
PropertiesBuilder theProperties = fromProperties(
toProperty( "message", "Hello world!" ),
toProperty( "foo", "bar" )
);
fileToTomlProperties( theProperties, "application.toml");
// ...
Properties thePrecedence = toPrecedence(
fromSystemProperties(),
fromEnvironmentVariables(),
seekFromTomlProperties( "application.toml" )
);
FooConfig theConfig = thePrecedence.toType( FooConfig.class );
// ...
}
// ...
public static class FooConfig {
String message;
int frequency;
TemperatureUnit unit;
int port;
}
// ...
}
See also the blog post Dead simple Java application configuration.
How do I get started?
Use the static import syntactic sugar to easily harness the refcoces-properties
features.
1
2
import static org.refcodes.properties.PropertiesSugar.*;
// ...
Create some properties
and play around with them:
1
2
3
4
5
6
7
8
// ...
PropertiesBuilder theProperties = fromProperties(
toProperty( "/user/firstName", "Nolan" ),
toProperty( "/user/lastName", "Bushnell" ),
toProperty( "/commodore/user/firstName", "Jack" ),
toProperty( "/commodore/user/lastName", "Tramiel" )
);
// ...
The content of your properties
looks as follows:
1
2
3
4
/user/firstName=Nolan
/user/lastName=Bushnell
/commodore/user/lastName=Tramiel
/commodore/user/firstName=Jack
The
PropertiesBuilder
inherits from theMap
interface, it is fully compatible with theJava collections framework
.
You may have noted that the keys
in your properties
look like path
declarations. This is no coincidence, you can now manipulate your properties
using (sub-)paths
. See more in the article The canonical model, an ace upon your sleeve. E.g. you may now retrieve the commodore
specific properties (the properties
below the /commodore
path):
1
2
3
// ...
Properties theCommodoreProperties = theProperties.retrieveFrom( "/commodore" );
// ...
This results in your commodore
specific properties
to look as such:
1
2
/user/lastName=Tramiel
/user/firstName=Jack
There are many more features hidden in the
Properties
type, just browse the Javadoc.
The Properties
type is the read-only
super-type of the PropertiesBuilder
type. Common functionality produces Properties
instances which easily can be converted into a mutable PropertiesBuilder
instance as follows:
1
2
3
// ...
PropertiesBuilder theBuilder = toPropertiesBuilder( theCommodoreProperties );
// ...
Snippets of interest
Below find some code snippets which demonstrate the various aspects of using the refcodes-properties
artifact (and , if applicable, its offsprings). See also the example source codes of this artifact for further information on the usage of this artifact.
Storing properties
Considering the example from above, storing properties
is as easy as this:
1
2
3
// ...
ResourcePropertiesBuilder theResourceProperties = saveToTomlProperties( theProperties, "/some/path/to/my/properties.toml" );
// ...
You can make the library
choose a suitable place for you where to save your properties
to; being near the launcher (JAR
) of your application:
1
2
3
// ...
ResourcePropertiesBuilder theResourceProperties = fileToTomlProperties( theProperties, "properties.toml" );
// ...
See the ConfigLocator on how a suitable location is determined by the
library
.
Loading properties
Considering the example from above, loading properties
back again is as easy as this:
1
2
3
// ...
ResourcePropertiesBuilder theResourceProperties = loadFromTomlProperties( "/some/path/to/my/properties.toml" ):
// ...
You can make the library
seek for a suitable properties
for you to load; being near the launcher (JAR
) of your application:
1
2
3
// ...
ResourcePropertiesBuilder theResourceProperties = seekFromTomlProperties( "properties.toml" );
// ...
See the ConfigLocator on how the properties file is discovered by the
library
and where it is to be placed.
Properties parsers
There are some notations being supported by the refcodes-properties
artifact:
- Java
PROPERTIES
based properties INI
based propertiesTOML
based propertiesXML
based propertiesYAML
based propertiesJSON
based properties
Please note that some notations use the
this
keyword to denote the value of the enclosing element as some notations do not support (or make it hard) to assign a value to an element which has child elements (see the blog postrefcodes-logger
regarding the various notations).
Profiles from properties
A profile
is identified by the first level path hierarchy in your originating properties
. In the example above, commodore
represents a profile specific configuration which we can use to get our profile
view:
1
2
3
// ...
Properties theProfile = fromProfile( theProperties, "commodore" );
// ...
We can also specify a property
in our properties
for the path /runtime/profiles
identifying the
profiles
to be considered (comma separated).
See also the
ProfilePropertiesProjection
type as well as theProfilePropertiesDecorator
type on more usages.
Schedule reloading of properties
Using the ResourcePropertiesBuilder
form above which we attached to a file, we now can schedule a reload of the properties
:
1
2
3
// ...
ResourceProperties theScheduled = schedule( theResourceProperties, 5000, ReloadMode.ORPHAN_REMOVAL );
// ...
We actually encapsulate the properties
with a schedule decorator
which reloads the encapsulated properties
accordingly: Reload them each 5000
milliseconds and remove any properties
not found in the attached resource (e.g. File
) from your properties
instance.
Observable properties
You may also listen to any create
, update
or delete
changes applied to your properties. To do so, you must encapsulate your ResourcePropertiesBuilder
instance with an observable decorator
of type ObservableResourcePropertiesBuilder
. This observable
fires a sub-type of the PropertyEvent
upon updates applied to the properties
to each subscriber of those events
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import static org.refcodes.cli.ext.observer.ObservablePropertiesSugar.*;
// ...
PropertiesBuilder theProperties = toPropertiesBuilder();
ObservablePropertiesBuilder theObservable = observe( theProperties );
theObservable.subscribeObserver( aEvent -> {
if ( aEvent.getAction() == PropertyAction.PROPERTY_UPDATED ) {
System.out.println( aEvent.getClass().getSimpleName() + " (" + aEvent.getAction() + ") --> " + aEvent.getKey() + " := " + aEvent.getValue() );
}
} );
theObservable.put( "/user/firstName", "Nolan" );
theObservable.put( "/user/lastName", "Bushnell" );
theObservable.put( "/user/firstName", "Jack" );
theObservable.put( "/user/lastName", "Tramiel" );
theObservable.remove( "/user/firstName" );
theObservable.remove( "/user/lastName" );
// ...
For the example above to work, make sure to include the refcodes-properties-ext-observer
dependency (see at the beginning of this article) in your project’s pom.xml
.
The lambda
being subscribed acts as a listener
which is a functional interface
of type PropertiesObserver
with which you explicitly may listen to the three event types PropertyCreatedEvent
, PropertyUpdatedEvent
and PropertyDeletedEvent
as well as to the super-type PropertyEvent
.
Observing just the PropertyEvent
type catches all of its sub-types (as we did in the example above).
Multiple properties precedence
We can combine multiple Properties
instances behind a composite behaving just like a Properties
type:
1
2
3
4
5
6
// ...
Properties theProperties = toPrecedence(
fromSystemProperties(),
fromEnvironmentVariables(),
seekFromJavaProperties( "application.config" ) );
// ...
Above we created a composite containing various properties
instances, the first one overrules the second one and the second one overrules the third one when accessing the resulting Properties
composite.
Take a look at the SystemProperties
as well as at the EnvironmentProperties
instances we added: They provide means to access Java ‘s system properties
or the operating system
’s environment variables
within your properties
..
Obfuscate your properties
Ever been worried about credentials (passwords, secrets or access keys) residing in your host’s application configuration files as plain text? Read on how to automatically obfuscate your application’s properties residing in configuration files at a system-context’s level?
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-properties-ext-obfuscation</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.8</version>
</dependency>
...
</dependencies>
Now you can use the ObfuscationResourcePropertiesBuilder
type as follows:
1
2
3
4
5
6
7
8
9
10
11
import static org.refcodes.properties.ext.obfuscation.ObfuscationPropertiesSugar.*;
// ...
public class Foo {
public Foo() throws IOException, ParseException {
ResourcePropertiesBuilder properties = seekFromJavaProperties( "application.properties" );
ObfuscationResourcePropertiesBuilder obfuscateProperties = obfuscate( properties );
obfuscateProperties.put("secret", "encrypt:Hello world!");
obfuscateProperties.flush();
}
// ...
}
The referenced “application.properties
” are now applied with obfuscation, any property value prefixed with “encrypt:
” will be encrypted upon flushing, causing it to be prefixed with “decrypt:
” and written (to the file system) after being encrypted! Accessing such a property within your application will provide you with the decrypted(!) value transparently.
For a detailed insight see also the blog post Automatically obfuscate your Java application’s configuration!
All in one
The ApplicationProperties
and the ApplicationProperties
types provide Properties
compositions for common everyday application setups, combining many of the above mentioned featured.
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>
For a detailed insight see also the blog post All-in-one Java configuration properties at hand!
Under the hood
The
canonical model
pattern is an ace up your sleeve in order to open your libraries for functionality otherwise to be implemented in a tiresome, error prone and redundant way. As you settle upon acanonical model
within your library, your library’s will be able to interact with any existing and yet to be implemented functionality on top of yourcanonical model
, making your bits and pieces work together magically.
The CanonicalMap
is the super-type of the Properties
related types. Read more in the blog post The canonical model, an ace upon your sleeve.
Examples
For examples and usage, please take a look at the according source code examples here. For examples and usage on the observable extensions, please take a look at the according source code examples here. For examples and usage on the “all in one” runtime extensions, please take a look at the according source code examples here.
Contribution guidelines
- Report issues
- Finding bugs
- Helping fixing bugs
- Making code and documentation better
- Enhance the code
Who do I talk to?
- Siegfried Steiner (steiner@refcodes.org)
Terms and conditions
The REFCODES.ORG
group of artifacts is published under some open source licenses; covered by the refcodes-licensing
(org.refcodes
group) artifact - evident in each artifact in question as of the pom.xml
dependency included in such artifact.