Working with RSCA using configuration APIs

One of the new features in IIS7 in windows server 2008 enable people to extend existing IIS configuration sections. If you have a schema file in schema folder which defines section already defined in IIS_Schema.xml, IIS configuration system will merge the schema defined in these two files. This enables you to add owner, email, phone properties to sites and have then keep this data under sites section in applicationHost.config. In addition to this, there is a new concept of dynamic properties. These are the properties which doesn’t have static values in configuration files but whose values can be supplied dynamically by some COM components. Dynamic properties can support get/set operations as other configuration properties. Other than get and set, configuration system allows you to define methods in schema which can be called using configuration APIs. These methods can accept input and return output as well. A perfect example which uses all this functionality of configuration system is part of the product itself. All RSCA data which could be earlier obtained using RSCA APIs (appcmd, UI, WMI use RSCA APIs underneath) is now exposed using dynamic properties. For this IIS has rscaext.xml in schema folder which extends existing system.applicationHost/sites and system.applicationHost/applicationPools sections defined in IIS_Schema.xml. Taking example of system.applicationHost/sites section, rscaext.xml has the following to extend it.

<sectionSchema name="system.applicationHost/sites">
  <collection addElement="site">
    <attribute name="state" type="enum" extension="Microsoft.ApplicationHost.RscaExtension">
      <enum name="Starting" value="0" />
      <enum name="Started"  value="1" />
      <enum name="Stopping" value="2" />
      <enum name="Stopped"  value="3" />
      <enum name="Unknown"  value="4" />
    </attribute>
    <method name="Start" extension="Microsoft.ApplicationHost.RscaExtension" />
    <method name="Stop" extension="Microsoft.ApplicationHost.RscaExtension" />
  </collection>
</sectionSchema>

This part of schema tells configuration system that for each collection element ‘site’, there is an additional attribute ‘state’ of type ‘enum’ and its value can be Starting|Started|Stopping|Stopped|Unknown. Additionally there are two methods which are supported for site, ‘Start’ and ‘Stop’. There is an additional property in schema in these attributes and methods which makes them dynamic and that is ‘extension’. This tells the configuration system which COM component to call when someone gets or sets the attribute or call the method defined in schema. For dealing with dynamic properties, additional interfaces which got added in AhAdmin are IAppHostMethodCollection, IAppHostMethod, IAppHostMethodInstance, IAppHostMethodSchema, IAppHostMethodExtension, IAppHostPropertyExtension, IAppHostElementExtension. A sample program which prints state of each site after calling Start and Stop method is written below.

try
{
    var ahadmin = new ActiveXObject("Microsoft.ApplicationHost.AdminManager");

    //
    // Get sites section
    //
    var sitesSection = ahadmin.GetAdminSection(
        "system.applicationHost/sites",
        "MACHINE/WEBROOT/APPHOST");

    //
    // Go through all sites
    //
    var sitesCollection = sitesSection.Collection;
    for(var i = 0; i < sitesCollection.Count; i++)
    {
        var siteElement = sitesCollection.Item(i);

        //
        // Print site name and its current state
        //
        var nameProperty = siteElement.GetPropertyByName("name");
        WScript.Echo("Site - " + nameProperty.Value);

        var stateProperty = siteElement.GetPropertyByName("state");
        var statePropertySchema = stateProperty.Schema;
        var possibleValues = statePropertySchema.PossibleValues;
        WScript.Echo("  State = " + possibleValues.Item(stateProperty.Value).Name);

        //
        // Get Start and Stop methods as in rscaext.xml
        //
        var methodsCollection = siteElement.Methods;
        var stopMethod = methodsCollection.Item("Stop");
        var startMethod = methodsCollection.Item("Start");

        //
        // Execute method Stop and print state
        //
        WScript.Echo("  Executing " + stopMethod.Name);
        stopMethod.CreateInstance().Execute();
        WScript.Echo("  State = " + possibleValues.Item(stateProperty.Value).Name);

        //
        // Execute method Start and print state
        //
        WScript.Echo("  Executing " + startMethod.Name);
        startMethod.CreateInstance().Execute();
        WScript.Echo("  State = " + possibleValues.Item(stateProperty.Value).Name);
    }
}
catch(e)
{
  WScript.Echo(e.number);
  WScript.Echo(e.description);
}

