|
//------------------------------------------------------------------------------
// <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.CreatePublicInstanceByWebObjectActivator(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.CreatePublicInstanceByWebObjectActivator(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();
}
}
}
}
|