Monday, February 20, 2012

Automatically change configuration based on current environment - Part 2

In the first part of this article we talked about the ideas behind detecting the environment. You should check it out if you don't know what I'm talking about.

In this second part we will take a detailed tour on how to use the code I share with you for environment detection and automatic configuration of your web app with Spring 3.1.

The Code


I share both zips with you to use it as you like. I only ask you to respect the authorship.
Profile-detector jars and pom. (Maven projects that enables the environment detection feature)
Example web-app with different configurations (Example web app project with automatic configuration based on the environment)


To make our application environment aware we need to follow the following steps:
1.- Use Spring 3.1
2.- Add the profile-detector dependency to our pom
3.- Modify the web.xml file so we can initialize Spring prior anything else happens
4.- Add integration classes so we can declare environments and sensors

5.- Extend integration class to define you own environment parameters
6.- Select which sensors we are going to use
7.- Define known environment profiles with its sensor values
8.- Create different configurations for our needs
9.- Map each configuration with corresponding environment
10.- Optionally you can add beans for different configurations


Dissecting the example web app

Let's see what's inside the example web app while we follow the steps so you can use the code in it for your own projects.
I will assume you know how to use maven, how to add Spring 3.1.0.RELEASE as dependency, and how to install the profile-detector project on your repository. (Otherwise you should chek: Spring and maven, Install a jar to your repository). So let's skip step 1.


2. Add profile-detector as dependency

Add to your pom:
(Make sure you installed the profile-detector on your maven repository)


3. Add a Spring initializer to the web.xml


To the existing web.xml with Spring defined:


Add an additional context parameter:

This way we tell Spring that it should look for this class, instantiate and use it for its own initialization,. We will use this hook to add the environment detection step.


4. Add integration classes


The classes I added over profile-detector project and integrating it with spring are:

  • AmbientBasedContextInitializerSupport
  • AmbientConfiguration
  • SpringBasedConfigurationSupport

These classes enable the feature of detecting the environment in the initilization phase of Spring.
You should take a look at the core of AmbientBasedContextInitializerSupport. This class does the magic of putting it all together:

As you can see, it makes a call to detect the current "AmbientProfile" which would represent the current environment using the profile-detector code. Then it asks its subclass for an "AmbientConfiguration" which represents the knowledge to adapt the current configuration to match the corresponding AmbientProfile.

5. Extend AmbientBasedContextInitializerSupport


If you payed attention to the web.xml snippet we declared a "com.tenpines.example.ambients.ExampleSpringContextInitializer" class. This class extends the "AmbientBasedContextInitializerSupport" class and enables the application to define its own characteristics.

This class will be instantiated by Spring to auto initialize itself.

6. Select which sensors to use


For the sake of the example I used only one sensor, the "HostnameSensor". This sensor takes the current machine's hostname and is probably the simplest way to detect different environments.
Implementing the "getUsedSensors" you tell which sensors to use:

Every sensor you declare in this method will be used any time the application starts to detect which environmet is current.

There are more predefined sensors you can use, and you can define yours just implementing the interface AmbientSensor. All the sensor have to take a snapshot of some environment variable and express it as a String so it can be compared later:
  • AvailableFileRootsSensor
    It gives you all the file roots available as a single String
  • AvailableProcessorsSensor
    It tells you the number of available processors on the current machine
  • CurrentSytemUserSensor
    Tells the name of the current user as reported by the operating system
  • CurrentWorkingDirSensor
    The name of the current working dir
  • HostIpSensor
    The current ip address
  • MaxMemorySensor
    The max memory available to the running virtual machine
  • OperatingSystemSensor
    A description of the current operating system
  • SystemArchitectureSensor
    A description of the current machine architecture
  • VmDescriptionSensor
    A decription of the running java virtual machine


7. Define your known environments


After selecting the sensors you should teach the application which sensors values should expect for each known environment.
You define different known environemnts (AmbientProfiles) to discriminate where is your code running. This is intentionally separated from the possible configurations (AmbientConfiguration) as a configuration could be shared by different environments (probably in development).


The "HOME_PROFILE" is my home machine profile. You should change the value to your own hostname in the "ALIEN_PROFILE" otherwise you will get and error indicating that the current environment is not known to the application when running the example.

