Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="cdmdotnet Limited">
4 : // // Copyright cdmdotnet 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 :
19 : namespace Cqrs.Configuration
20 : {
21 : /// <summary>
22 : /// Triggers the <see cref="IEventHandlerRegistrar"/> and <see cref="ICommandHandlerRegistrar"/> if they are registered in the <see cref="IDependencyResolver"/>.
23 : /// </summary>
24 : public class BusRegistrar
25 1 : {
26 : protected IDependencyResolver DependencyResolver { get; private set; }
27 :
28 : public static Func<Type, Type, IHandlerRegistrar> GetEventHandlerRegistrar { get; set; }
29 :
30 : public static Func<Type, Type, IHandlerRegistrar> GetCommandHandlerRegistrar { get; set; }
31 :
32 0 : public BusRegistrar(IDependencyResolver dependencyResolver)
33 : {
34 : if(dependencyResolver == null)
35 : throw new ArgumentNullException("dependencyResolver");
36 :
37 : DependencyResolver = dependencyResolver;
38 : if (GetEventHandlerRegistrar == null)
39 : GetEventHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<IEventHandlerRegistrar>();
40 : if (GetCommandHandlerRegistrar == null)
41 : GetCommandHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<ICommandHandlerRegistrar>();
42 : }
43 :
44 0 : public virtual void Register(params Type[] typesFromAssemblyContainingMessages)
45 : {
46 : var eventHandlerRegistrar = DependencyResolver.Resolve<IEventHandlerRegistrar>();
47 : if (eventHandlerRegistrar != null)
48 : Register(true, ResolveEventHandlerInterface, true, typesFromAssemblyContainingMessages);
49 :
50 : var commandHandlerRegistrar = DependencyResolver.Resolve<ICommandHandlerRegistrar>();
51 : if (commandHandlerRegistrar != null)
52 : Register(false, ResolveCommandHandlerInterface, false, typesFromAssemblyContainingMessages);
53 : }
54 :
55 0 : public virtual void Register(bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, bool skipCommandHandlers, params Type[] typesFromAssemblyContainingMessages)
56 : {
57 : foreach (Type typesFromAssemblyContainingMessage in typesFromAssemblyContainingMessages)
58 : {
59 : Assembly executorsAssembly = typesFromAssemblyContainingMessage.Assembly;
60 : var executorTypes = executorsAssembly
61 : .GetTypes()
62 : .Select(type => new { Type = type, Interfaces = resolveMessageHandlerInterface(type) })
63 : .Where(type => type.Interfaces != null && type.Interfaces.Any());
64 :
65 : foreach (var executorType in executorTypes)
66 : {
67 : foreach (Type @interface in executorType.Interfaces)
68 : {
69 : Type safeExecutorType = executorType.Type;
70 : if (typesFromAssemblyContainingMessage.IsGenericType && typesFromAssemblyContainingMessage.Name == typeof(DtoCommandHandler<,>).Name)
71 : {
72 : if (skipCommandHandlers)
73 : continue;
74 : Type[] genericArguments = typesFromAssemblyContainingMessage.GetGenericArguments().Take(2).ToArray();
75 : safeExecutorType = safeExecutorType.MakeGenericType(genericArguments.Take(2).ToArray());
76 : }
77 : InvokeHandler(@interface, trueForEventsFalseForCommands, resolveMessageHandlerInterface, safeExecutorType);
78 : }
79 : }
80 : }
81 : }
82 :
83 : /// <summary>
84 : /// Extract the <see cref="IHandlerRegistrar.RegisterHandler{TMessage}"/> method from the provided <paramref name="bus"/>
85 : /// Create an <see cref="Action"/> around the provided <paramref name="executorType"/>
86 : /// Then register the created <see cref="Action"/> using the extracted <see cref="IHandlerRegistrar.RegisterHandler{TMessage}"/> method
87 : /// </summary>
88 : /// <param name="executorType">The <see cref="Type"/> of the event handler that will do the handling</param>
89 1 : protected virtual void InvokeHandler(Type @interface, bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, Type executorType)
90 : {
91 : MethodInfo registerExecutorMethod = null;
92 :
93 : MethodInfo originalRegisterExecutorMethod = (trueForEventsFalseForCommands ? GetEventHandlerRegistrar(null, executorType) : GetCommandHandlerRegistrar(null, executorType))
94 : .GetType()
95 : .GetMethods(BindingFlags.Instance | BindingFlags.Public)
96 : .Where(mi => mi.Name == "RegisterHandler")
97 : .Where(mi => mi.IsGenericMethod)
98 : .Where(mi => mi.GetGenericArguments().Count() == 1)
99 : .Single(mi => mi.GetParameters().Count() == 3);
100 :
101 : IList<Type> interfaceGenericArguments = @interface.GetGenericArguments().ToList();
102 : if (interfaceGenericArguments.Count == 2)
103 : {
104 : Type commandType = interfaceGenericArguments[1];
105 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
106 : }
107 : else
108 : {
109 : foreach (Type commandType in interfaceGenericArguments)
110 : {
111 : try
112 : {
113 : registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
114 : break;
115 : }
116 : catch (VerificationException)
117 : {
118 : }
119 : catch (ArgumentException)
120 : {
121 : }
122 : }
123 : }
124 :
125 : if (registerExecutorMethod == null)
126 : throw new InvalidOperationException("No executor method could be compiled for " + @interface.FullName);
127 :
128 : HandlerDelegate handlerDelegate = BuildDelegateAction(executorType, resolveMessageHandlerInterface);
129 :
130 : InvokeHandlerDelegate(registerExecutorMethod, trueForEventsFalseForCommands, handlerDelegate);
131 : }
132 :
133 0 : protected virtual void InvokeHandlerDelegate(MethodInfo registerExecutorMethod, bool trueForEventsFalseForCommands, HandlerDelegate handlerDelegate)
134 : {
135 : Type messageType = null;
136 : bool holdMessageLock;
137 : try
138 : {
139 : messageType = registerExecutorMethod.GetParameters()[0].ParameterType.GetGenericArguments()[0];
140 : string messageTypeName = messageType.FullName;
141 : string configuration = string.Format("{0}<{1}>.HoldMessageLock", handlerDelegate.TargetedType.FullName, messageTypeName);
142 : // If this cannot be parsed then assume the safe route that this must be required to hold the lock.
143 : if (!bool.TryParse(DependencyResolver.Resolve<IConfigurationManager>().GetSetting(configuration), out holdMessageLock))
144 : holdMessageLock = true;
145 : }
146 : catch
147 : {
148 : holdMessageLock = true;
149 : }
150 : registerExecutorMethod.Invoke(trueForEventsFalseForCommands ? GetEventHandlerRegistrar(messageType, handlerDelegate.TargetedType) : GetCommandHandlerRegistrar(messageType, handlerDelegate.TargetedType), new object[] { handlerDelegate.Delegate, handlerDelegate.TargetedType, holdMessageLock });
151 : }
152 :
153 0 : protected virtual HandlerDelegate BuildDelegateAction(Type executorType, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface)
154 : {
155 : Action<dynamic> handlerDelegate = x =>
156 : {
157 : dynamic handler = DependencyResolver.Resolve(executorType);
158 : try
159 : {
160 : handler.Handle(x);
161 : }
162 : catch (NotImplementedException exception)
163 : {
164 : var logger = DependencyResolver.Resolve<ILogger>();
165 : 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);
166 : }
167 : };
168 :
169 : return new HandlerDelegate { Delegate = handlerDelegate, TargetedType = executorType };
170 : }
171 :
172 0 : protected virtual MethodInfo BuildExecutorMethod(MethodInfo originalRegisterExecutorMethod, Type executorType, Type commandType)
173 : {
174 : Type safeCommandType = commandType;
175 : if (safeCommandType.IsGenericType && safeCommandType.Name == typeof(DtoCommand<,>).Name && executorType.IsGenericType && executorType.Name == typeof(DtoCommandHandler<,>).Name)
176 : {
177 : Type[] genericArguments = executorType.GetGenericArguments().Take(2).ToArray();
178 : safeCommandType = typeof(DtoCommand<,>).MakeGenericType(genericArguments);
179 : }
180 :
181 : return originalRegisterExecutorMethod.MakeGenericMethod(safeCommandType);
182 : }
183 :
184 0 : protected virtual IEnumerable<Type> ResolveEventHandlerInterface(Type type)
185 : {
186 : return type
187 : .GetInterfaces()
188 : .Where
189 : (
190 : @interface =>
191 : @interface.IsGenericType &&
192 : (
193 : @interface.GetGenericTypeDefinition() == typeof(IEventHandler<,>)
194 : )
195 : );
196 : }
197 :
198 0 : protected virtual IEnumerable<Type> ResolveCommandHandlerInterface(Type type)
199 : {
200 : return type
201 : .GetInterfaces()
202 : .Where
203 : (
204 : @interface =>
205 : @interface.IsGenericType &&
206 : (
207 : @interface.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
208 : )
209 : );
210 : }
211 : }
212 : }
|