Package org.tquadrat.foundation.config


package org.tquadrat.foundation.config

This module provides a facility for the runtime configuration of a program. The core component is a class, the Configuration, that holds the configuration values. Basically, this class is a POJO (or, more precisely, a JavaBean) that will be generated during compilation from an annotated interface by an Annotation Processor (refer to the project org.tquadrat.foundation.config.ap).

The configuration values themselves may have various origins that are defined by the respective annotations; it is also possible that a value can can have multiple origins.

In particular, these origins are possible:

  • The values can be provided during the generation.
  • The values can be set programmatically, either through the setters defined in the interface, or by an implementation of Map.put() that will be generated.
  • Other possible sources are the System Properties and the Environment settings.
  • Configuration values can be set from the command line, both as Options and Arguments.
  • The values can be read from the Preferences, and, in case of User Preferences, they can also stored there.
  • The values can be read from a Windows INI file, and they can be persisted there, too.
  • The values can be read from a Java Properties file.

Other origins, like XML, JSON and JavaScript are planned.

Unless otherwise stated, null argument values will cause methods and constructors of all classes in this package to throw an Exception, usually a NullArgumentException, but in some rare cases, it could be also a NullPointerException.


User Manual

Table of Contents

  1. Introduction
  2. Usage
    1. Basic Configuration
      1. Initialisation
      2. Limitations
      3. The Annotations for the basic Configuration Bean
      4. default Getters and Setters
    2. Parsing and Interpretation of the Command Line
    3. Storage and Retrieval of Preferences

Introduction

As already said above, the core functionality is the generation of a configuration bean through an annotation processor, based on the configuration bean specification, an annotated interface that itself extends the interface ConfigBeanSpec. On how to configure the build tool of choice to use the annotation processor, refer to the Javadoc for the org.tquadrat.foundation.config.ap project; this project implements the annotation processor.

The specification interface defines getter and setter methods as described in chapter 7 of the JavaBeans™ Specification, with the extension that getters for boolean may be prefixed with "is" instead of "get". Implicitly, those getters and setters will define also the properties of the configuration bean.

Aside that the generation of a JavaBean from just an interface will save some manual work, a specification that extends only ConfigBeanSpec is boring. Additional functionality can be added to the configuration bean by extending additional interfaces as described in the following chapters.

There are two different types of configuration beans: one "global" one and a session specific version, called "SessionConfigurationBean", that uses an interface extending SessionBeanSpec as specification for the generator. The latter allows multiple instances that are distinguished by a "session key". Such a session key can be any arbitrary unique value.

Usage

As already mentioned, a configuration bean will be generated from the configuration bean specification during compile time; although the generated source code might be accessible — depending on the development environment in use — it is strongly discouraged to modify the generated code.

To obtain an instance of the generated configuration bean, call org.tquadrat.foundation.config.ConfigUtil.getConfiguration() like this:

  001002final AConfigBean configBean = ConfigUtil.getConfiguration( AConfigBean.class, c -> c.getConstructor().newInstance() );
  003

For a session bean, the call looks like this:

  001002String sessionKey = …
  003013014final ASessionBean sessionBean = ConfigUtil.getConfiguration( ASessionBean.class, sessionKey, (c, s) -> c.getConstructor( String.class ).newInstance( s ) );
  015

The lambda expression that is provided as the last parameter is required to circumvent some limitations that are introduced by the module system: usually, the package that contains the generated configuration bean is not accessible for the code in the ConfigUtil class. As a consequence, that code cannot instantiate the generated class.

But the lambda expression is code that can access the generated code, and the lambda can be executed by the code in ConfigUtil, so this construct allows to create an instance of the generated configuration bean.

When using the API provided by ConfigUtil, each call to getConfiguration() with the same arguments will return the same instance of the configuration bean.

As having a call like that above in production code looks a bit clumsy, it is recommended to add a static factory method to the configuration bean specification interface, like this:

  001002public static AConfigBean getInstance()
  003{
  004    return ConfigUtil.getConfiguration( AConfigBean.class, c -> c.getConstructor().newInstance() );
  005}
  006

This would allow to obtain a reference to the configuration bean like this:

  001002final var configBean = AConfigBean.getInstance();
  003

Annotation processing does not support the manipulation of source code before it will be compiled, at least not without breaking some rules: the class JCTree, that does provide some basic capabilities for that purpose, belongs to the package com.sun.tools.javac.tree<, and that is not exported by the module jdk.compiler, at least not to the public.

