diff --git a/Source/BlueCollar/MachineProxy.cs b/Source/BlueCollar/MachineProxy.cs index 1a85c83..86d3e9d 100644 --- a/Source/BlueCollar/MachineProxy.cs +++ b/Source/BlueCollar/MachineProxy.cs @@ -9,9 +9,9 @@ namespace BlueCollar using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Globalization; using System.Linq; using System.Reflection; - using System.Text.RegularExpressions; using System.Web; /// @@ -19,12 +19,13 @@ namespace BlueCollar /// public sealed class MachineProxy : MarshalByRefObject, IDisposable { - private static string[] assemblyBlackList = new string[] { "/^system/", "/^microsoft" }; - + private static readonly string[] AssemblyBlacklist = new string[] { "System.", "Microsoft." }; + private static readonly string[] HttpApplicationEntryPoints = new[] { "Application_Start", "Application_OnStart" }; + private static readonly string[] HttpApplicationExitPoints = new[] { "Application_End", "Application_OnEnd" }; + private List httpApplications = new List(); private Machine machine; private bool disposed; - private List webApplications = new List(); - + /// /// Initializes a new instance of the MachineProxy class. /// @@ -44,7 +45,6 @@ public MachineProxy(ILogger logger, bool enabled) if (enabled) { AppDomain.CurrentDomain.AssemblyLoad += this.AssemblyLoadEventHandler; - this.machine = new Machine(logger); } } @@ -77,6 +77,38 @@ public void Dispose(bool force) GC.SuppressFinalize(this); } + /// + /// Finds a method on the specified type that looks like an event handler. + /// I.e., signatures include Method(), Method(object), or Method(object, EventArgs). + /// + /// The type to find the method in. + /// The names of methods to look for. + /// The first, most specific, method found that looks like an event handler. + private static MethodInfo FindEventHandler(Type type, string[] names) + { + MethodInfo result = null; + + var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(m => names.Any(n => n.Equals(m.Name, StringComparison.OrdinalIgnoreCase))) + .Select(m => new { Args = m.GetParameters(), Method = m }) + .Where(obj => obj.Args.Length == 0 || obj.Args[0].ParameterType == typeof(object)) + .OrderByDescending(obj => obj.Args.Length); + + foreach (var method in methods) + { + if ((method.Args.Length == 2 + && method.Args[1].ParameterType == typeof(EventArgs)) + || method.Args.Length == 1 + || method.Args.Length == 0) + { + result = method.Method; + break; + } + } + + return result; + } + /// /// Disposes of resources used by this instance. /// @@ -86,14 +118,18 @@ private void Dispose(bool force, bool disposing) { if (!this.disposed) { + AppDomain.CurrentDomain.AssemblyLoad -= this.AssemblyLoadEventHandler; + if (disposing) { - if (this.machine != null) - { - this.DisposeWebApplications(); - this.machine.Dispose(force); + this.DisposeHttpApplications(); - this.machine = null; + Machine m = this.machine; + this.machine = null; + + if (m != null) + { + m.Dispose(force); } } @@ -111,51 +147,66 @@ private void AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args) Assembly assembly = args.LoadedAssembly; string assemblyName = assembly.GetName().Name; - if (MachineProxy.assemblyBlackList.Any(b => Regex.Match(assemblyName, b).Length != 0)) + if (!MachineProxy.AssemblyBlacklist.Any(n => assemblyName.StartsWith(n, StringComparison.OrdinalIgnoreCase))) { - return; - } - - Type type = typeof(HttpApplication); - string[] methods = { "Application_Start", "Application_OnStart" }; - - IEnumerable types = assembly.GetTypes().Where(t => type.IsAssignableFrom(t) && t != type && t.IsAbstract == false); - IEnumerable apps = types.Select(t => Activator.CreateInstance(t)); - - foreach (object app in apps) - { - MethodInfo method = app.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(m => methods.Select(em => em.ToUpperInvariant()).Contains(m.Name.ToUpperInvariant())) - .FirstOrDefault(); + IEnumerable types = assembly.GetTypes().Where(t => typeof(HttpApplication).IsAssignableFrom(t) && t != typeof(HttpApplication) && !t.IsAbstract); - if (method != null) + foreach (Type type in types) { - this.webApplications.Add(app); + MethodInfo method = MachineProxy.FindEventHandler(type, MachineProxy.HttpApplicationEntryPoints); - method.Invoke(app, new object[] { this, EventArgs.Empty }); + if (method != null) + { + HttpApplication app = (HttpApplication)Activator.CreateInstance(type); + this.httpApplications.Add(app); + this.InvokeEventHandler(app, method); + } } } } /// - /// Disposes all web applications that were created as entry points. + /// Disposes all http applications that were created as entry points. /// - private void DisposeWebApplications() + private void DisposeHttpApplications() { - string[] methods = { "Application_End", "Application_OnEnd" }; - - foreach (object app in this.webApplications) + foreach (HttpApplication app in this.httpApplications) { - MethodInfo method = app.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(m => methods.Select(em => em.ToUpperInvariant()).Contains(m.Name.ToUpperInvariant())) - .FirstOrDefault(); + MethodInfo method = MachineProxy.FindEventHandler(app.GetType(), MachineProxy.HttpApplicationExitPoints); if (method != null) { - method.Invoke(app, new object[] { this, EventArgs.Empty }); + this.InvokeEventHandler(app, method); } - (app as HttpApplication).Dispose(); + app.Dispose(); + } + + this.httpApplications.Clear(); + } + + /// + /// Invokes the event handler identified by the given method on the given instance. + /// + /// The instance to invoke the event handler on. + /// The method identifying the event handler to invoke. + private void InvokeEventHandler(object instance, MethodInfo method) + { + ParameterInfo[] args = method.GetParameters(); + + switch (args.Length) + { + case 2: + method.Invoke(instance, new object[] { this, new EventArgs() }); + break; + case 1: + method.Invoke(instance, new object[] { this }); + break; + case 0: + method.Invoke(instance, null); + break; + default: + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "The specified method was expected to have 0, 1, or 2 parameters: {0}", method.ToString())); } } }