Tuesday, April 15, 2014

Operation friendly Spring application runtime configuration

Most applications need some kind of runtime configuration like e.g. database connection settings. These seetings are usually provided as program arguments or system properties. The following post will show a proper way to include runtime configuration support in a modern Spring application to make it is easy to handle the application by the operation team.

Configuration with property placeholder


The runtime configuration settings are most likely needed in your Spring configuration class. That's the place were the beans should be properly instantiated.

  1 /**
  2  * Core Application Configuration.
  3  */
  4 @Configuration
  5 @PropertySource("${myAppProperties:classpath:myApp.properties}")
  6 public class AppConfig {
  7 
  8   @Bean
  9   public SampleBean sampleBean(@Value("${httpProxy:}") String httpProxy,
 10           @Value("${httpProxyPort:0}") int httpProxyPort,
 11           @Value("${httpConnectionTimeout:30000}") int httpConnectionTimeout,
 12           @Value("${httpReceiveTimeout:60000}") int httpReceiveTimeout,
 13           @Value("${targetServiceUrl}") String targetServiceUrl) {
 14     return new SampleBean(httpProxy, httpProxyPort, httpConnectionTimeout,
 15             httpReceiveTimeout, targetServiceUrl);
 16   }
 17 
 18   @Bean
 19   public static PropertySourcesPlaceholderConfigurer properties() {
 20     PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
 21     pspc.setNullValue(""); // handle empty Strings as null value
 22     return pspc;
 23   }
 24 
 25 }
 
To inject a runtime configuration value into a bean the @Value annotation is used together with a property placeholder (${<propertyName>:<defaultValue>}).The interesting parts of this configuration class are:
  • method parameter injection is used to set the runtime properties
    (see @Value annotation in sampleBean)
  • a default value for a property is provided whenever possible
    (the default value is provided after the ':' in the property placeholder)
  • a PropertySource annotation is used to add a property file to the Spring Enviroment
  • a PropertySourcePlaceholderConfigurer is instantiated to resolve the properties against the Spring Environment

The location of the PropertySource is declared as a placeholder itself

${myAppProperties:classpath:myApp.properties}

which means that the value of the myAppProperties system property will be used as a PropertySource resource or - if this is not defined - the classpath:myApp.properties file per default.

 This setup now allows to put all properties in a property file and "activate" the file using e.g.

-DmyAppProperties=file:c:/<configDir>/myApp.properties

If this property is not set, the default myApp.properties file which is provided in the classpath of the application (Maven src/main/resources directory) will be used. The content of this file looks like this

  1 ## ----------------------------------------------------------------------------
  2 ## Example application property file. 
  3 ## All application runtime configuration properties are documented here.
  4 ##
  5 ## Required properties with no default value must be configured in a special
  6 ## property file or by setting corresponding system properties. The resource 
  7 ## reference to a special property file can be set with the system property 
  8 ## 'myAppProperties' e.g. like this:
  9 ##
 10 ##   -DmyAppProperties=file:c:/<confidDir>/myApp.properties
 11 ##
 12 ## ----------------------------------------------------------------------------
 13 
 14 # http proxy server
 15 # -- optional
 16 #httpProxyServer=
 17 
 18 # http proxy port
 19 # -- optional
 20 #httpProxyServer=
 21 
 22 # http connection timeout in milliseconds
 23 # -- required, default: 30000
 24 #httpConnectionTimeout=
 25 
 26 # http receive timeout in milliseconds
 27 # -- required, default: 30000
 28 #httpReceiveTimeout=
 29 
 30 # the URL of the service wee need to access
 31 # -- required
 32 #targetServiceUrl=

You see that no properties are set in here (everything is commented out). In my example application this file is mostly used for documentation purposes and to provide a default file so that it is not required to set the myAppProperties system property on startup.

If the default property file is picked up, the settings of course have to be provided in another way. The nearby solution is to provide them as system properties. As only one property is really required in the example it will be enough to specify

-DtargetServiceUrl=http//foo.bar

during application startup. All other properties will be instantiated with their default values defined in the configuration class.

Starting the application


The newest trend to start Spring Applications is to use Spring Boot. A very simple Main class that uses Spring Boot to start the application looks like this:

  1 /**
  2  * The main class to start the application.
  3  */
  4 public class Main {
  5 
  6   /**
  7    * Start the application. This will just configure the Spring Application
  8    * Context and print the runtime configuration setting.
  9    *
 10    * @param args
 11    */
 12   public static void main(String[] args) {
 13     SpringApplication app = new SpringApplication(AppConfig.class);
 14     app.setShowBanner(false);
 15     ApplicationContext ctx = app.run(args);
 16 
 17     SampleBean sampleBean = ctx.getBean(SampleBean.class);
 18     System.out.println(sampleBean.toString());
 19   }
 20 
 21 }

With Spring Boot it is now even possible to use program arguments to configure the application like this

java Main --targetServiceUrl=http://foo.bar

It is also possible to mix the usage of system properties and program arguments.
Here are some example invocations of the "application" with the output of the configuration settings.

# configuration via system properties
> java -cp lib\*; -DtargetServiceUrl=http://foo.bar Main
SampleBean{httpProxy=null, httpProxyPort=0, httpConnectionTimeout=30000, httpReceiveTimeout=60000, targetServiceUrl=http://foo.bar}

# configuration via program arguments
> java -cp lib\*; Main --targetServiceUrl=http://lorem.ipsum
SampleBean{httpProxy=null, httpProxyPort=0, httpConnectionTimeout=30000, httpReceiveTimeout=60000, targetServiceUrl=http://lorem.ipsum}

# mixed system property / program arguments configuration
> java -cp lib\*; -DhttpConnectionTimeout=15000 Main --targetServiceUrl=http://lorem.ipsum
SampleBean{httpProxy=null, httpProxyPort=0, httpConnectionTimeout=15000, httpReceiveTimeout=60000, targetServiceUrl=http://lorem.ipsum}

Conclusion


The Spring framework offers wide possibilities to support runtime configuration for an application. This includes the possibilities to
  • provide default configuration values whenever possible (convention over configuration)
  • provide runtime settings in a property file
  • provide runtime settings as system properties
  • provide runtime settings as program arguments
The operation team that runs the application can pick up the configuration possibility that fits best to their runtime environment - and should be happy ;-)

Further reading:

  • Sample project of this post on GitHub

No comments:

Post a Comment