Perhaps a future version of annotation processing will provide that capability, or we try an implementation with byte code manipulation.

Basic Configuration

A simple configuration bean specification interface may look like this:

  001package com.sample.test;
  002
  003import java.util.Optional;
  004
  005import org.tquadrat.foundation.ui.configuration.CheckEmpty;
  006import org.tquadrat.foundation.ui.configuration.ConfigBeanBase;
  007import org.tquadrat.foundation.ui.configuration.ConfigurationBeanSpecification;
  008
  009@ConfigurationBeanSpecification
  010public interface ASimpleConfigurationBean extends ConfigBeanBase
  011{
  012    public Optional<String> getConfigValue();
  013
  014    public boolean isFlag();
  015
  016    @CheckEmpty
  017    public void setConfigValue( final String value );
  018}

This would generate a configuration bean that looks similar to this.

The final bean contains several methods more than those specified in the specification interface: these are defined in the interface ConfigBeanSpec. Aside some technical configuration values like the time zone and the locale, these are addListener() that allows to add a listener to the configuration bean to observe any changes to the configuration, and removeListener() that removes that listener in the end.

Internally, the configuration bean uses an instance of ConfigChangeListenerSupport to manage the listeners, and to distribute the change events.

The listener references are implemented as weak references to make memory dissipation a bit more unlikely.

When firing events, the method propertyChange() is called on each listener in a new thread.

Initialisation

The configuration bean properties can be initialised from the command line, from preferences, or from a resource that is configured through the @ConfigurationBeanSpecification annotation.

The resource has to be a standard properties file as it will be written by Properties.store(), with the name of the configuration bean property as the name for file. The values have to be String representations that can be translated to the type of the property by the respective StringConverter.

An additional (or alternative) approach would be to provide a default or static method with the signature

Map<String,Object> initData() throws Exception

with the configuration bean specification. This method returns the initialisation data for the configuration bean properties in a Map.

It is also possible to provide an implementation of the method in the base class, then the declaration in the configuration bean specification interface may be abstract (neither static nor default).

The property name is the key for the Map, and the associated value is the value for the property, as the proper type, not as its String representation! Neither the key nor the value may be null, but it is not necessary to provide values for all properties defined by the configuration bean specification.

The method initData() will be called from the constructor of the configuration bean; if it throws an Exception, the constructor will terminate with an ExceptionInInitializerError.

If both a resource and the method initData() are provided, the data from the resource is applied first, than the data from the method.

Finally, the properties that are annotated with either &#64;SystemProperty or &#64;EnvironmentVariable will be initialised from the system properties (System.getProperty()) or the environment variables (System.getenv()) accordingly.

The methods CLIBeanSpec.parseCommandLine() and PreferencesBeanSpec.loadPreferences() will not be called automatically, they have to be called from user code. They may overwrite previously set values.

Limitations

Some types are more or less inappropriate for a configuration bean attribute, basically those that are mutable so that different calls to the getter for that property may return different results. For some types, this is manageable, like for all the collection classes (List, Set, Map), for StringBuilder, for StringBuffer or for Calendar; for others, it is difficult. Therefore an Error will be thrown for the types

The getter for a property with a type implementing List, Set, or Map will always return an immutable copy of the List, Set, or Map, created through a call to List.copyOf(), Set.copyOf(), or Map.copyOf(), respectively. This means that null values are not allowed, for a Map neither as key nor as value.

The initialisation works only for types that have a known implementation of StringConverter, for Date, and for all enum types. Date is special, as an instance is to be represented as the milliseconds since the begin of the epoch (1970-01-01T00:00:00 UTC).

The Annotations for the basic Configuration Bean

As seen in the sample, the behaviour of a configuration property can be changed by adding some annotations to the getter and setter methods.

For a simple configuration bean without additional features, this are the annotations

The annotation @ConfigurationBeanSpecification is special as it is not applied to a method, but to the interface itself; in fact this is what marks an interface as a configuration bean specification.

@CheckEmpty

The annotation @CheckEmpty is allowed only for setters, and there only for those that take an argument of type String, CharSequence or Optional, or a collection type like List or Map.

