Properties - Best Practices : How to

There are basically two kinds of properties;

  1. Properties that belong to the environment - for example the database you connect to when you are running the application in Dev. as opposed to the database you connect to when you are running in Prod.
  2. Properties that crosscut environments - for example how you want to log or the resource bundle you are using for messages.

That said you may want to override properties in certain environments - for instance you may want to use a different message bundle in Prod w/o recompiling or restarting the application:

  1. There needs to be a hierarchical and consistent way to override properties.
  2. Properties need to be configurable at runtime.

These are some of the basic premises used to arrive at the current practice of how properties are implemented.

A reference implementation exists in SocraticGrid github:
     https://github.com/SocraticGrid/PropertiesConfiguration

This project is intended to provide a reference implementation to be adopted in part or in all.

The two properties files:

  • config.xml
  • External.properties

demonstrate the different syntaxes able to be read by the property configurator; XML and standard properties syntax.  The XML has a richer syntax which allows for simple property definitions:
    <property name="example" value="example_value" />

as well as a means to group properties into environments:<environments default="dev-local">

<environment id="dev-local">
   <property name="agent.name" value="kmf-fhir-agent" />
   <property name="orders.endpoint" value="http://172.31.5.68:8080/OrderService/ordermanagement" />
   <property name="alerting.endpoint" value="http://172.31.5.72:8080/KMFUCSAlerting/ucsalerting" />
</environment>

The environment can then be referenced and only properties for that environment will be read (please see config.xml)

External.properties demonstrates a normal property syntax - the architect can choose to use one format or both or mix.  Which reader is used is configured in the main property class in this case SGProperties (see org.socraticgrid.properties.SGProperties.java).

Properties are overridden in last read order. In other words if a property is configured in multiple places the last place it is read from is the final setting.  The order of reading is:

  1. SGProperties class
  2. config.xml
  3. External.properties

The order of reading is determined in SGProperties but is intended to follow the hierarchy imposed by the classloader.  

  1. The first instantiation of the properties is within the SGProperties class.  
  2. In this project config.xml is placed in WEB-INF/classes and is read second
  3. Finally External.properties is not embedded in the war file at all but is intended to be in $CATALINA_HOME/shared/classes and is loaded last.

This means if a property appears in the External.properties file it will be read last and the setting for that property will be that which is set in External.properties.  This is meant to provide a means for configuration to be set by a variety of different people.  For instance if your application is going to only be used in known environments then those environments can be configured in config.xml and which environment the application is in can be determined by External.properties which resides in $CATALINA_HOME and never needs to be reset.  If your application is going to be used in environments that are unknown the it makes sense for the application to be completely configured by External.properties and possibly reasonable defaults placed in either SGProperties or in config.xml.  Any time properties need to be available from without the application it makes sense to have them configured in External.properties.

Behavioral differences

MethodParameter formatLookup failure behaviorUsage example

ClassLoader.

getResourceAsStream()

"/"-separated names; no leading "/" (all names are absolute)Silent (returns null)

this.getClass().getClassLoader()

.getResourceAsStream

("some/pkg/resource.properties")

Class.

getResourceAsStream()

"/"-separated names; leading "/" indicates absolute names; all other names are relative to the class's packageSilent (returns null)

this.getClass()

.getResourceAsStream

("resource.properties")

ResourceBundle.

getBundle()

"."-separated names; all names are absolute;.propertiessuffix is implied

Throws unchecked

java.util.MissingResourceException

ResourceBundle.getBundle

("some.pkg.resource")