EventWiringFacility
Facility to allow components to dynamically subscribe to events offered by
other components. We call the component that offers events publishers and
the components that uses them, subscribers.
using Castle.Core;
using Castle.Core.Configuration;
using Castle.MicroKernel;
using Castle.MicroKernel.Facilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Castle.Facilities.EventWiring
{
public class EventWiringFacility : AbstractFacility
{
private const string SubscriberList = "evts.subscriber.list";
protected override void Init()
{
base.Kernel.ComponentModelCreated += OnComponentModelCreated;
base.Kernel.ComponentCreated += OnComponentCreated;
base.Kernel.ComponentDestroyed += OnComponentDestroyed;
}
private void OnComponentModelCreated(ComponentModel model)
{
ExtractAndRegisterEventInformation(model);
}
private void ExtractAndRegisterEventInformation(ComponentModel model)
{
if (!IsNotPublishingEvents(model)) {
IConfiguration val = model.Configuration.get_Children().get_Item("subscribers");
if (((List<IConfiguration>)val.get_Children()).Count < 1)
throw new EventWiringException("The subscribers node must have at least an one subsciber child. Check node subscribers of the " + model.Name + " component");
Dictionary<string, List<WireInfo>> dictionary = new Dictionary<string, List<WireInfo>>();
foreach (IConfiguration item in (List<IConfiguration>)val.get_Children()) {
string subscriberKey = GetSubscriberKey(item);
AddSubscriberDependecyToModel(subscriberKey, model);
ExtractAndAddEventInfo(dictionary, subscriberKey, item, model);
}
model.ExtendedProperties["evts.subscriber.list"] = dictionary;
}
}
private void ExtractAndAddEventInfo(IDictionary<string, List<WireInfo>> subscribers2Evts, string subscriberKey, IConfiguration subscriber, ComponentModel model)
{
if (!subscribers2Evts.TryGetValue(subscriberKey, out List<WireInfo> value))
value = (subscribers2Evts[subscriberKey] = new List<WireInfo>());
string text = subscriber.get_Attributes().get_Item("event");
if (string.IsNullOrEmpty(text))
throw new EventWiringException("You must supply an 'event' attribute which is the event name on the publisher you want to subscribe. Check node 'subscriber' for component " + model.Name + "and id = " + subscriberKey);
string text2 = subscriber.get_Attributes().get_Item("handler");
if (string.IsNullOrEmpty(text2))
throw new EventWiringException("You must supply an 'handler' attribute which is the method on the subscriber that will handle the event. Check node 'subscriber' for component " + model.Name + "and id = " + subscriberKey);
value.Add(new WireInfo(text, text2));
}
private void AddSubscriberDependecyToModel(string subscriberKey, ComponentModel model)
{
DependencyModel dependencyModel = new DependencyModel(DependencyType.ServiceOverride, subscriberKey, null, false);
model.Dependencies.Add(dependencyModel);
}
private static string GetSubscriberKey(IConfiguration subscriber)
{
string text = subscriber.get_Attributes().get_Item("id");
if (string.IsNullOrEmpty(text))
throw new EventWiringException("The subscriber node must have a valid Id assigned");
return text;
}
private bool IsNotPublishingEvents(ComponentModel model)
{
if ((object)model.Configuration != null)
return (object)model.Configuration.get_Children().get_Item("subscribers") == null;
return true;
}
private void OnComponentCreated(ComponentModel model, object instance)
{
if (IsPublisher(model))
WirePublisher(model, instance);
}
private void WirePublisher(ComponentModel model, object publisher)
{
StartAndWirePublisherSubscribers(model, publisher);
}
private bool IsPublisher(ComponentModel model)
{
return model.ExtendedProperties["evts.subscriber.list"] != null;
}
private void StartAndWirePublisherSubscribers(ComponentModel model, object publisher)
{
IDictionary dictionary = (IDictionary)model.ExtendedProperties["evts.subscriber.list"];
if (dictionary != null) {
IDictionaryEnumerator enumerator = dictionary.GetEnumerator();
try {
while (enumerator.MoveNext()) {
DictionaryEntry dictionaryEntry = (DictionaryEntry)enumerator.Current;
string text = (string)dictionaryEntry.Key;
IList list = (IList)dictionaryEntry.Value;
IHandler handler = base.Kernel.GetHandler(text);
AssertValidHandler(handler, text);
object obj;
try {
obj = base.Kernel.Resolve<object>(text);
} catch (Exception innerException) {
throw new EventWiringException("Failed to start subscriber " + text, innerException);
}
Type type = publisher.GetType();
foreach (WireInfo item in list) {
string eventName = item.EventName;
EventInfo event = type.GetEvent(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if ((object)event == null)
throw new EventWiringException("Could not find event on publisher. Event " + eventName + " Publisher " + type.FullName);
MethodInfo method = obj.GetType().GetMethod(item.Handler, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if ((object)method == null)
throw new EventWiringException("Could not find the method '" + item.Handler + "' to handle the event " + eventName + ". Subscriber " + obj.GetType().FullName);
Delegate handler2 = Delegate.CreateDelegate(event.EventHandlerType, obj, item.Handler);
event.AddEventHandler(publisher, handler2);
}
}
} finally {
(enumerator as IDisposable)?.Dispose();
}
}
}
private static void AssertValidHandler(IHandler handler, string subscriberKey)
{
if (handler == null)
throw new EventWiringException("Publisher tried to start subscriber " + subscriberKey + " that was not found");
if (handler.CurrentState == HandlerState.WaitingDependency)
throw new EventWiringException("Publisher tried to start subscriber " + subscriberKey + " that is waiting for a dependency");
}
private void OnComponentDestroyed(ComponentModel model, object instance)
{
}
}
}