It triggers the code generator to add a check on empty or null to the setter method, like in code below:

  001002/**
  003 * {@inheritDoc}
  004 */
  005@Override
  006public final void setConfigValue( final String _$value )
  007{
  008    try( final var l = m_WriteLock.lock() ) {
  009        var oldValue = m_ConfigValue;
  010        m_ConfigValue = requireNotEmptyArgument( _$value, "_$value" );
  011        m_ListenerSupport.fireEvent( "configValue", oldValue, m_ConfigValue );
  012    }
  013}  //  setConfigValue()
  014
@CheckNull

Similar to the annotation @CheckEmpty described above, the annotation @CheckNull adds an argument validation to a setter that takes any non-primitive argument; in this case, it checks that the given argument value is not null:

  001002/**
  003 * {@inheritDoc}
  004 */
  005@Override
  006public final void setConfigValue( final Date _$value )
  007{
  008    try( final var l = m_WriteLock.lock() ) {
  009        var oldValue = m_ConfigValue;
  010        m_ConfigValue = requireNonNullArgument( _$value, "_$value" );
  011        m_ListenerSupport.fireEvent( "configValue", oldValue, m_ConfigValue );
  012    }
  013}  //  setConfigValue()
  014

Obviously,the two annotations &#64;CheckNull and &#64;CheckEmpty are mutually exclusive.

@ConfigurationBeanSpecification

Primarily, this annotation marks an interface that extends ConfigBeanSpec as a configuration bean specification. But it also allows to determine the name of the generated class for the configuration bean.

