JOIN
Get Time
soft_tut   
Using the .Net Configuration Manager

By marius_neo
TopCoder Member

Over the last 60 days, the Java version of the Configuration Manager component has been downloaded more often than any other component in TopCoder's catalog. Just a few spaces behind it in the Top 10 most-downloaded list is the .NET version of Configuration Manager.

Most TopCoder members who have participated in at least one .NET design or development competition will tell you that the first component they needed to learn was the Configuration Manager. Simply put, it’s a key to unlocking the potential of the component catalog, because it abstracts access to the configuration data on almost every single .NET component you'll ever need to use. In this article, we'll look at the functionality that makes this component so powerful.

Because the implementation details of Configuration Manager are already presented in the component specifications, this article will focus on the following topics:

  • Where to use the Configuration Manager component
  • Overview of component API
  • Discussion on the functionalities provided
  • How to plug this component into your application.

The target audience for this article is TopCoder members who are new to component competitions, or who are interested in taking their first steps into the component arena. If you're more experienced and don't need all the explanations on the component, you might still find the section on configuring your application useful – if so, feel free to skip over the next few sections and go directly to 'How to plug this component into your application'.

Where to use Configuration Manager
As stated in its Requirements Specification, the purpose of the Configuration Manager is to centralize the management of, and access to, the configuration data. This component is almost ubiquitous in the TopCoder .NET Catalog because of this functionality.

Common examples where this component should be used are libraries that need configuration for the external entities with whom they are communicating (like an FTP, SMTP server address and port or a database connections string), or libraries that need to distinguish items that change, depending on which machine they are used, from items that stay the same.

Some of the most popular TopCoder components that benefit from the power provided by this component include:

  • Connection Factory, which decouples a particular database implementation from applications and other components by providing a layer of abstraction around the creation of a database connection.
  • Object Factory, which provides a standard interface to create objects based on configuration settings or some other specifications.

Overview of the component API
In this section we'll introduce how the component should be used, by presenting a series of code samples focused on the usage of the .NET Configuration Manager API.

In order to take advantage of the configuration properties we need to load them first. The component provides a comfort method for developers, which helps load a new configuration file. The component’s internal mechanisms will recognize the configuration file’s extension and use the proper handler to load the file.

const string iniConfigFilePath = @"..\..\conf\Email_Engine.ini"
const string xmlConfigFilePath = @"..\..\conf\Message_Persistence.xml";
// Retrieve the Configuration Manager instance
ConfigManager cm = ConfigManager.GetInstance();
cm.LoadFile(iniConfigFilePath);
cm.LoadFile(xmlConfigFilePath);

Retrieving property values is pretty straightforward. By specifying the namespace and the property name we can obtain the property value(s) needed. Starting from the second version of this component there have appeared a set of comfort methods for retrieving long or double values from configuration (internally the Configuration Manager contains DefaultStringConverter class, which takes care of converting from string to the needed type).

Another feature introduced in the second version of the component is the possibility to retrieve property values with a parsing preference. The parsing preference refers to constraints we can declare when retrieving a property value like:

  • property is required to exist in the configuration
  • property is required to have a non-empty/non-blank string value.
try

{
    // Retrieve the Configuration Manager instance
    ConfigManager cm = ConfigManager.GetInstance();

    // Verify if the configNamespace exists
    if (cm.GetNamespace(configNamespace) == null)
    {
    StringBuilder msg = new StringBuilder();
    msg.Append("The configuration manager namespace ")
    .Append(configNamespace).Append(" is missing");
    throw new ConfigurationException(msg.ToString()); 
    }

    smtpPort = cm.GetIntValue(configNamespace, smtpPortPropertyName);
    // A common practice for making a more robust reading mechanism    
    // from configuration is to check if the parameters are missing or 
    // if they are empty/blank strings. A ParsingPreferenceException    
    // will be thrown if the specified preferences for the value are 
    // not respected.
    // In the case of GetIntValue/GetDoubleValue methods this check is   
    // made by default.
    ParsingPreference smtpAddressparsingPreference = new ParsingPreference();
    smtpAddressparsingPreference.AllowNull = false;
    smtpAddressparsingPreference.AllowEmpty = false;
    smtpAddressparsingPreference.TrimSpace = true;

    smtpAddress = cm.GetValue(configNamespace,     
    smtpAddressPropertyName,smtpAddressparsingPreference);
}
catch (ConfigurationException)
{
    throw;
}
catch (Exception ex)
{
    // All exceptions thrown by ConfigManager are wrapped into a  
    // exception used in the application for denoting a configuration
    // problem. Note that ConfigurationException is not part of  
    // Configuration Manager component's assembly.
    throw new ConfigurationException(
        "There is a problem with the configuration : "
            + ex.Message, ex);
}