[The last part of the init method declares a map, so we know which configuration goes with each profile]


8. Create different configurations


In this example there are two classes:
  • ExampleHomeAmbientConfiguration
    This will be the configuration for my machine
  • ExampleAlienAmbientConfiguration
    This should be the configuration for your machine

They represent different configurations and also they use different spring profiles for the beans. When one configuration is elected according to the AmbientProfile, Spring is initialized with a specific profile:


Each configuration defines the Spring profile to use when activated:

Build your own configurations according to your needs. I my own project I change the logging options so in development mode I see everything on the STDOUT, but in production it goes to a rolling file.

9. Map each profile with its configuration

This is done in the method "getConfigurationFor" overrided from the "AmbientBasedContextInitializerSupport" class. You should use any criteria. I used a map.

The init method initialized the map for each profile.

10. Use different set of beans for each configuration

Finally and depending on your needs you can define different sets of bean to use for each configuration.
I mapped a bean profile for each configuration. The beans are declared in the main spring configuration file web-application-context.xml.


The syntax is pretty self-explanatory. You can see more on the Spring 3.1 tutorials.


Thanks for watching!


Friday, February 17, 2012

Automatically change configuration based on current environment - Part 1

The problem

Suppose you are developing a web application (I'm doing it right now). You use Spring, Hibernate, Maven and all those good old friends from Java world.

Now you have a production environment and a development environment, and you have 2 developers, each one with different operating systems. So you probably end up with 3 possible configurations (different file locations, connection urls and logging settings) for each of the possible environments (production, developer1, developer2).

How do you manage your configuration settings so any time you run the application the correct configuration applies?

Previous solution

Initially, in previous projects, we used what Maven calls "maven profiles" and "resource filtering".
Basically this means that our configuration files had variables that maven was able to replace with correct values based on the current profile.
For example, we could define a variable "v" that had different values for development, and production environments. Our configuration files used that variable "v" and running maven with the correct profile resulted in v replaced by "A". (You can see more details here)

This approach had 2 main drawbacks:
  1. It's purely manual. We had to run this filtering process by manually specifying which maven profile was adequate.
  2. You have different builds for different environments, so you have to quality test every one of them.
  3. You have an additional build step. You had to make sure that whenever the application is started, the "filtering process" was correctly executed. This is not a problem in production environment because the war is assembled by maven, but it is an annoyance when developing.

New idea


What if the application were aware of its different running environments so it could change its configuration automatically based on the current?

If we wanted to do that, we had to solve 2 issues:

Fortunately for the second issue we have Spring 3.1 which introduced the concept of profiles to bean definitions. This is similar to what maven does to files but applied to beans. (more details)

Now, we can define different set of beans according to the current spring profile. If we structure our configuration in different sets of beans for each desired configuration we can change it easily.
The problem remains in how to detect the current environment to tell which spring profile to use.


Detect current environment


If we were able to determine which spring profile corresponds to the current execution environment we could let the application auto-configure itself.

So how do we detect the current execution environment?

 
We need to add environment sensors to the application, and define which possible environment profiles exist. Then if you know how each sensor measured on each environment profile, when you read current values, you can determine which environment profile is currently valid.


About the decision strategy


To decide which profile is best, I used a fuzzy-like criteria.
Since environments change and it's likely that sensor values do change with them it's necessary that the application tolerates some level of change. I used a threshold approach for this.

When the application has to decide which profile is best, it evaluates each profile against all sensors assigning a score value to the profile. The best scored profile is the candidate for current environment profile, however there is a minimum value that any profile has to score to be accepted.

The rationale for this is that the environment can change, but if it changes a lot, it is another environment.


New Solution


I implemented this ideas in a very small maven project for the environment detection which you can freely use in your own projects. The "profile-detector" project is independent from Spring and can be used in any Java project to detect the current environment. (just the detection part)

Additionally you will want the integration classes that enable a web application with spring framework to auto-change its configuration based on the detected environment. (the configuration part)

In the second part of this article I will tell you the details to use the project and show you the code!

I share both zips with you to use it as you like. I only ask you to respect the authorship.
Profile-detector jars and pom.
Example web-app with different configurations