The default behaviour is that for a specification interface named com.sample.application.MyConfig the class name for the bean will be com.sample.application.generated.MyConfigImpl. But if MyConfig is annotated with

  001002@ConfigurationBeanSpecification( name = "MyConfigBean", samePackage = true )
  003public interface MyConfig
  004{
  005

the class name would be com.sample.application.MyConfigBean.

It should be obvious that the fully qualified name of the generated configuration bean class needs to be distinct from any other class name.

If the configuration bean specification interface is an inner class,the enclosing class(es) will be ignored when determining the name of the class for the generated configuration bean.

The attribute initDataResource defines a resource that is used to initialise the configuration bean, as described above.

@PropertyName

The annotation &#64;PropertyName is allowed for getters, setters and "add" methods; it provides a mechanism to modify the name of the property. Usually the property name is determined from the name of the method:

  • getValue() results in the property name value
  • isFlag() results in the property name flag
  • setSwitch results in the property name switch

With the argument to the annotation &#64;PropertyName,that property name can be changed to any other arbitrary value – given that it is a valid Java identifier.

The code

  001002@PropertyName( "StartDate" )
  003public Date getConfigValue();
  004

in the configuration bean specification interface means that there will be a property StartDate in the generated configuration bean and no property configValue.

This feature is especially useful with "add" methods (that are described below): while the property for the collection itself is usually named with the plural form (getEvents()), the "add" method uses the singular form: addEvent().To connect the latter to the property events instead of event,the annotation &#64;PropertyName will be applied to the method.

@SpecialProperty

Special Propertiesare configuration properties that have a somehow special behaviour; they will be initialised in a special way or do have a special meaning or alike. The types of the available special properties are defined in SpecialPropertyType.

Getters and sometimes setters for some of the special properties are already defined in ConfigBeanSpec and SessionBeanSpec, others can be added to the configuration bean specification. Not all special properties do allow both getters and setters; refer to the respective description for the type of the special property.

@SystemProperty and @EnvironmentVariable

These annotations are allowed only for getters, and if there is no default StringConverter for the type of the property, an implementation for a String converter has to be provided with the annotation.

The value for the annotation is the name of a system property or a environment variable that is used to initialise the annotated configuration property.

System properties can be set programmatically with a call to System.setProperty() or with the -D option on the JVM's arguments list, while environment variables cannot be manipulated from inside the running program.

Calling the setter for the configuration property will not modify neither the system property nor the environment variable,and a change of the system property that occurs after the configuration bean was initialised is not reflected to the configuration property.

The &#64;EnvironmentVariable is especially handy when writing code that should run later in a Docker (or similar) container environment as these are quite often configured through environment variables.

default Getters and Setters

Since Java 8, it is possible to provide implementations in interfaces, by using the default modifier for the methods. Of course, this is also possible for a configuration bean specification. Usually, these methods are ignored by the annotation processor, only getters and setters that are marked as default need some special attention.

  • None of the annotations defined in this module may be applied to one of the default getters or setters
  • A default setter is not allowed without a default getter
  • The property that is handled by that default method is not backed by a preference value

"Add"Methods

For properties of the types List<T>, Set<T>, and Map<K,V> it is possible to have methods that add new values to the collection.Such a method would be named add<PropertyName> and takes a single argument of the collection's element type. The methods will be generated always to throw a NullArgumentException when the given argument is null, like if the method would be annotated with &#64;checkEmpty.

Parsing and Interpretation of the Command Line

This feature is based on an original idea from Kohsuke Kawaguchi (kk@kohsuke.org) and Mark Sinke, but their implementation evaluated the annotation that defined the options and arguments during runtime. Since Java 9and the introduction of Jigsaw, this approach does not work that well anymore: the annotations had to be placed either to the (private) fields or the setters for these fields *–that might be private, too, when the value should be read-only.

So if the old approach should work any longer,the annotated class needs to be in an exported module; that may be feasible, but as a requirement, this is too much of a limitation.

Therefore the whole stuff had been re-implemented on base of an annotation processor, evaluating the annotations at compile time.

In addition, the API itself can be used also in a programmatic form and with an XML configuration file; this is described in detail on the overview page for the package org.tquadrat.foundation.config.cli.

To use the Command Line (CLI) feature with the annotation processor, a configuration property just needs to be annotated with one of the annotations below. Parsing the command line is then as easy as:

  001002public final static int main( final String...args )
  003{
  004    …
  …
  014015    final var configBean = AConfigBean.getInstance();
  016    configBean.setResources( resourceBundle );
  017    if( !configBean.parseCommandLine( args ) )
  018    {
  019        configBean.retrieveParseErrorMessage.ifPresent( out::println );
  020        configBean.printUsage( out, "command" );
  021    }
  022    …
  …
  032033}  // main()
  034

That's all!

Confessed, there is a little bit more: not all data types can be annotated, only those with an implementation of CmdLineValueHandler existing on theCLASSPATHat compile time and during execution.The library knows already several value handlers, others can be provided by the program or other libraries,too.

The CLI Annotations

For the Command Line(CLI)feature,two annotations were added to the UI library:

They have to be placed on the getter method for a property, and either an &#64;Argument or an &#64;Option are allowed, but not both.

The two annotations do have mainly the same attributes,described below:

String[] aliases

This attribute is only known by the &#64;Option annotation; it provides additional option names (see below) that follow the same rules. The default value is the empty array.

String format

Some value handlers (like the DateValueHandler or those value handler implementations that are derived from TimeValueHandler) use this field for additional validation information, like a format String or a regular expression. It is ignored by most others. Refer to the documentation of those value handlers for the exact contents specification.

The default is the empty String.

Class<?extends CmdLineValueHandler> handler

Specifies the command line value handler that translates the command line argument value to the type of the target property and places that value to the property.

If not set, the effective CmdLineValueHandler implementation will be inferred from the type of the annotated property, given that such a handler is registered;otherwise, an exception is thrown.

If this annotation attribute is set, it overrides the inference and determines the handler to be used. This does not only allow the support for previously unknown data-types, it is also convenient for defining a non-standard option parsing semantics:

      001002// this is a normal argument, allowing true and false
      003@Argument( index = 0 )
      004boolean getFlag();
      005
      006// This causes that MyHandler is used instead of the default handler provided
      007// for boolean; now Yes and No can be used instead of true and false
      008@Argument( index = 1, handler = MyHandler.class )
      009boolean getYesNo();
      010
int index

This attribute is only known by the &#64;Argument annotation.

A command line argument is identified by its relative position (its 'index') on the command line, instead by a name. The first position has the index value 0, the second is 1 and so on. No gaps are allowed in the numbering of the arguments.

String metaVar

A descriptive name for the argument or for the option parameter that is used in messages. If left unspecified,that name is inferred from the name of the configuration property itself for an argument, or from the type of the property for an option.

For the property "position" that is provided as an argument and the property "location" that is provided as the value for the option "--loc", this may look like this:

Usage: command --loc STRING POSITION
boolean multiValued

A flag that indicates whether the argument or option is multi-valued, for mappings to a Collection.

As this will consume all remaining arguments from the command line, the so annotated property has to be the last argument.

For an option, it means that the same option is allowed to appear multiple times on the command line.

String name

As aliases above, this attribute is only valid for the &#64;Option annotation; it provides the name for the command line option for the annotated property.

Such an option name (as well as an option alias) has to be either a single dash ("-"), followed by a single character (a short option), or two dashes followed by more than one character (long option).

The name may contain letters, numbers, and most special characters that are allowed on a command line (excluding the equals sign "=" – for obvious reasons –, double and single quotes), but no whitespace characters.

Some examples are:

  • -r
  • --port
  • -1 – valid but not really recommended
  • -- – invalid, but allowed on the command line, having a special meaning there
  • - – invalid
  • -name – invalid: has to begin with two dashes
  • --f – invalid: not enough characters after the two dashes; would be valid as '-f'
  • --port-number – valid,but dashes within the name are discouraged
  • --port number – invalid because of the blank between port and number
  • --port_number
  • -@ – valid but strongly discouraged (see below)
  • -? – valid; usually used to request a help output
boolean required

A flag that specifies whether this argument or option is mandatory.

For an argument, this implies that all previous arguments (those with lower indexes) are mandatory as well.

String usage

A help text that will be displayed in the usage output (see CLIBeanSpec.printUsage()) when ConfigBeanSpec.getResourceBundle() does not return a ResourceBundle instance or the call to getString() with the value of "usageKey" (see below) on the retrieved resources throws a MissingResourceException.

Will be empty if not provided.

String usageKey

The resource bundle key for a help text that will be displayed in the usage output (see CLIBeanSpec.printUsage()).

If not specified, the value will be derived from the name of the property like this:

USAGE_KEY-<PropertyName>

The text will be retrieved from the ResourceBundle that is returned from ConfigBeanSpec.getResourceBundle(); if that is null the value of "usage()" (see above) is taken instead.

This allows to localise the usage output.

The Command Line

The command line parser recognises command lines according to this pattern:

[{@<filename>|-<o>[[ ][<parameter>]]|--<option>[{ |=}<parameter>]}…][ --][ {@<filename>|<argument>]}…]

