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 : HandlerTypeInformation[] executorTypes = executorsAssembly
85 : .GetTypes()
86 : .Select(type => new HandlerTypeInformation { Type = type, Interfaces = resolveMessageHandlerInterface(type) })
87 : .Where(type => type.Interfaces != null && type.Interfaces.Any())
88 : .ToArray();
89 :
90 : Register(trueForEventsFalseForCommands, resolveMessageHandlerInterface, skipCommandHandlers, executorTypes);
91 : }
92 : }
93 :
94 : /// <summary>
95 : /// Information explaining the registration to make
96 : /// </summary>
97 : public class HandlerTypeInformation
98 1 : {
99 : /// <summary>
100 : /// The <see cref="Type"/> of the hanlder to register
101 : /// </summary>
102 : public Type Type { get; set; }
103 :
104 : /// <summary>
105 : /// The Handler type to resolve to, so either <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>
106 : /// </summary>
107 : public IEnumerable<Type> Interfaces { get; set; }
108 : }
109 :
110 : /// <summary>
111 : /// Registers all <see cref="IHandler"/> instances in the provided <paramref name="executorTypes"/>.
112 : /// </summary>
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="skipCommandHandlers">Indicates if registering of <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> is enabled.</param>
116 : /// <param name="executorTypes">A collection of <see cref="Type"/> to register.</param>
117 1 : public virtual void Register(bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, bool skipCommandHandlers, params HandlerTypeInformation[] executorTypes)
118 : {
119 : foreach (var executorType in executorTypes)
120 : {
121 : foreach (Type @interface in executorType.Interfaces)
122 : {
123 : Type safeExecutorType = executorType.Type;
124 : if (safeExecutorType.IsGenericType && safeExecutorType.Name == typeof(DtoCommandHandler<,>).Name)
125 : {
126 : if (skipCommandHandlers)
127 : continue;
128 : Type[] genericArguments = safeExecutorType.GetGenericArguments().Take(2).ToArray();
129 : safeExecutorType = safeExecutorType.MakeGenericType(genericArguments.Take(2).ToArray());
130 : }
131 : InvokeHandler(@interface, trueForEventsFalseForCommands, resolveMessageHandlerInterface, safeExecutorType);
132 : }
133 : }
134 : }
135 :
136 : /// <summary>
137 : /// Extract the <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method of <see cref="GetEventHandlerRegistrar"/> or <see cref="GetCommandHandlerRegistrar"/>.
138 : /// Create an <see cref="Action"/> around the provided <paramref name="executorType"/>
139 : /// Then register the created <see cref="Action"/> using the extracted <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method
140 : /// </summary>
141 : /// <param name="interface">The <see cref="Type"/> of <see cref="IHandler"/></param>
142 : /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
143 : /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
144 : /// <param name="executorType">The <see cref="Type"/> of the event handler that will do the handling</param>
145 1 : protected virtual void InvokeHandler(Type @interface, bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, Type executorType)
146 : {
147 : MethodInfo registerExecutorMethod = null;
148 :
149 : MethodInfo originalRegisterExecutorMethod = (trueForEventsFalseForCommands ? GetEventHandlerRegistrar(null, executorType) : GetCommandHandlerRegistrar(null, executorType))
150 : .GetType()
151 : .GetMethods(BindingFlags.Instance | BindingFlags.Public)
152 : .Where(mi => mi.Name == "RegisterHandler")
153 : .Where(mi => mi.IsGenericMethod)
154 : .Where(mi => mi.GetGenericArguments().Count() == 1)
155 : .Single(mi => mi.GetParameters().Count() == 3);
156 :
157 : IList<Type> interfaceGenericArguments = @interface.GetGenericArguments().ToList();
158 : if (interfaceGenericArguments.Count == 2)
159 : {
160 : Type commandType = interfaceGenericArguments[1];
161 : Type[] genericArguments = commandType.GetGenericArguments();
162 : if (genericArguments.Length == 1)
163 : if (typeof (IEvent<>).MakeGenericType(genericArguments.First()) == commandType)
164 : return;
165 :
166 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
167 : }
168 : else
169 : {
170 : foreach (Type commandType in interfaceGenericArguments)
171 : {
172 : try
173 : {
174 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
175 : break;
176 : }
177 : catch (VerificationException)
178 : {
179 : }
180 : catch (ArgumentException)
181 : {
182 : }
183 : }
184 : }
185 :
186 : if (registerExecutorMethod == null)
187 : throw new InvalidOperationException("No executor method could be compiled for " + @interface.FullName);
188 :
189 : HandlerDelegate handlerDelegate = BuildDelegateAction(executorType, resolveMessageHandlerInterface);
190 :
191 : InvokeHandlerDelegate(registerExecutorMethod, trueForEventsFalseForCommands, handlerDelegate);
192 : }
193 :
194 : /// <summary>
195 : /// Invokes <paramref name="handlerDelegate"/>, fetching the corresponding "HoldMessageLock" configuration setting
196 : /// </summary>
197 : /// <param name="registerExecutorMethod">The <see cref="MethodInfo"/> to use to invoke <paramref name="handlerDelegate"/>.</param>
198 : /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
199 : /// <param name="handlerDelegate">The actual <see cref="HandlerDelegate"/> that gets executed.</param>
200 1 : protected virtual void InvokeHandlerDelegate(MethodInfo registerExecutorMethod, bool trueForEventsFalseForCommands, HandlerDelegate handlerDelegate)
201 : {
202 : Type messageType = null;
203 : bool holdMessageLock;
204 : try
205 : {
206 : messageType = registerExecutorMethod.GetParameters()[0].ParameterType.GetGenericArguments()[0];
207 : string messageTypeName = messageType.FullName;
208 : string configuration = string.Format("{0}<{1}>.HoldMessageLock", handlerDelegate.TargetedType.FullName, messageTypeName);
209 : // If this cannot be parsed then assume the safe route that this must be required to hold the lock.
210 : if (!bool.TryParse(DependencyResolver.Resolve<IConfigurationManager>().GetSetting(configuration), out holdMessageLock))
211 : holdMessageLock = true;
212 : }
213 : catch
214 : {
215 : holdMessageLock = true;
216 : }
217 : registerExecutorMethod.Invoke(trueForEventsFalseForCommands ? GetEventHandlerRegistrar(messageType, handlerDelegate.TargetedType) : GetCommandHandlerRegistrar(messageType, handlerDelegate.TargetedType), new object[] { handlerDelegate.Delegate, handlerDelegate.TargetedType, holdMessageLock });
218 : }
219 :
220 : /// <summary>
221 : /// Builds a <see cref="HandlerDelegate"/> that will resolve the provided <paramref name="executorType"/> and invoke the Handle method, when executed.
222 : /// </summary>
223 : /// <param name="executorType">The type of <see cref="IHandler"/> to resolve.></param>
224 : /// <param name="resolveMessageHandlerInterface">Not used.</param>
225 1 : protected virtual HandlerDelegate BuildDelegateAction(Type executorType, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface)
226 : {
227 : Action<dynamic> handlerDelegate = x =>
228 : {
229 : dynamic handler = DependencyResolver.Resolve(executorType);
230 : try
231 : {
232 : handler.Handle(x);
233 : }
234 : catch (NotImplementedException exception)
235 : {
236 : var logger = DependencyResolver.Resolve<ILogger>();
237 : 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);
238 : }
239 : };
240 :
241 : return new HandlerDelegate { Delegate = handlerDelegate, TargetedType = executorType };
242 : }
243 :
244 : /// <summary>
245 : /// Builds a method replacing the generic type with <paramref name="commandType"/>.
246 : /// </summary>
247 1 : protected virtual MethodInfo BuildExecutorMethod(MethodInfo originalRegisterExecutorMethod, Type executorType, Type commandType)
248 : {
249 : Type safeCommandType = commandType;
250 : if (safeCommandType.IsGenericType && safeCommandType.Name == typeof(DtoCommand<,>).Name && executorType.IsGenericType && executorType.Name == typeof(DtoCommandHandler<,>).Name)
251 : {
252 : Type[] genericArguments = executorType.GetGenericArguments().Take(2).ToArray();
253 : safeCommandType = typeof(DtoCommand<,>).MakeGenericType(genericArguments);
254 : }
255 :
256 : return originalRegisterExecutorMethod.MakeGenericMethod(safeCommandType);
257 : }
258 :
259 : /// <summary>
260 : /// Finds all <see cref="Type"/> that implement <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> that are implemented by <paramref name="type"/>.
261 : /// </summary>
262 : /// <param name="type">The <see cref="Type"/> to find all <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> of.</param>
263 1 : public virtual IEnumerable<Type> ResolveEventHandlerInterface(Type type)
264 : {
265 : return type
266 : .GetInterfaces()
267 : .Where
268 : (
269 : @interface =>
270 : @interface.IsGenericType &&
271 : (
272 : @interface.GetGenericTypeDefinition() == typeof(IEventHandler<,>)
273 : )
274 : );
275 : }
276 :
277 : /// <summary>
278 : /// Finds all <see cref="Type"/> that implement <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> that are implemented by <paramref name="type"/>.
279 : /// </summary>
280 : /// <param name="type">The <see cref="Type"/> to find all <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> of.</param>
281 1 : public virtual IEnumerable<Type> ResolveCommandHandlerInterface(Type type)
282 : {
283 : return type
284 : .GetInterfaces()
285 : .Where
286 : (
287 : @interface =>
288 : @interface.IsGenericType &&
289 : (
290 : @interface.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
291 : )
292 : );
293 : }
294 : }
295 : }
|