/src/LinFu.IoC/Interceptors/InterceptorAttributeLoader.cs

http://github.com/philiplaureano/LinFu · C# · 268 lines · 146 code · 44 blank · 78 comment · 32 complexity · 987957a0602564b612ea5abcc858efc1 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using LinFu.AOP.Interfaces;
  7. using LinFu.IoC.Configuration;
  8. using LinFu.IoC.Configuration.Interfaces;
  9. using LinFu.IoC.Interfaces;
  10. using LinFu.Proxy.Interfaces;
  11. using LinFu.Reflection;
  12. namespace LinFu.IoC.Interceptors
  13. {
  14. /// <summary>
  15. /// The class responsible for loading interceptors marked with the
  16. /// <see cref="InterceptsAttribute" /> class.
  17. /// </summary>
  18. internal class InterceptorAttributeLoader : ITypeLoader
  19. {
  20. private readonly ILoader<IServiceContainer> _loaderHost;
  21. /// <summary>
  22. /// Initializes the class with the given <paramref name="loaderHost" />.
  23. /// </summary>
  24. /// <param name="loaderHost">
  25. /// The <see cref="ILoader{TTarget}" /> instance that will be responsible for loading the
  26. /// <see cref="IServiceContainer" /> instance itself.
  27. /// </param>
  28. internal InterceptorAttributeLoader(ILoader<IServiceContainer> loaderHost)
  29. {
  30. _loaderHost = loaderHost;
  31. }
  32. /// <summary>
  33. /// Loads an <see cref="IInterceptor" /> derived class into a particular <see cref="IServiceContainer" /> instance
  34. /// so that the current interceptor type can intercept calls made to services created from the given
  35. /// target container.
  36. /// </summary>
  37. /// <param name="input">The interceptor type.</param>
  38. /// <returns>
  39. /// By default, this will always return an empty set of container actions. The actual interceptor itself will be
  40. /// injected at the end of the postprocessor chain.
  41. /// </returns>
  42. public IEnumerable<Action<IServiceContainer>> Load(Type input)
  43. {
  44. var typeInstance = Activator.CreateInstance(input);
  45. var interceptor = typeInstance as IInterceptor;
  46. Func<IServiceRequestResult, IInterceptor> getInterceptor = null;
  47. // Return the interceptor by default
  48. if (interceptor != null)
  49. getInterceptor = result =>
  50. {
  51. var target = result.ActualResult;
  52. var container = result.Container;
  53. var methodInvoke = container.GetService<IMethodInvoke<MethodInfo>>();
  54. var factory = container.GetService<IProxyFactory>();
  55. // Manually initialize the interceptor
  56. var initialize = interceptor as IInitialize;
  57. if (initialize != null)
  58. initialize.Initialize(container);
  59. return new Redirector(() => target, interceptor, factory, methodInvoke);
  60. };
  61. if (typeInstance != null && typeInstance is IAroundInvoke)
  62. {
  63. // Convert the IAroundInvoke instance into
  64. // a running interceptor
  65. var aroundInvoke = typeInstance as IAroundInvoke;
  66. getInterceptor = result =>
  67. {
  68. var container = result.Container;
  69. var methodInvoke = container.GetService<IMethodInvoke<MethodInfo>>();
  70. var factory = container.GetService<IProxyFactory>();
  71. // Manually initialize the interceptor
  72. var initialize = aroundInvoke as IInitialize;
  73. if (initialize != null)
  74. initialize.Initialize(container);
  75. // HACK: The adapter can't be created until runtime since
  76. // the aroundInvoke instance needs an actual target
  77. var target = result.ActualResult;
  78. var adapter = new AroundInvokeAdapter(() => target,
  79. methodInvoke, aroundInvoke);
  80. var redirector = new Redirector(() => target, adapter, factory, methodInvoke);
  81. return redirector;
  82. };
  83. }
  84. // The type must implement either the IInterceptor interface
  85. // or the IAroundInvoke interface
  86. if (getInterceptor == null)
  87. return new Action<IServiceContainer>[0];
  88. // Determine which service types should be intercepted
  89. var attributes =
  90. from attribute in input.GetCustomAttributes(typeof(InterceptsAttribute), false)
  91. let currentAttribute = attribute as InterceptsAttribute
  92. where currentAttribute != null
  93. select currentAttribute;
  94. var interceptedTypes = new Dictionary<Type, HashSet<string>>();
  95. foreach (var attribute in attributes)
  96. {
  97. var serviceName = attribute.ServiceName;
  98. var serviceType = attribute.TargetType;
  99. // Keep track of the service name and service type
  100. // and mark the current type for interception
  101. // using the current interceptor
  102. if (!interceptedTypes.ContainsKey(serviceType))
  103. interceptedTypes[serviceType] = new HashSet<string>();
  104. if (!interceptedTypes[serviceType].Contains(serviceName))
  105. interceptedTypes[serviceType].Add(serviceName);
  106. }
  107. // There must be at least one InterceptsAttribute defined on
  108. // the input type
  109. if (interceptedTypes.Count == 0)
  110. return new Action<IServiceContainer>[0];
  111. // Match the service type with the current type
  112. Func<IServiceRequestResult, bool> filter = request =>
  113. {
  114. var container = request.Container;
  115. // There must be a valid proxy factory
  116. if (container == null ||
  117. !container.Contains(typeof(IProxyFactory)))
  118. return false;
  119. var serviceType = request.ServiceType;
  120. // Ignore requests to intercept IMethodInvoke<MethodInfo>
  121. if (serviceType == typeof(IMethodInvoke<MethodInfo>))
  122. return false;
  123. // Sealed types cannot be proxied by default
  124. if (serviceType.IsSealed)
  125. return false;
  126. // Match any service name if the service name is blank
  127. if (request.ServiceName == null &&
  128. interceptedTypes.ContainsKey(serviceType))
  129. return true;
  130. // Match the service name and type
  131. if (interceptedTypes.ContainsKey(serviceType) &&
  132. interceptedTypes[serviceType].Contains(
  133. request.ServiceName))
  134. return true;
  135. if (!serviceType.IsGenericType)
  136. return false;
  137. // Determine if an interceptor can intercept the
  138. // entire family of generic types
  139. var baseDefinition =
  140. serviceType.GetGenericTypeDefinition();
  141. // The list of intercepted types should contain
  142. // the generic type definition and its matching
  143. // service name
  144. var serviceName = request.ServiceName;
  145. return interceptedTypes.ContainsKey(baseDefinition) &&
  146. interceptedTypes[baseDefinition].Contains(
  147. serviceName);
  148. };
  149. // Create the proxy using the service request
  150. Func<IServiceRequestResult, object> createProxy = request => CreateProxyFrom(request, getInterceptor);
  151. // Place the interceptor at the end of the
  152. // postprocessor chain
  153. var injector = new ProxyInjector(filter, createProxy);
  154. _loaderHost.Plugins.Add(new ProxyContainerPlugin(injector));
  155. return new Action<IServiceContainer>[0];
  156. }
  157. /// <summary>
  158. /// Determines whether or not a target type is an interceptor.
  159. /// </summary>
  160. /// <param name="inputType">The target type currently being tested.</param>
  161. /// <returns>
  162. /// Returns <c>true</c> if the <paramref name="inputType" /> is an interceptor; otherwise, it will return
  163. /// <c>false</c>.
  164. /// </returns>
  165. public bool CanLoad(Type inputType)
  166. {
  167. try
  168. {
  169. var attributes = inputType.GetCustomAttributes(typeof(InterceptsAttribute), false);
  170. if (attributes == null)
  171. attributes = new object[0];
  172. // The target type must have at least one InterceptsAttribute defined
  173. var matches = from attribute in attributes
  174. let currentAttribute = attribute as InterceptsAttribute
  175. where currentAttribute != null
  176. select currentAttribute;
  177. return matches.Count() > 0;
  178. }
  179. catch (TypeInitializationException)
  180. {
  181. // Ignore the error
  182. return false;
  183. }
  184. catch (FileNotFoundException)
  185. {
  186. // Ignore the error
  187. return false;
  188. }
  189. }
  190. /// <summary>
  191. /// Generates a proxy instance from an existing <see cref="IServiceRequestResult" /> instance.
  192. /// </summary>
  193. /// <param name="request">
  194. /// The <see cref="IServiceRequestResult" /> instance that describes the proxy type that must be
  195. /// generated.
  196. /// </param>
  197. /// <param name="getInterceptor">
  198. /// The <see cref="IInterceptor" /> functor that will create the interceptor which will handle
  199. /// all calls made to the proxy instance.
  200. /// </param>
  201. /// <returns>A service proxy.</returns>
  202. private static object CreateProxyFrom(IServiceRequestResult request,
  203. Func<IServiceRequestResult, IInterceptor> getInterceptor)
  204. {
  205. var interceptor = getInterceptor(request);
  206. var container = request.Container;
  207. var proxyFactory =
  208. container.GetService<IProxyFactory>();
  209. // The proxy factory must exist
  210. if (proxyFactory == null)
  211. return null;
  212. // Generate the proxy type
  213. var proxyType = proxyFactory.CreateProxyType(request.ServiceType,
  214. new Type[0]);
  215. // The generated proxy instance
  216. // must implement IProxy
  217. var proxy = Activator.CreateInstance(proxyType) as IProxy;
  218. // Assign the interceptor
  219. if (proxy != null)
  220. proxy.Interceptor = interceptor;
  221. return proxy;
  222. }
  223. }
  224. }