This means that

  • -oparameter
  • -o parameter
  • --option parameter
  • --option=parameter

are all valid.

@<filename> stands for an argument file containing a single command line entry per line. This means that an argument file with the contents

  # This is an argument file
  --flag
  --option1
  value
  --option2=other value
  --option3
  yet another value
  argument
  another argument

is parsed like the command line

--flag --option1 value --option2 "other value" --option3 "yet another value" argument "another argument"

The line beginning with the hash ("#") is treated as a comment.

The contents of an argument is copied to the command line at the location of the reference to that file: if argfile has the contents

  --flag
  --option
  value
  argument

and the command line was given like

  --loc NONE @argfile

the final sequence is

--loc NONE --flag --option value argument

The token "--" means that after it no more options will follow. So after --,an argument like "--dashes" might be valid.

Storage and Retrieval of Preferences

With the package java.util.prefs an API exists that can be used to persist program settings between different runs of an application. The different implementations of that API utilise mechanisms that are specific to the operating system where the program will be executed. So it will use the "Registry" if running on Microsoft Windows or special files in the home folder on a Linux system.

This library connects the configuration values (at least most of them) with preferences values, using the name of the configuration bean specification as the name of the node and the key is the name of the property. Both can be modified by annotations.

To enable the preferences support for a configuration bean specification, it needs to extend the interface PreferencesBeanSpec.

That interface provides the methods PreferencesBeanSpec.loadPreferences() that reads the preference values, and PreferencesBeanSpec.updatePreferences() to write the values back.

Per default, all custom properties that have a supported type will be backed by preferences. If a preference is unwanted for a certain property, it can be annotated with @NoPreference.

"Supported types" are all those types for that an implementation of PreferenceAccessor exists; some will be detected automatically, for others the class of the implementation can be provided with the @Preference annotation.

USER vs. SYSTEM

The Preferences API distinguishes between system-wide preferences and user-specific preferences. Per default, the configuration bean properties are treated as user properties, because system-wide properties may need special authorisation to read and especially to write them. As a consequence it is not possible to write back system-wide properties with PreferencesBeanSpec.updatePreferences().

On a UNIX based system (this includes Linux and MacOS), the preferences are stored in the file system.

For MacOS, the preferences files generated by the Preferences API are named com.apple.java.util.prefs.plist. The user's preferences file is stored in their home directory (~/Library/Preferences/). The system preferences are stored in /Library/Preferences/and are only persisted to disk if the user is an administrator. Some more details can be found on StackOverflow.

On UNIX and Linux, the location for the user's preferences is set with the system property ""java.util.prefs.userRoot""; if not set, it is ~, the user's home directory. This would then result in the path <path>/.java/.userPrefs/<nodeName>/prefs.xml.