Configuration Manager is also used in unit tests of components. In this manner, multiple testing environments are simulated with writing less code. An interesting detail worth noting here is that, after running a test case, the testing environment that was previously created must be cleaned in order to avoid possible inconsistencies when running successive test cases.

/// <summary>
/// Releases all resources used in testing. Clears all the namespaces 
/// from the Configuration Manager.
/// </summary>
[TearDown]
public void TearDown()
{
    ConfigManager cm = ConfigManager.GetInstance();
    // By making this method call all the namespace are cleared and by 
    // specifying <c>preload</c> property as false the preload 
    // files(specified usually in "..\conf\preload.xml" file will not   
    // be reloaded.
    cm.Clear(false);
}

/// <summary>
/// Tests the constructor of the <c>MockEmailEvengine</c> class when 
/// the configuration namespace is loaded from a <c>.ini</c> file.
/// </summary>
 [Test]
public void TestConstructorInitializedWithIniConfigFile()
{
    ConfigManager cm = ConfigManager.GetInstance();
    cm.LoadFile(iniConfigFilePath);
    MockEmailEngine instance = new MockEmailEngine(ConfigNamespace);
    Assert.IsNotNull(instance, "The instance was not created");
}

Configuration Manager functionality
This component provides the possibility to read property values from different types of configuration files like:

  • .ini files
  • .xml files
  • .mxml files (which contain the description of property retrieval handler from an alternative data source).

You can examine the formats of those files on the attached demo provided by this article. Note that a configuration file can contain one or more namespaces declaration.

This component is almost a replicate of the component with the same name developed in Java. There are a few interesting differences between those two components, though. For example in Java we can have imbricate properties, like in the following configuration sample code:

<Property name="transition1">
  <Property name="startState">
    <Value>state1</Value>
  </Property>
  <Property name="endState">
    <Value>state2</Value>
  </Property>
  <Property name="input">
    <Value>input1</Value>
  </Property>
  <Property name="actionNames">
    <Value>sendmail</Value>
  </Property>
</Property>

This feature is not available in the .NET version of the component, where properties must be coded in a custom manner:

<Property name="transition1_startState">
    <Value>state1</Value>
</Property>
<Property name="transition1_endState">
  <Value>state2</Value>
</Property>
<Property name="transition_input">
  <Value>input1</Value>
</Property>
<Property name="transition_actionNames">
    <Value>sendmail</Value>
</Property>

This component implements the Singleton design pattern so there is no reason for you to include a ConfigManager parameter type in your method calls as a parameter. The way to obtain the only instance of this class is: ConfigManager cm = ConfigManager.GetInstance();

Another functionality provided by this component is its ability to preload configuration files when they are specified in @"..\..\conf\preload.xml" configurations file of the component, or on the file denoted by "TopCoder.Util.ConfigurationManager.Preload" property name in the application configuration file. Here is an example of how to fill out the configuration files to be preloaded:

<?xml version="1.0" encoding="UTF-8"?>

<ConfigManager>
  <namespace name="TopCoder.Util.ConfigurationManager">
    <property name="preload">
      <!mdash;
           All the values of this property represent configuration  
           files to be preloaded.
      -->
      <value>..\..\test_files\test1.xml</value>
      <value>..\..\test_files\test2.xml</value>
    </property>
  </namespace>
</ConfigManager>

How to plug this component into your application
In this section, we'll focus on the necessary steps required to use this component in your application. The demo contains a build file, which can be used in order to compile and run the code.

Add TopCoder.Util.ConfigurationManager.dll as reference for your application
In order to use the Configuration Manager component in your source code you need to add it as a reference library for your application. In the demo, the library is located in ".\lib\tcs" directory. Note that you should download at least version 2.0.1 of the component in order to have the demo and the tests run without problems.

Configure preloaded files
When using this component you must remember to have either @"..\..\conf\preload.xml" file relative to the place where your application is executed, or the application configuration file must contain the "TopCoder.Util.ConfigurationManager.Preload" property, which should contain as a value the configurations file path for preloaded files of this component.

Running the demo
The demo application represents a Mock Email Engine, which is supposed to send messages to addresses found in configuration. This application doesn’t really send any mail -- its sole purpose is proving the utility of Configuration Manager.

Essentially, the demo application's Email Engine console displays the SMTP server through which the mock email message will be sent. The content of the Email Engine console will contain the configuration source from where the property values are read and the address:port for the SMTP server read from the configuration. You'll notice that if you change the property values in the configuration, and then restart the application, your changes will be reflected in the content of the Email Engine console.

Before running the demo application you should verify that you have the .NET Framework 2.0 installed on your machine. In order to run the demo you simply need unzip the demo file and then run the TopCoder.ConfigManagerTutorial.exe file located in the build\classes directory.

If you make modifications on the source files of the demo application and you have NAnt tool installed on machine you simply need to call nant rundemo to test your changes.

Good luck!