using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Abp.Events.Bus.Factories; using Abp.Events.Bus.Factories.Internals; using Abp.Events.Bus.Handlers; using Abp.Events.Bus.Handlers.Internals; using Abp.Extensions; using Abp.Threading; using Abp.Threading.Extensions; using Castle.Core.Logging; namespace Abp.Events.Bus { /// /// Implements EventBus as Singleton pattern. /// public class EventBus : IEventBus { /// /// Gets the default instance. /// public static EventBus Default { get; } = new EventBus(); /// /// Reference to the Logger. /// public ILogger Logger { get; set; } /// /// All registered handler factories. /// Key: Type of the event /// Value: List of handler factories /// private readonly ConcurrentDictionary> _handlerFactories; /// /// Creates a new instance. /// Instead of creating a new instace, you can use to use Global . /// public EventBus() { _handlerFactories = new ConcurrentDictionary>(); Logger = NullLogger.Instance; } /// public IDisposable Register(Action action) where TEventData : IEventData { return Register(typeof(TEventData), new ActionEventHandler(action)); } /// public IDisposable AsyncRegister(Func action) where TEventData : IEventData { return Register(typeof(TEventData), new AsyncActionEventHandler(action)); } /// public IDisposable Register(IEventHandler handler) where TEventData : IEventData { return Register(typeof(TEventData), handler); } /// public IDisposable AsyncRegister(IAsyncEventHandler handler) where TEventData : IEventData { return Register(typeof(TEventData), handler); } /// public IDisposable Register() where TEventData : IEventData where THandler : IEventHandler, new() { return Register(typeof(TEventData), new TransientEventHandlerFactory()); } /// public IDisposable Register(Type eventType, IEventHandler handler) { return Register(eventType, new SingleInstanceHandlerFactory(handler)); } /// public IDisposable Register(IEventHandlerFactory factory) where TEventData : IEventData { return Register(typeof(TEventData), factory); } /// public IDisposable Register(Type eventType, IEventHandlerFactory factory) { GetOrCreateHandlerFactories(eventType) .Locking(factories => factories.Add(factory)); return new FactoryUnregistrar(this, eventType, factory); } /// public void Unregister(Action action) where TEventData : IEventData { Check.NotNull(action, nameof(action)); GetOrCreateHandlerFactories(typeof(TEventData)) .Locking(factories => { factories.RemoveAll( factory => { var singleInstanceFactory = factory as SingleInstanceHandlerFactory; if (singleInstanceFactory == null) { return false; } var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler; if (actionHandler == null) { return false; } return actionHandler.Action == action; }); }); } /// public void AsyncUnregister(Func action) where TEventData : IEventData { Check.NotNull(action, nameof(action)); GetOrCreateHandlerFactories(typeof(TEventData)) .Locking(factories => { factories.RemoveAll( factory => { var singleInstanceFactory = factory as SingleInstanceHandlerFactory; if (singleInstanceFactory == null) { return false; } var actionHandler = singleInstanceFactory.HandlerInstance as AsyncActionEventHandler; if (actionHandler == null) { return false; } return actionHandler.Action == action; }); }); } /// public void Unregister(IEventHandler handler) where TEventData : IEventData { Unregister(typeof(TEventData), handler); } /// public void AsyncUnregister(IAsyncEventHandler handler) where TEventData : IEventData { Unregister(typeof(TEventData), handler); } /// public void Unregister(Type eventType, IEventHandler handler) { GetOrCreateHandlerFactories(eventType) .Locking(factories => { factories.RemoveAll( factory => factory is SingleInstanceHandlerFactory && (factory as SingleInstanceHandlerFactory).HandlerInstance == handler ); }); } /// public void Unregister(IEventHandlerFactory factory) where TEventData : IEventData { Unregister(typeof(TEventData), factory); } /// public void Unregister(Type eventType, IEventHandlerFactory factory) { GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory)); } /// public void UnregisterAll() where TEventData : IEventData { UnregisterAll(typeof(TEventData)); } /// public void UnregisterAll(Type eventType) { GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear()); } /// public void Trigger(TEventData eventData) where TEventData : IEventData { Trigger((object)null, eventData); } /// public void Trigger(object eventSource, TEventData eventData) where TEventData : IEventData { Trigger(typeof(TEventData), eventSource, eventData); } /// public void Trigger(Type eventType, IEventData eventData) { Trigger(eventType, null, eventData); } /// public void Trigger(Type eventType, object eventSource, IEventData eventData) { var exceptions = new List(); eventData.EventSource = eventSource; foreach (var handlerFactories in GetHandlerFactories(eventType)) { foreach (var handlerFactory in handlerFactories.EventHandlerFactories) { var handlerType = handlerFactory.GetHandlerType(); if (IsAsyncEventHandler(handlerType)) { AsyncHelper.RunSync(() => TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions)); } else if (IsEventHandler(handlerType)) { TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions); } else { var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!"; exceptions.Add(new AbpException(message)); } } } //Implements generic argument inheritance. See IEventDataWithInheritableGenericArgument if (eventType.GetTypeInfo().IsGenericType && eventType.GetGenericArguments().Length == 1 && typeof(IEventDataWithInheritableGenericArgument).IsAssignableFrom(eventType)) { var genericArg = eventType.GetGenericArguments()[0]; var baseArg = genericArg.GetTypeInfo().BaseType; if (baseArg != null) { var baseEventType = eventType.GetGenericTypeDefinition().MakeGenericType(baseArg); var constructorArgs = ((IEventDataWithInheritableGenericArgument)eventData).GetConstructorArgs(); var baseEventData = (IEventData)Activator.CreateInstance(baseEventType, constructorArgs); baseEventData.EventTime = eventData.EventTime; Trigger(baseEventType, eventData.EventSource, baseEventData); } } if (exceptions.Any()) { if (exceptions.Count == 1) { exceptions[0].ReThrow(); } throw new AggregateException("More than one error has occurred while triggering the event: " + eventType, exceptions); } } /// public Task TriggerAsync(TEventData eventData) where TEventData : IEventData { return TriggerAsync((object)null, eventData); } /// public Task TriggerAsync(object eventSource, TEventData eventData) where TEventData : IEventData { return TriggerAsync(typeof(TEventData), eventSource, eventData); } /// public Task TriggerAsync(Type eventType, IEventData eventData) { return TriggerAsync(eventType, null, eventData); } /// public async Task TriggerAsync(Type eventType, object eventSource, IEventData eventData) { var exceptions = new List(); eventData.EventSource = eventSource; await new SynchronizationContextRemover(); foreach (var handlerFactories in GetHandlerFactories(eventType)) { foreach (var handlerFactory in handlerFactories.EventHandlerFactories) { var handlerType = handlerFactory.GetHandlerType(); if (IsAsyncEventHandler(handlerType)) { await TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions); } else if (IsEventHandler(handlerType)) { TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions); } else { var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!"; exceptions.Add(new AbpException(message)); } } } //Implements generic argument inheritance. See IEventDataWithInheritableGenericArgument if (eventType.GetTypeInfo().IsGenericType && eventType.GetGenericArguments().Length == 1 && typeof(IEventDataWithInheritableGenericArgument).IsAssignableFrom(eventType)) { var genericArg = eventType.GetGenericArguments()[0]; var baseArg = genericArg.GetTypeInfo().BaseType; if (baseArg != null) { var baseEventType = eventType.GetGenericTypeDefinition().MakeGenericType(baseArg); var constructorArgs = ((IEventDataWithInheritableGenericArgument)eventData).GetConstructorArgs(); var baseEventData = (IEventData)Activator.CreateInstance(baseEventType, constructorArgs); baseEventData.EventTime = eventData.EventTime; await TriggerAsync(baseEventType, eventData.EventSource, baseEventData); } } if (exceptions.Any()) { if (exceptions.Count == 1) { exceptions[0].ReThrow(); } throw new AggregateException("More than one error has occurred while triggering the event: " + eventType, exceptions); } } private void TriggerHandlingException(IEventHandlerFactory handlerFactory, Type eventType, IEventData eventData, List exceptions) { var eventHandler = handlerFactory.GetHandler(); try { if (eventHandler == null) { throw new ArgumentNullException($"Registered event handler for event type {eventType.Name} is null!"); } var handlerType = typeof(IEventHandler<>).MakeGenericType(eventType); var method = handlerType.GetMethod( "HandleEvent", new[] { eventType } ); method.Invoke(eventHandler, new object[] { eventData }); } catch (TargetInvocationException ex) { exceptions.Add(ex.InnerException); } catch (Exception ex) { exceptions.Add(ex); } finally { handlerFactory.ReleaseHandler(eventHandler); } } private async Task TriggerAsyncHandlingException(IEventHandlerFactory asyncHandlerFactory, Type eventType, IEventData eventData, List exceptions) { var asyncEventHandler = asyncHandlerFactory.GetHandler(); try { if (asyncEventHandler == null) { throw new ArgumentNullException($"Registered async event handler for event type {eventType.Name} is null!"); } var asyncHandlerType = typeof(IAsyncEventHandler<>).MakeGenericType(eventType); var method = asyncHandlerType.GetMethod( "HandleEventAsync", new[] { eventType } ); await (Task)method.Invoke(asyncEventHandler, new object[] { eventData }); } catch (TargetInvocationException ex) { exceptions.Add(ex.InnerException); } catch (Exception ex) { exceptions.Add(ex); } finally { asyncHandlerFactory.ReleaseHandler(asyncEventHandler); } } private bool IsEventHandler(Type handlerType) { return handlerType.GetInterfaces() .Where(i => i.IsGenericType) .Any(i => i.GetGenericTypeDefinition() == typeof(IEventHandler<>)); } private bool IsAsyncEventHandler(Type handlerType) { return handlerType.GetInterfaces() .Where(i => i.IsGenericType) .Any(i => i.GetGenericTypeDefinition() == typeof(IAsyncEventHandler<>)); } private IEnumerable GetHandlerFactories(Type eventType) { var handlerFactoryList = new List(); foreach (var handlerFactory in _handlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key))) { handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value)); } return handlerFactoryList.ToArray(); } private static bool ShouldTriggerEventForHandler(Type eventType, Type handlerType) { //Should trigger same type if (handlerType == eventType) { return true; } //Should trigger for inherited types if (handlerType.IsAssignableFrom(eventType)) { return true; } return false; } private List GetOrCreateHandlerFactories(Type eventType) { return _handlerFactories.GetOrAdd(eventType, (type) => new List()); } private class EventTypeWithEventHandlerFactories { public Type EventType { get; } public List EventHandlerFactories { get; } public EventTypeWithEventHandlerFactories(Type eventType, List eventHandlerFactories) { EventType = eventType; EventHandlerFactories = eventHandlerFactories; } } // Reference from // https://blogs.msdn.microsoft.com/benwilli/2017/02/09/an-alternative-to-configureawaitfalse-everywhere/ private struct SynchronizationContextRemover : INotifyCompletion { public bool IsCompleted { get { return SynchronizationContext.Current == null; } } public void OnCompleted(Action continuation) { var prevContext = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(null); continuation(); } finally { SynchronizationContext.SetSynchronizationContext(prevContext); } } public SynchronizationContextRemover GetAwaiter() { return this; } public void GetResult() { } } } }