The location for the system preferences is set with the system property ""java.util.prefs.systemRoot""; if not set, it is either /etc or $JAVA_HOME, depending on the JVM.This would then result in the path <path>/.java/.systemPrefs/<nodeName>/prefs.xml.

In case a node has child nodes, there is a folder hierarchy underneath the <nodeName> folder. Some more details can be found again on StackOverflow.

In case the node name contains characters that are not appropriate for a UNIX folder name — according to the logic of the API, this includes the dot('.',0x2e) and the underscore ('_',0x5f) — the folder name will be the BASE64 encrypted form of the node name, prepended with an underscore ('_',0x5f)(sic!). As the the default name for the preferences node for a configuration bean is its fully qualified class name — containing dots — the folder name for that preferences file is usually something cryptic like "_!':!bw"t!#4!cw"h!'0!c!"s!'`!.g"0!'`!cw"0!#4!~w"l!'4!~@"y!'%!d!"l!'@!.g"$!'8!bg"m!'k!~w"1!()!@"0!'k!bw"u!%)!~@"h!'4!]@"t!(!!b!==}" (this is for "com.sample.test.generated.ConfigurationBeanImpl").

Implementation of Map

A configuration bean specification interface can extend the interface java.util.Map<String,Object>; the required methods will be generated automatically.

The Map interface provides reading access to the property values, the mutating methods of Map (like clear() or put()) will throw an UnsupportedOperationException.

If the Map feature is used, the configuration bean specification may not define a getter method isEmpty() as this would collide with the method with the same name from the interface Map.

i18n and Configuration Beans

A configuration bean is the perfect source for messages and texts. By adding the interface I18nSupport to the configuration bean specification, the configuration bean will be enabled as such a source for texts and messages.

It uses the Locale that will be returned by ConfigBeanSpec.getLocale() to identify the resource bundle that will be returned by ConfigBeanSpec.getResourceBundle(); a call to ConfigBeanSpec.setLocale() will change the language of the texts and messages (if there are translations for the new language, of course).

When the i18n support is used, it is not recommended to define a setter for the resourceBundle property.

  • Class
    Description
    This annotation is used in the context of a configuration bean specification to mark a property that receives the value of a command line argument.
    Used with a setter and indicates that a EmptyArgumentException should be thrown when the new value for the property is empty.
    Used with a setter and indicates that a NullArgumentException should be thrown when the new value for the property is null.
    When a configuration bean should be initialised from the command line, the respective specification interface needs to extend this interface.
    Signals an error in the user input.
    The base for the specification of a configuration bean; the final specification interface must also be annotated with @ConfigurationBeanSpecification in order to be recognised properly.
    This exception type is used to signal a problem with the initialisation of a configuration bean.
    The marker for a configuration bean specification.
    The event object that is thrown each time a property of a configuration bean is changed.
    The definition of a listener for configuration change events.
    Utility methods that can be used to handle configuration beans.
    This annotation indicates that the property for the annotated getter is initialised from an environment variable with the given name.
    Excludes the value of the property belonging to annotated getter from being included into the output of Object.toString().
    A configuration bean specification has to extend this interface in order to get the i18n support.
    When a configuration bean should be connected with an INI file (a Windows style configuration file), the respective configuration bean specification interface needs to extend this interface.
    The definition of an INI file that is used in the context of a configuration bean implementing INIBeanSpec.
    The definition for a INI file group.
    The container for @INIGroup annotations.
    The marker for properties that will be persisted in an INI file.
    Excludes the property from having a preferences reference.
    This annotation is used in the context of a configuration bean specification to mark a property that receives the value of a command line option.
    Forces a property to have a preferences reference and configures it.
    When a configuration bean should be connected with Preferences, the respective configuration bean specification interface needs to extend this interface.
    Provides some general configuration settings for a configuration bean that implements PreferencesBeanSpec.
    The interface for a configuration bean that allows multiple instances ("Sessions").
    This annotation is used to mark getters and setters that are implemented in a special way.
    The types of the special properties for a configuration bean.
    This annotation defines the implementation of StringConverter that is used to convert the value of annotated property into a String, or a String into the value.
    This annotation indicates that the property for the annotated getter is initialised from a SYSTEM Preferences value with the path and name.
    This annotation indicates that the property for the annotated getter is initialised from a system property with the given name.