Site Start and Stop method definitions don’t specify inputElement and outputElement as these methods don’t accept any input parameters and don’t return anything. For an example of a method which accepts input and gives output, see schema of GetRequests and GetCustomData methods. Sample below shows how to call GetRequests method to prints all requests in progress with timeElapsed > 5000msec.

try
{
    var ahadmin = new ActiveXObject("Microsoft.ApplicationHost.AdminManager");

    //
    // Get applicationPools section
    //
    var appPoolsSection = ahadmin.GetAdminSection(
        "system.applicationHost/applicationPools",
         "MACHINE/WEBROOT/APPHOST");

    //
    // Go through all apppools and print
    // currently executing requests
    //
    var appPoolsCollection = appPoolsSection.Collection;
    for(var i = 0; i < appPoolsCollection.Count; i++)
    {
        var appPoolElement = appPoolsCollection.Item(i);

        //
        // Print applicationPool name and state
        //
        var appPoolName = appPoolElement.GetPropertyByName("name").Value;
        var appPoolStateProperty = appPoolElement.GetPropertyByName("state");
        var appPoolStateName = appPoolStateProperty.Schema.PossibleValues.Item(appPoolStateProperty.Value).Name;
        WScript.Echo("Application Pool - " + appPoolName + " (" + appPoolStateName + ")");

        //
        // Get worker process serving this application pool
        //
        var workerProcessesElement = appPoolElement.ChildElements.Item("workerProcesses");
        var workerProcessCollection = workerProcessesElement.Collection;

        //
        // Go through worker process collection
        // and execute GetRequests method for each
        //
        for(var j = 0; j < workerProcessCollection.Count; j++)
        {
            var workerProcess = workerProcessCollection.Item(j);
            WScript.Echo("  Worker process - " + workerProcess.GetPropertyByName("processId").Value);

            var getRequestsMethod = workerProcess.Methods.Item("GetRequests");
            var getRequestsMethodInstance = getRequestsMethod.CreateInstance();

            //
            // Set timeElapsedFilter to 5000 to get requests with
            // time elapsed > 5000 msec. To get all the currently
            // executing requests, leave default value of 0
            //
            var inputElement = getRequestsMethodInstance.Input;
            inputElement.GetPropertyByName("timeElapsedFilter").Value = 5000;

            getRequestsMethodInstance.Execute();

            var output = getRequestsMethodInstance.Output;
            var requestsCollection = output.Collection;

            for(var i = 0; i < requestsCollection.Count; i++)
            {
                WScript.Echo("    Request");
                var requestPropertiesCollection = requestsCollection.Item(i).Properties;

        
        for (var j = 0; j < requestPropertiesCollection.Count; j++)
                {
                    var requestProperty = requestPropertiesCollection.Item(j);
                    WScript.Echo("      " + requestProperty.Name + "=" +
                        requestProperty.StringValue);
                }
            }
        }
    }
}
catch(e)
{
    WScript.Echo(e.number);
    WScript.Echo(e.description);
}

Hope this helps.

Kanwal

2 Comments

  • Hi Kanwal,

    This is a great sample, thanks for publishing it!

    One comment - this sample should use Microsoft.ApplicationHost.AdminManager instead, since it doesnt require actually writing to the configuration store. This will reduce the amount of memory occupied by the config system, and in the future prevent any other kind of file locking / write-related resource usage. Customers should use read-only config systems where possible.

    Thanks,

    Mike

  • Thanks Mike. I made the change in the sample.

Comments have been disabled for this content.