File: HealthMonitoringSectionHelper.cs

package info (click to toggle)
mono 4.6.2.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 778,148 kB
  • ctags: 914,052
  • sloc: cs: 5,779,509; xml: 2,773,713; ansic: 432,645; sh: 14,749; makefile: 12,361; perl: 2,488; python: 1,434; cpp: 849; asm: 531; sql: 95; sed: 16; php: 1
file content (609 lines) | stat: -rw-r--r-- 26,603 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
//------------------------------------------------------------------------------
// <copyright file="HealthMonitoringSectionHelper.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace System.Web.Configuration {
    using System;
    using System.Xml;
    using System.Configuration;
    using System.Collections.Specialized;
    using System.Collections;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.ComponentModel;
    using System.Web.Hosting;
    using System.Web.Util;
    using System.Web.Configuration;
    using System.Web.Management;
    using System.Web.Compilation;
    using System.Security.Permissions;

    internal class HealthMonitoringSectionHelper {
        static HealthMonitoringSectionHelper s_helper;

        static RuleInfoComparer s_ruleInfoComparer = new RuleInfoComparer();

        HealthMonitoringSection _section;
        internal ProviderInstances _providerInstances;
        internal Hashtable _customEvaluatorInstances;
        internal ArrayList _ruleInfos;
        bool _enabled;

        // Cached matched rules for system events.  For details, see comments for s_eventArrayDimensionSizes
        // in WebEventCodes.cs
        static ArrayList[,] _cachedMatchedRules;

#if DBG
        // Because we assume no two different event type will use the same event code, in debug mode
        // we use this hashtable to make sure our assumption is true.
        Hashtable                       _cachedTypeOfMatchedRulesSystem = new Hashtable();
#endif

        // Cached matched rules based on WebBaseEvent Hashcode, and is for non-system events.
        Hashtable _cachedMatchedRulesForCustomEvents;

        static internal HealthMonitoringSectionHelper GetHelper() {
            if (s_helper == null) {
                s_helper = new HealthMonitoringSectionHelper();
            }

            return s_helper;
        }

        HealthMonitoringSectionHelper() {

            // Handle config exceptions so we can still log messages to the event log.
            try {
                _section = RuntimeConfig.GetAppConfig().HealthMonitoring;
            }
            catch(Exception e) {
                // If InitializationException has not already been set, then this exception
                // is happening because the <healthMonitoring> section has an error.
                // By setting InitializationException, we allow the exception to be displayed in the response.
                // If InitializationException is already set, ignore this exception so we can 
                // display the original in the response.
                if (HttpRuntime.InitializationException == null) {
                    HttpRuntime.InitializationException = e;
                }
                _section = RuntimeConfig.GetAppLKGConfig().HealthMonitoring;
                // WOS 1965670: if we fail to get the section throw the previous error
                if (_section == null) {
                    throw;
                }
            }

            _enabled = _section.Enabled;

            if (!_enabled) {
                return;
            }

            // First run some basic sanity check
            BasicSanityCheck();

            // Init some class members
            _ruleInfos = new ArrayList();
            _customEvaluatorInstances = new Hashtable();
            _providerInstances = new ProviderInstances(_section);
            _cachedMatchedRulesForCustomEvents = new Hashtable(new WebBaseEventKeyComparer());
            _cachedMatchedRules = new ArrayList[WebEventCodes.GetEventArrayDimensionSize(0),
                                                        WebEventCodes.GetEventArrayDimensionSize(1)];

            BuildRuleInfos();

            _providerInstances.CleanupUninitProviders();
        }

        internal bool Enabled {
            get { return _enabled; }
        }

        internal HealthMonitoringSection HealthMonitoringSection {
            get { return _section; }
        }

        [FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
        void BasicSanityCheck() {
            Type type;

            foreach (ProviderSettings providerSettings in _section.Providers) {
                // Make sure the type is valid.
                type = ConfigUtil.GetType(providerSettings.Type, "type", providerSettings);

                // Make sure the type support WebEventProvider
                HandlerBase.CheckAssignableType(providerSettings.ElementInformation.Properties["type"].Source,
                        providerSettings.ElementInformation.Properties["type"].LineNumber,
                        typeof(WebEventProvider), type);

            }

            foreach (EventMappingSettings eventMappingSettings in _section.EventMappings) {
                // Make sure the type is valid.
                type = ConfigUtil.GetType(eventMappingSettings.Type, "type", eventMappingSettings);

                // Make sure startEventCode <= endEventCode
                if (!(eventMappingSettings.StartEventCode <= eventMappingSettings.EndEventCode)) {
                    string attribute;

                    // We don't know which one was specified unless we test it
                    attribute = "startEventCode";
                    if (eventMappingSettings.ElementInformation.Properties[attribute].LineNumber == 0) {
                        attribute = "endEventCode";
                        Debug.Assert(eventMappingSettings.ElementInformation.Properties[attribute].LineNumber != 0,
                                    "eventMappingSettings.ElementInformation.Properties[attribute].LineNumber != 0");
                    }

                    throw new ConfigurationErrorsException(
                        SR.GetString(SR.Event_name_invalid_code_range),
                        eventMappingSettings.ElementInformation.Properties[attribute].Source, eventMappingSettings.ElementInformation.Properties[attribute].LineNumber);
                }

                // Make sure the type support WebBaseEvent
                HandlerBase.CheckAssignableType(eventMappingSettings.ElementInformation.Properties["type"].Source,
                            eventMappingSettings.ElementInformation.Properties["type"].LineNumber,
                            typeof(System.Web.Management.WebBaseEvent), type);

                // It's a valid type.  Might as well save it.
                eventMappingSettings.RealType = type;
            }

            foreach (RuleSettings rule in _section.Rules) {

                // Go thru all the Rules, and make sure all referenced provider, eventName
                // and profile exist.

                string provider = rule.Provider;
                if (!String.IsNullOrEmpty(provider)) {
                    ProviderSettings providerSettings = _section.Providers[provider];
                    if (providerSettings == null) {
                        throw new ConfigurationErrorsException(
                            SR.GetString(SR.Health_mon_provider_not_found, provider),
                                rule.ElementInformation.Properties["provider"].Source,
                                rule.ElementInformation.Properties["provider"].LineNumber);
                    }
                }

                string profile = rule.Profile;
                if (!String.IsNullOrEmpty(profile)) {
                    if (_section.Profiles[profile] == null) {
                        throw new ConfigurationErrorsException(
                            SR.GetString(SR.Health_mon_profile_not_found, profile),
                                rule.ElementInformation.Properties["profile"].Source,
                                rule.ElementInformation.Properties["profile"].LineNumber);
                    }
                }

                if (_section.EventMappings[rule.EventName] == null) {
                    throw new ConfigurationErrorsException(
                        SR.GetString(SR.Event_name_not_found, rule.EventName),
                            rule.ElementInformation.Properties["eventName"].Source, rule.ElementInformation.Properties["eventName"].LineNumber);
                }

            }
        }

        void DisplayRuleInfo(RuleInfo ruleInfo) {
#if DEBUG
            Debug.Trace("BuildRuleInfos", "====================== Rule Info =======================");
            Debug.Trace("BuildRuleInfos", "name:" + ruleInfo._ruleSettings.Name);
            Debug.Trace("BuildRuleInfos", "type:" + ruleInfo._eventMappingSettings.RealType.Name);
            Debug.Trace("BuildRuleInfos", "minInstances:" + ruleInfo._minInstances);
            Debug.Trace("BuildRuleInfos", "maxLimit:" + ruleInfo._maxLimit);
            Debug.Trace("BuildRuleInfos", "minInterval:" + ruleInfo._minInterval);
            Debug.Trace("BuildRuleInfos", "provider:" + ruleInfo._ruleSettings.Provider);
            Debug.Trace("BuildRuleInfos", "referenced provider:" + (ruleInfo._referencedProvider == null ? String.Empty : ruleInfo._referencedProvider.GetType().Name));
            Debug.Trace("BuildRuleInfos", "=========================================================");
#endif
        }

        void BuildRuleInfos() {
            Debug.Trace("BuildRuleInfos", "BuildRuleInfos called");

            // Each ruleInfo is an object that takes the information
            // stored in a ruleSettings and merge it with values from profileSettings.

            // At the end, we'll sort the rules based on type (most specific type last)

            foreach (RuleSettings ruleSettings in _section.Rules) {
                RuleInfo ruleInfo = CreateRuleInfo(ruleSettings);
                DisplayRuleInfo(ruleInfo);
                _ruleInfos.Add(ruleInfo);
            }

            _ruleInfos.Sort(s_ruleInfoComparer);
        }

        RuleInfo CreateRuleInfo(RuleSettings ruleSettings) {
            RuleInfo ruleInfo = new RuleInfo(ruleSettings, _section);

            // Inherit values from profile
            MergeValuesWithProfile(ruleInfo);

            // Find out which provider it's referencing
            InitReferencedProvider(ruleInfo);

            // Initialize the cutom evaluator type
            InitCustomEvaluator(ruleInfo);

            return ruleInfo;
        }

        void InitReferencedProvider(RuleInfo ruleInfo) {
            String providerName;
            WebEventProvider provider;

            Debug.Assert(ruleInfo._referencedProvider == null, "ruleInfo._referencedProvider == null");

            providerName = ruleInfo._ruleSettings.Provider;
            if (String.IsNullOrEmpty(providerName)) {
                return;
            }

            provider = _providerInstances[providerName];
            Debug.Assert(provider != null, "provider != null");

            ruleInfo._referencedProvider = provider;
        }

        void MergeValuesWithProfile(RuleInfo ruleInfo) {
            ProfileSettings profileSettings = null;

            if (ruleInfo._ruleSettings.ElementInformation.Properties["profile"].ValueOrigin != PropertyValueOrigin.Default) {
                profileSettings = _section.Profiles[ruleInfo._ruleSettings.Profile];
                Debug.Assert(profileSettings != null, "profileSettings != null");
            }

            if (profileSettings != null && ruleInfo._ruleSettings.ElementInformation.Properties["minInstances"].ValueOrigin == PropertyValueOrigin.Default) {
                ruleInfo._minInstances = profileSettings.MinInstances;
            }
            else {
                ruleInfo._minInstances = ruleInfo._ruleSettings.MinInstances;
            }

            if (profileSettings != null && ruleInfo._ruleSettings.ElementInformation.Properties["maxLimit"].ValueOrigin == PropertyValueOrigin.Default) {
                ruleInfo._maxLimit = profileSettings.MaxLimit;
            }
            else {
                ruleInfo._maxLimit = ruleInfo._ruleSettings.MaxLimit;
            }

            if (profileSettings != null && ruleInfo._ruleSettings.ElementInformation.Properties["minInterval"].ValueOrigin == PropertyValueOrigin.Default) {
                ruleInfo._minInterval = profileSettings.MinInterval;
            }
            else {
                ruleInfo._minInterval = ruleInfo._ruleSettings.MinInterval;
            }

            if (profileSettings != null && ruleInfo._ruleSettings.ElementInformation.Properties["custom"].ValueOrigin == PropertyValueOrigin.Default) {
                ruleInfo._customEvaluator = profileSettings.Custom;
                ruleInfo._customEvaluatorConfig = profileSettings;
            }
            else {
                ruleInfo._customEvaluator = ruleInfo._ruleSettings.Custom;
                ruleInfo._customEvaluatorConfig = ruleInfo._ruleSettings;
            }
        }

        void InitCustomEvaluator(RuleInfo ruleInfo) {
            string customEvaluator = ruleInfo._customEvaluator;

            if (customEvaluator == null ||
                customEvaluator.Trim().Length == 0) {
                ruleInfo._customEvaluatorType = null;
                return;
            }

            ruleInfo._customEvaluatorType = ConfigUtil.GetType(ruleInfo._customEvaluator,
                "custom", ruleInfo._customEvaluatorConfig);

            // Make sure the type support WebBaseEvent
            HandlerBase.CheckAssignableType(ruleInfo._customEvaluatorConfig.ElementInformation.Properties["custom"].Source,
                    ruleInfo._customEvaluatorConfig.ElementInformation.Properties["custom"].LineNumber,
                    typeof(System.Web.Management.IWebEventCustomEvaluator), ruleInfo._customEvaluatorType);

            // Create a public instance of the custom evaluator
            if (_customEvaluatorInstances[ruleInfo._customEvaluatorType] == null) {
                _customEvaluatorInstances[ruleInfo._customEvaluatorType] = HttpRuntime.CreatePublicInstance(ruleInfo._customEvaluatorType);
            }
        }

        // Find the corresponding array of RuleInfo based on the fired event
        internal ArrayList FindFiringRuleInfos(Type eventType, int eventCode) {
            ArrayList foundFiringRuleInfos;
            bool systemEvent = eventCode < WebEventCodes.WebExtendedBase;
            CustomWebEventKey customWebEventKey = null;
            object lockObject;
            int index0 = 0, index1 = 0;

#if DBG
            if (systemEvent) {
                Type    type;
                
                type = (Type)_cachedTypeOfMatchedRulesSystem[eventCode];
                if (type == null) {
                    lock(_cachedTypeOfMatchedRulesSystem) {
                        type = (Type)_cachedTypeOfMatchedRulesSystem[eventCode];
                        if (type == null) {
                            _cachedTypeOfMatchedRulesSystem[eventCode] = eventType;
                        }
                    }
                }

                if (type != null) {
                    Debug.Assert(type == eventType, 
                        "For system events, we assume each event code will map only to one event type. " +
                        "Eventcode= " + eventCode + "; stored type= " + type.ToString() +
                        "; raised event type= " + eventType);
                }
            }
#endif

            // First, we look at the cache to see if we find the array.
            if (systemEvent) {
                WebEventCodes.GetEventArrayIndexsFromEventCode(eventCode, out index0, out index1);
                foundFiringRuleInfos = _cachedMatchedRules[index0, index1];
            }
            else {
                customWebEventKey = new CustomWebEventKey(eventType, eventCode);
                foundFiringRuleInfos = (ArrayList)_cachedMatchedRulesForCustomEvents[customWebEventKey];
            }

            if (foundFiringRuleInfos != null) {
                return foundFiringRuleInfos;
            }

            if (systemEvent) {
                lockObject = _cachedMatchedRules;
            }
            else {
                lockObject = _cachedMatchedRulesForCustomEvents;
            }

            lock (lockObject) {

                if (systemEvent) {
                    foundFiringRuleInfos = _cachedMatchedRules[index0, index1];
                }
                else {
                    Debug.Assert(customWebEventKey != null);
                    foundFiringRuleInfos = (ArrayList)_cachedMatchedRulesForCustomEvents[customWebEventKey];
                }

                if (foundFiringRuleInfos != null) {
                    return foundFiringRuleInfos;
                }

                // Not found in cache.

                ArrayList matchedRules = new ArrayList();

                // Go thru the sorted ruleInfo array and look for matching ruleInfo,
                // starting from the most specific type.
                for (int i = _ruleInfos.Count - 1; i >= 0; i--) {
                    RuleInfo curRule = (RuleInfo)_ruleInfos[i];

                    // Now see if the current rule matches the raised event
                    if (curRule.Match(eventType, eventCode)) {
                        matchedRules.Add(new FiringRuleInfo(curRule));
                    }
                }

                // Then for each matched rule, we need to figure out if the provider it
                // uses is also used by other rules.  We need this info because if multiple rules are
                // using the same provider, we fire the event to the provider only once.
                int count = matchedRules.Count;
                for (int i = 0; i < count; i++) {
                    FiringRuleInfo info1 = (FiringRuleInfo)matchedRules[i];

                    if (info1._ruleInfo._referencedProvider != null) {
                        for (int j = i + 1; j < count; j++) {

                            FiringRuleInfo info2 = (FiringRuleInfo)matchedRules[j];
                            if (info2._ruleInfo._referencedProvider != null &&    // ignore null-provider
                                info2._indexOfFirstRuleInfoWithSameProvider == -1 &&  // ignore rules that were marked already
                                info1._ruleInfo._referencedProvider == info2._ruleInfo._referencedProvider) {   // they are pointing to the same provider

                                // We'll remember the index of the first rule info that share the same
                                // provider. For details on how this index is used, please see
                                // WebBaseEvent.RaiseInternal.
                                if (info1._indexOfFirstRuleInfoWithSameProvider == -1) {
                                    info1._indexOfFirstRuleInfoWithSameProvider = i;
                                }

                                info2._indexOfFirstRuleInfoWithSameProvider = i;

                            }
                        }
                    }
                }


#if DBG
                Debug.Trace("FindRuleInfos", "------------------------------------------------");
                Debug.Trace("FindRuleInfos", "Find ruleInfos for event with type=" + eventType.ToString() +
                    ", EventCode=" + eventCode);
                
                foreach(FiringRuleInfo info in matchedRules) {
                    Debug.Trace("FindRuleInfos", "Provider=" + info._ruleInfo._ruleSettings.Provider +
                        "; eventNameType=" + info._ruleInfo._eventMappingSettings.RealType.ToString() +
                        "; _indexOfFirstRuleInfoWithSameProvider=" + info._indexOfFirstRuleInfoWithSameProvider);
                }
                Debug.Trace("FindRuleInfos", "------------------------------------------------");
#endif

                // save matchedRules in the cache
                if (systemEvent) {
                    _cachedMatchedRules[index0, index1] = matchedRules;
                }
                else {
                    Debug.Assert(customWebEventKey != null);
                    _cachedMatchedRulesForCustomEvents[customWebEventKey] = matchedRules;
                }

                return matchedRules;
            }
        }

        internal class RuleInfo {
            internal string _customEvaluator;
            internal ConfigurationElement _customEvaluatorConfig;

            // The following properties are cached here for performance reason
            internal int _minInstances;
            internal int _maxLimit;
            internal TimeSpan _minInterval;   // in seconds

            internal RuleSettings _ruleSettings;

            internal WebEventProvider _referencedProvider;

            internal Type _customEvaluatorType;

            internal EventMappingSettings _eventMappingSettings;

            internal RuleFiringRecord _ruleFiringRecord;

            internal RuleInfo(RuleSettings ruleSettings, HealthMonitoringSection section) {
                _eventMappingSettings = section.EventMappings[ruleSettings.EventName];

                _ruleSettings = ruleSettings;
                _ruleFiringRecord = new RuleFiringRecord(this);
            }

            internal bool Match(Type eventType, int eventCode) {
                // Fail if the type doesn't match.
                if (!(eventType.Equals(_eventMappingSettings.RealType) ||
                       eventType.IsSubclassOf(_eventMappingSettings.RealType))) {
                    return false;
                }

                // Fail if the event code doesn't match
                if (!(_eventMappingSettings.StartEventCode <= eventCode &&
                        eventCode <= _eventMappingSettings.EndEventCode)) {
                    return false;
                }

                return true;
            }
        }

        internal class FiringRuleInfo {
            internal RuleInfo _ruleInfo;
            internal int _indexOfFirstRuleInfoWithSameProvider;

            internal FiringRuleInfo(RuleInfo ruleInfo) {
                _ruleInfo = ruleInfo;
                _indexOfFirstRuleInfoWithSameProvider = -1;
            }
        }

        internal class ProviderInstances {
            internal Hashtable _instances; // case-insensitive because the providers collection is too.

            [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
            internal ProviderInstances(HealthMonitoringSection section) {
                // Build the array of providers
                // Don't create an instance yet, but only store the providerInfo in the HashTable.
                _instances = CollectionsUtil.CreateCaseInsensitiveHashtable(section.Providers.Count);

                foreach (object obj in section.Providers) {
                    ProviderSettings settings = (ProviderSettings)obj;

                    // Please note we are storing the ProviderSettings in the hashtable.
                    // But if we create an instance of that provider, we will replace
                    // that string with a provider object.
                    _instances.Add(settings.Name, settings);
                }
            }

            WebEventProvider GetProviderInstance(string providerName) {
                WebEventProvider provider;
                object o;

                o = _instances[providerName];
                if (o == null) {
                    return null;
                }

                ProviderSettings providerSettings = o as ProviderSettings;

                if (providerSettings != null) {
                    // If what we got is still a ProviderSettings, it means we haven't created an instance
                    // of it yet.
                    Type type;
                    string typeName = providerSettings.Type;

                    type = BuildManager.GetType(typeName, false);
                    Debug.Assert(type != null, "type != null");

                    if (typeof(IInternalWebEventProvider).IsAssignableFrom(type)) {
                        provider = (WebEventProvider)HttpRuntime.CreateNonPublicInstance(type);
                    }
                    else {
                        provider = (WebEventProvider)HttpRuntime.CreatePublicInstance(type);
                    }

                    using (new ProcessImpersonationContext()) {
                        try {
                            provider.Initialize(providerSettings.Name, providerSettings.Parameters);
                        }
                        catch (ConfigurationErrorsException) {
                            throw;
                        }
                        catch (ConfigurationException e) {
                            throw new ConfigurationErrorsException(e.Message, providerSettings.ElementInformation.Properties["type"].Source,
                                providerSettings.ElementInformation.Properties["type"].LineNumber);
                        }
                        catch {
                            throw;
                        }
                    }

                    Debug.Trace("ProviderInstances", "Create a provider instance: " +
                        "name=" + providerSettings.Name + ";type=" + typeName);

                    _instances[providerName] = provider;
                }
                else {
                    provider = o as WebEventProvider;
                    Debug.Assert(provider != null, "provider != null");
                }

                return provider;
            }

            internal WebEventProvider this[String name] {
                get {
                    return GetProviderInstance(name);
                }
            }

            // Cleanup each provider for which we have NOT created an instance.
            internal void CleanupUninitProviders() {
                ArrayList list = new ArrayList();

                foreach (DictionaryEntry de in _instances) {
                    if (de.Value is ProviderSettings) {
                        list.Add(de.Key);
                    }
                }

                foreach (object o in list) {
                    Debug.Trace("ProviderInstances", "Remove " + (string)o + " from providers");
                    _instances.Remove(o);
                }
            }

            internal bool ContainsKey(string name) {
                return _instances.ContainsKey(name);
            }

            public IDictionaryEnumerator GetEnumerator() {
                return _instances.GetEnumerator();
            }
        }
    }
}