Sunday, September 13, 2015

Spring Externalized Configuration Design Patterns

This post is a dump of details surrounding setting up a configuration ecosystem for WAR deployments that is independent and as stateless as possible.
Some of these patterns I learned while developing the Drug Database and API for Telus Health.  However, the major portion of these patterns I picked up while developing with ProntoForms Inc.

Requirements


R1)  Environment variance: Some of the configuration will vary depending on whether we are in a production, staging, qa or dev deployment.
R2) Developer and Task specific: We will allow for developer and task discriminators to further specialize and override properties
R3) Plural properties: We need the ability to specify arbitrary sets or lists of property values - via XML or YAML



Implementation

Normally I would put the ecosystem, discriminator and developer variables in the @Value annotation to do some runtime dynamic substitution.  However, this would target only a specific node in the properties tree.  What we need is a layered resolution of properties where we override any properties previously loaded with those deeper in the tree.  We accomplish this by loading the properties in sequence.

    <context:property-placeholder 
        location="file:///${os.environment.configuration.dir}//${os.environment.ecosystem}/biometric.properties" 
        order="1" ignore-unresolvable="true" ignore-resource-not-found="true"/>
    <context:property-placeholder 
        location="file:///${os.environment.configuration.dir}//${os.environment.ecosystem}/${os.environment.discriminator:}/biometric.properties" 
        order="2" ignore-unresolvable="true" ignore-resource-not-found="true"/>
    <context:property-placeholder 
        location="file:///${os.environment.configuration.dir}//${os.environment.ecosystem}/${os.environment.developer.username:}/biometric.properties" 
        order="3" ignore-unresolvable="true" ignore-resource-not-found="true"/>
    <context:property-placeholder 
        location="file:///${os.environment.configuration.dir}//${os.environment.ecosystem}/${os.environment.discriminator:}/${os.environment.developer.username:}/biometric.properties" 

        order="4" ignore-unresolvable="true" ignore-resource-not-found="true"/>

        

System environment variables

Tomcat container - Launch Configuration | Environment
os.environment.ecosystem=dev
os.environment.developer=obrien
os.environment.discriminator=<empty>
os.environment.configuration.dir=/wsc/os/config

File System property files

/wsc/os/config/biometric.properties
os.environment.persistencecontext.applicationservice.name=from

Note: for external resources you also need the following for missing directories

ignore-resource-not-found="true"

Spring context xml

 <context:property-placeholder 
        location="file:///${os.environment.configuration.dir}/biometric.properties" />

    <util:list id="locationsList">
        <value>${os.environment.configuration.dir:classpath:}/${os.environment.ecosystem}}</value>
        <value>${os.environment.configuration.dir:classpath:}/${os.environment.ecosystem}/${os.environment.discriminator:}}</value>
        <value>${os.environment.configuration.dir:classpath:}/${os.environment.ecosystem}/${os.environment.developer.username:}}</value>
        <value>${os.environment.configuration.dir:classpath:}/${os.environment.ecosystem}/${os.environment.discriminator:}/${os.environment.developer.username:}}</value>
    </util:list>

Spring Bean


private @Value("${server}") String server;
@PersistenceContext(name="${os.environment.persistencecontext.applicationservice.name}")

private EntityManager entityManager;


References
https://jira.spring.io/browse/SPR-5719
https://jira.spring.io/browse/SPR-6428

Total Pageviews

Followers