Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="Chinchilla Software Limited">
4 : // // Copyright Chinchilla Software Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Generic;
11 : using System.Linq;
12 : using System.Reflection;
13 : using System.Security;
14 : using cdmdotnet.Logging;
15 : using Cqrs.Bus;
16 : using Cqrs.Commands;
17 : using Cqrs.Events;
18 : using Cqrs.Messages;
19 :
20 : namespace Cqrs.Configuration
21 : {
22 : /// <summary>
23 : /// Triggers the <see cref="IEventHandlerRegistrar"/> and <see cref="ICommandHandlerRegistrar"/> if they are registered in the <see cref="IDependencyResolver"/>.
24 : /// </summary>
25 : public class BusRegistrar
26 1 : {
27 : /// <summary>
28 : /// Gets or set the <see cref="IDependencyResolver"/>.
29 : /// </summary>
30 : protected IDependencyResolver DependencyResolver { get; private set; }
31 :
32 : /// <summary>
33 : /// A <see cref="Func{Type, Type, THandlerRegistrar}"/> to use in-place of <see cref="IEventHandlerRegistrar"/>
34 : /// </summary>
35 : public static Func<Type, Type, IHandlerRegistrar> GetEventHandlerRegistrar { get; set; }
36 :
37 : /// <summary>
38 : /// A <see cref="Func{Type, Type, THandlerRegistrar}"/> to use in-place of <see cref="ICommandHandlerRegistrar"/>
39 : /// </summary>
40 : public static Func<Type, Type, IHandlerRegistrar> GetCommandHandlerRegistrar { get; set; }
41 :
42 : /// <summary>
43 : /// Instantiates a new instance of <see cref="BusRegistrar"/>.
44 : /// </summary>
45 1 : public BusRegistrar(IDependencyResolver dependencyResolver)
46 : {
47 : if(dependencyResolver == null)
48 : throw new ArgumentNullException("dependencyResolver");
49 :
50 : DependencyResolver = dependencyResolver;
51 : if (GetEventHandlerRegistrar == null)
52 : GetEventHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<IEventHandlerRegistrar>();
53 : if (GetCommandHandlerRegistrar == null)
54 : GetCommandHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<ICommandHandlerRegistrar>();
55 : }
56 :
57 : /// <summary>
58 : /// Registers all <see cref="IEventHandler"/> and <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> instances found in the <see cref="Assembly"/> for each <see cref="Type"/> in <paramref name="typesFromAssemblyContainingMessages"/>.
59 : /// </summary>
60 : /// <param name="typesFromAssemblyContainingMessages">A collection of <see cref="Type"/> to track back to their containing <see cref="Assembly"/> and scan.</param>
61 1 : public virtual void Register(params Type[] typesFromAssemblyContainingMessages)
62 : {
63 : var eventHandlerRegistrar = DependencyResolver.Resolve<IEventHandlerRegistrar>();
64 : if (eventHandlerRegistrar != null)
65 : Register(true, ResolveEventHandlerInterface, true, typesFromAssemblyContainingMessages);
66 :
67 : var commandHandlerRegistrar = DependencyResolver.Resolve<ICommandHandlerRegistrar>();
68 : if (commandHandlerRegistrar != null)
69 : Register(false, ResolveCommandHandlerInterface, false, typesFromAssemblyContainingMessages);
70 : }
71 :
72 : /// <summary>
73 : /// Registers all <see cref="IHandler"/> instances found in the <see cref="Assembly"/> for each <see cref="Type"/> in <paramref name="typesFromAssemblyContainingMessages"/>.
74 : /// </summary>
75 : /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
76 : /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
77 : /// <param name="skipCommandHandlers">Indicates if registering of <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> is enabled.</param>
78 : /// <param name="typesFromAssemblyContainingMessages">A collection of <see cref="Type"/> to track back to their containing <see cref="Assembly"/> and scan.</param>
79 1 : public virtual void Register(bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, bool skipCommandHandlers, params Type[] typesFromAssemblyContainingMessages)
80 : {
81 : foreach (Type typesFromAssemblyContainingMessage in typesFromAssemblyContainingMessages)
82 : {
83 : Assembly executorsAssembly = typesFromAssemblyContainingMessage.Assembly;
84 : var executorTypes = executorsAssembly
85 : .GetTypes()
86 : .Select(type => new { Type = type, Interfaces = resolveMessageHandlerInterface(type) })
87 : .Where(type => type.Interfaces != null && type.Interfaces.Any());
88 :
89 : foreach (var executorType in executorTypes)
90 : {
91 : foreach (Type @interface in executorType.Interfaces)
92 : {
93 : Type safeExecutorType = executorType.Type;
94 : if (typesFromAssemblyContainingMessage.IsGenericType && typesFromAssemblyContainingMessage.Name == typeof(DtoCommandHandler<,>).Name)
95 : {
96 : if (skipCommandHandlers)
97 : continue;
98 : Type[] genericArguments = typesFromAssemblyContainingMessage.GetGenericArguments().Take(2).ToArray();
99 : safeExecutorType = safeExecutorType.MakeGenericType(genericArguments.Take(2).ToArray());
100 : }
101 : InvokeHandler(@interface, trueForEventsFalseForCommands, resolveMessageHandlerInterface, safeExecutorType);
102 : }
103 : }
104 : }
105 : }
106 :
107 : /// <summary>
108 : /// Extract the <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method of <see cref="GetEventHandlerRegistrar"/> or <see cref="GetCommandHandlerRegistrar"/>.
109 : /// Create an <see cref="Action"/> around the provided <paramref name="executorType"/>
110 : /// Then register the created <see cref="Action"/> using the extracted <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method
111 : /// </summary>
112 : /// <param name="interface">The <see cref="Type"/> of <see cref="IHandler"/></param>
113 : /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
114 : /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
115 : /// <param name="executorType">The <see cref="Type"/> of the event handler that will do the handling</param>
116 1 : protected virtual void InvokeHandler(Type @interface, bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, Type executorType)
117 : {
118 : MethodInfo registerExecutorMethod = null;
119 :
120 : MethodInfo originalRegisterExecutorMethod = (trueForEventsFalseForCommands ? GetEventHandlerRegistrar(null, executorType) : GetCommandHandlerRegistrar(null, executorType))
121 : .GetType()
122 : .GetMethods(BindingFlags.Instance | BindingFlags.Public)
123 : .Where(mi => mi.Name == "RegisterHandler")
124 : .Where(mi => mi.IsGenericMethod)
125 : .Where(mi => mi.GetGenericArguments().Count() == 1)
126 : .Single(mi => mi.GetParameters().Count() == 3);
127 :
128 : IList<Type> interfaceGenericArguments = @interface.GetGenericArguments().ToList();
129 : if (interfaceGenericArguments.Count == 2)
130 : {
131 : Type commandType = interfaceGenericArguments[1];
132 : Type[] genericArguments = commandType.GetGenericArguments();
133 : if (genericArguments.Length == 1)
134 : if (typeof (IEvent<>).MakeGenericType(genericArguments.First()) == commandType)
135 : return;
136 :
137 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
138 : }
139 : else
140 : {
141 : foreach (Type commandType in interfaceGenericArguments)
142 : {
143 : try
144 : {
145 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
146 : break;
147 : }
148 : catch (VerificationException)
149 : {
150 : }
151 : catch (ArgumentException)
152 : {
153 : }
154 : }
155 : }
156 :
157 : if (registerExecutorMethod == null)
158 : throw new InvalidOperationException("No executor method could be compiled for " + @interface.FullName);
159 :
160 : HandlerDelegate handlerDelegate = BuildDelegateAction(executorType, resolveMessageHandlerInterface);
161 :
162 : InvokeHandlerDelegate(registerExecutorMethod, trueForEventsFalseForCommands, handlerDelegate);
163 : }
164 :
165 : /// <summary>
166 : /// Invokes <paramref name="handlerDelegate"/>, fetching the corresponding "HoldMessageLock" configuration setting
167 : /// </summary>
168 : /// <param name="registerExecutorMethod">The <see cref="MethodInfo"/> to use to invoke <paramref name="handlerDelegate"/>.</param>
169 : /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
170 : /// <param name="handlerDelegate">The actual <see cref="HandlerDelegate"/> that gets executed.</param>
171 1 : protected virtual void InvokeHandlerDelegate(MethodInfo registerExecutorMethod, bool trueForEventsFalseForCommands, HandlerDelegate handlerDelegate)
172 : {
173 : Type messageType = null;
174 : bool holdMessageLock;
175 : try
176 : {
177 : messageType = registerExecutorMethod.GetParameters()[0].ParameterType.GetGenericArguments()[0];
178 : string messageTypeName = messageType.FullName;
179 : string configuration = string.Format("{0}<{1}>.HoldMessageLock", handlerDelegate.TargetedType.FullName, messageTypeName);
180 : // If this cannot be parsed then assume the safe route that this must be required to hold the lock.
181 : if (!bool.TryParse(DependencyResolver.Resolve<IConfigurationManager>().GetSetting(configuration), out holdMessageLock))
182 : holdMessageLock = true;
183 : }
184 : catch
185 : {
186 : holdMessageLock = true;
187 : }
188 : registerExecutorMethod.Invoke(trueForEventsFalseForCommands ? GetEventHandlerRegistrar(messageType, handlerDelegate.TargetedType) : GetCommandHandlerRegistrar(messageType, handlerDelegate.TargetedType), new object[] { handlerDelegate.Delegate, handlerDelegate.TargetedType, holdMessageLock });
189 : }
190 :
191 : /// <summary>
192 : /// Builds a <see cref="HandlerDelegate"/> that will resolve the provided <paramref name="executorType"/> and invoke the Handle method, when executed.
193 : /// </summary>
194 : /// <param name="executorType">The type of <see cref="IHandler"/> to resolve.></param>
195 : /// <param name="resolveMessageHandlerInterface">Not used.</param>
196 1 : protected virtual HandlerDelegate BuildDelegateAction(Type executorType, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface)
197 : {
198 : Action<dynamic> handlerDelegate = x =>
199 : {
200 : dynamic handler = DependencyResolver.Resolve(executorType);
201 : try
202 : {
203 : handler.Handle(x);
204 : }
205 : catch (NotImplementedException exception)
206 : {
207 : var logger = DependencyResolver.Resolve<ILogger>();
208 : logger.LogInfo(string.Format("An event message arrived of the type '{0}' went to a handler of type '{1}' but was not implemented.", x.GetType().FullName, handler.GetType().FullName), exception: exception);
209 : }
210 : };
211 :
212 : return new HandlerDelegate { Delegate = handlerDelegate, TargetedType = executorType };
213 : }
214 :
215 : /// <summary>
216 : /// Builds a method replacing the generic type with <paramref name="commandType"/>.
217 : /// </summary>
218 1 : protected virtual MethodInfo BuildExecutorMethod(MethodInfo originalRegisterExecutorMethod, Type executorType, Type commandType)
219 : {
220 : Type safeCommandType = commandType;
221 : if (safeCommandType.IsGenericType && safeCommandType.Name == typeof(DtoCommand<,>).Name && executorType.IsGenericType && executorType.Name == typeof(DtoCommandHandler<,>).Name)
222 : {
223 : Type[] genericArguments = executorType.GetGenericArguments().Take(2).ToArray();
224 : safeCommandType = typeof(DtoCommand<,>).MakeGenericType(genericArguments);
225 : }
226 :
227 : return originalRegisterExecutorMethod.MakeGenericMethod(safeCommandType);
228 : }
229 :
230 : /// <summary>
231 : /// Finds all <see cref="Type"/> that implement <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> that are implemented by <paramref name="type"/>.
232 : /// </summary>
233 : /// <param name="type">The <see cref="Type"/> to find all <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> of.</param>
234 1 : protected virtual IEnumerable<Type> ResolveEventHandlerInterface(Type type)
235 : {
236 : return type
237 : .GetInterfaces()
238 : .Where
239 : (
240 : @interface =>
241 : @interface.IsGenericType &&
242 : (
243 : @interface.GetGenericTypeDefinition() == typeof(IEventHandler<,>)
244 : )
245 : );
246 : }
247 :
248 : /// <summary>
249 : /// Finds all <see cref="Type"/> that implement <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> that are implemented by <paramref name="type"/>.
250 : /// </summary>
251 : /// <param name="type">The <see cref="Type"/> to find all <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> of.</param>
252 1 : protected virtual IEnumerable<Type> ResolveCommandHandlerInterface(Type type)
253 : {
254 : return type
255 : .GetInterfaces()
256 : .Where
257 : (
258 : @interface =>
259 : @interface.IsGenericType &&
260 : (
261 : @interface.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
262 : )
263 : );
264 : }
265 : }
266 : }
|