|
|
|
@ -1,173 +1,28 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
//From http://caliburnmicro.codeplex.com/
|
|
|
|
|
using NLog;
|
|
|
|
|
|
|
|
|
|
namespace NzbDrone.Common.Eventing
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Enables loosely-coupled publication of and subscription to events.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class EventAggregator : IEventAggregator
|
|
|
|
|
{
|
|
|
|
|
readonly List<Handler> handlers = new List<Handler>();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The default thread marshaller used for publication;
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static Action<Action> DefaultPublicationThreadMarshaller = action => action();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Processing of handler results on publication thread.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static Action<object, object> HandlerResultProcessing = (target, result) => { };
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref = "EventAggregator" /> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public EventAggregator()
|
|
|
|
|
{
|
|
|
|
|
PublicationThreadMarshaller = DefaultPublicationThreadMarshaller;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the default publication thread marshaller.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>
|
|
|
|
|
/// The default publication thread marshaller.
|
|
|
|
|
/// </value>
|
|
|
|
|
public Action<System.Action> PublicationThreadMarshaller { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name = "instance">The instance to subscribe for event publication.</param>
|
|
|
|
|
public virtual void Subscribe(object instance)
|
|
|
|
|
{
|
|
|
|
|
lock (handlers)
|
|
|
|
|
{
|
|
|
|
|
if (handlers.Any(x => x.Matches(instance)))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
private readonly Logger _logger;
|
|
|
|
|
private readonly IEnumerable<IHandle> _handlers;
|
|
|
|
|
|
|
|
|
|
handlers.Add(new Handler(instance));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Unsubscribes the instance from all events.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name = "instance">The instance to unsubscribe.</param>
|
|
|
|
|
public virtual void Unsubscribe(object instance)
|
|
|
|
|
public EventAggregator(Logger logger, IEnumerable<IHandle> handlers)
|
|
|
|
|
{
|
|
|
|
|
lock (handlers)
|
|
|
|
|
{
|
|
|
|
|
var found = handlers.FirstOrDefault(x => x.Matches(instance));
|
|
|
|
|
|
|
|
|
|
if (found != null)
|
|
|
|
|
{
|
|
|
|
|
handlers.Remove(found);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_handlers = handlers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Publishes a message.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name = "message">The message instance.</param>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Does not marshall the the publication to any special thread by default.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public virtual void Publish(object message)
|
|
|
|
|
public void Publish<TEvent>(TEvent message)
|
|
|
|
|
{
|
|
|
|
|
Publish(message, PublicationThreadMarshaller);
|
|
|
|
|
}
|
|
|
|
|
_logger.Trace("Publishing {0}", message.GetType().Name);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Publishes a message.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name = "message">The message instance.</param>
|
|
|
|
|
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
|
|
|
|
|
public virtual void Publish(object message, Action<System.Action> marshal)
|
|
|
|
|
{
|
|
|
|
|
Handler[] toNotify;
|
|
|
|
|
lock (handlers)
|
|
|
|
|
foreach (var handler in _handlers.OfType<IHandle<TEvent>>())
|
|
|
|
|
{
|
|
|
|
|
toNotify = handlers.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
marshal(() =>
|
|
|
|
|
{
|
|
|
|
|
var messageType = message.GetType();
|
|
|
|
|
|
|
|
|
|
var dead = toNotify
|
|
|
|
|
.Where(handler => !handler.Handle(messageType, message))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
if (dead.Any())
|
|
|
|
|
{
|
|
|
|
|
lock (handlers)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in dead)
|
|
|
|
|
{
|
|
|
|
|
handlers.Remove(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Handler
|
|
|
|
|
{
|
|
|
|
|
readonly WeakReference reference;
|
|
|
|
|
readonly Dictionary<Type, MethodInfo> supportedHandlers = new Dictionary<Type, MethodInfo>();
|
|
|
|
|
|
|
|
|
|
public Handler(object handler)
|
|
|
|
|
{
|
|
|
|
|
reference = new WeakReference(handler);
|
|
|
|
|
|
|
|
|
|
var interfaces = handler.GetType().GetInterfaces()
|
|
|
|
|
.Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType);
|
|
|
|
|
|
|
|
|
|
foreach (var @interface in interfaces)
|
|
|
|
|
{
|
|
|
|
|
var type = @interface.GetGenericArguments()[0];
|
|
|
|
|
var method = @interface.GetMethod("Handle");
|
|
|
|
|
supportedHandlers[type] = method;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Matches(object instance)
|
|
|
|
|
{
|
|
|
|
|
return reference.Target == instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Handle(Type messageType, object message)
|
|
|
|
|
{
|
|
|
|
|
var target = reference.Target;
|
|
|
|
|
if (target == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var pair in supportedHandlers)
|
|
|
|
|
{
|
|
|
|
|
if (pair.Key.IsAssignableFrom(messageType))
|
|
|
|
|
{
|
|
|
|
|
var result = pair.Value.Invoke(target, new[] { message });
|
|
|
|
|
if (result != null)
|
|
|
|
|
{
|
|
|
|
|
HandlerResultProcessing(target, result);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
_logger.Trace("{0} => {1}", message.GetType().Name, handler.GetType().Name);
|
|
|
|
|
handler.Handle(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|