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!

No comments:

Post a Comment