PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ReferenceImpl/BankBranchClient/Source/Infrastructure/Library/Services/DependentModuleLoaderService.cs

http://scsf2012.codeplex.com
C# | 495 lines | 363 code | 87 blank | 45 comment | 39 complexity | a94f199c8d3a7ba6e7bf952c3acda8a9 MD5 | raw file
  1. //===============================================================================
  2. // Microsoft patterns & practices
  3. // Smart Client Software Factory 2010
  4. //===============================================================================
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9. // FITNESS FOR A PARTICULAR PURPOSE.
  10. //===============================================================================
  11. // The example companies, organizations, products, domain names,
  12. // e-mail addresses, logos, people, places, and events depicted
  13. // herein are fictitious. No association with any real company,
  14. // organization, product, domain name, email address, logo, person,
  15. // places, or events is intended or should be inferred.
  16. //===============================================================================
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Reflection;
  23. using System.Threading;
  24. using Microsoft.Practices.CompositeUI;
  25. using Microsoft.Practices.CompositeUI.Configuration;
  26. using Microsoft.Practices.CompositeUI.Services;
  27. using Microsoft.Practices.CompositeUI.Utility;
  28. using Microsoft.Practices.ObjectBuilder;
  29. namespace GlobalBank.Infrastructure.Library.Services
  30. {
  31. /// <summary>
  32. /// Service to load modules into the application.
  33. /// </summary>
  34. public class DependentModuleLoaderService : IModuleLoaderService
  35. {
  36. Dictionary<Assembly, ModuleMetadata> loadedModules = new Dictionary<Assembly, ModuleMetadata>();
  37. TraceSource traceSource = null;
  38. /// <summary>
  39. /// Initializes a new instance of the <see cref="ModuleLoaderService"/> class with the
  40. /// provided trace source.
  41. /// </summary>
  42. /// <param name="traceSource">The trace source for tracing. If null is
  43. /// passed, the service does not perform tracing.</param>
  44. [InjectionConstructor]
  45. public DependentModuleLoaderService([ClassNameTraceSource] TraceSource traceSource)
  46. {
  47. this.traceSource = traceSource;
  48. }
  49. /// <summary>
  50. /// See <see cref="IModuleLoaderService.ModuleLoaded"/> for more information.
  51. /// </summary>
  52. public event EventHandler<DataEventArgs<LoadedModuleInfo>> ModuleLoaded;
  53. /// <summary>
  54. /// See <see cref="IModuleLoaderService.LoadedModules"/> for more information.
  55. /// </summary>
  56. public IList<LoadedModuleInfo> LoadedModules
  57. {
  58. get
  59. {
  60. List<LoadedModuleInfo> result = new List<LoadedModuleInfo>();
  61. foreach (ModuleMetadata module in loadedModules.Values)
  62. result.Add(module.ToLoadedModuleInfo());
  63. return result.AsReadOnly();
  64. }
  65. }
  66. /// <summary>
  67. /// See <see cref="IModuleLoaderService.Load(WorkItem, IModuleInfo[])"/> for more information.
  68. /// </summary>
  69. public void Load(WorkItem workItem, params IModuleInfo[] modules)
  70. {
  71. Guard.ArgumentNotNull(workItem, "workItem");
  72. Guard.ArgumentNotNull(modules, "modules");
  73. InnerLoad(workItem, modules);
  74. }
  75. /// <summary>
  76. /// See <see cref="IModuleLoaderService.Load(WorkItem, Assembly[])"/> for more information.
  77. /// </summary>
  78. public void Load(WorkItem workItem, params Assembly[] assemblies)
  79. {
  80. Guard.ArgumentNotNull(workItem, "workItem");
  81. Guard.ArgumentNotNull(assemblies, "assemblies");
  82. List<IModuleInfo> modules = new List<IModuleInfo>();
  83. foreach (Assembly assembly in assemblies)
  84. modules.Add(new ModuleInfo(assembly));
  85. InnerLoad(workItem, modules.ToArray());
  86. }
  87. /// <summary>
  88. /// Fires the ModuleLoaded event.
  89. /// </summary>
  90. /// <param name="module">The module that was loaded.</param>
  91. protected virtual void OnModuleLoaded(LoadedModuleInfo module)
  92. {
  93. if (ModuleLoaded != null)
  94. ModuleLoaded(this, new DataEventArgs<LoadedModuleInfo>(module));
  95. }
  96. private void InnerLoad(WorkItem workItem, IModuleInfo[] modules)
  97. {
  98. if (modules.Length == 0)
  99. return;
  100. IModuleInfo[] allowedModules = FilterModulesBasedOnRole(modules);
  101. LoadAssemblies(allowedModules);
  102. List<ModuleMetadata> loadOrder = GetLoadOrder();
  103. foreach (ModuleMetadata module in loadOrder)
  104. module.LoadServices(workItem);
  105. foreach (ModuleMetadata module in loadOrder)
  106. module.InitializeWorkItemExtensions(workItem);
  107. foreach (ModuleMetadata module in loadOrder)
  108. module.InitializeModuleClasses(workItem);
  109. foreach (ModuleMetadata module in loadOrder)
  110. module.NotifyOfLoadedModule(OnModuleLoaded);
  111. }
  112. private IModuleInfo[] FilterModulesBasedOnRole(IModuleInfo[] modules)
  113. {
  114. List<IModuleInfo> allowedModules = new List<IModuleInfo>();
  115. foreach (IModuleInfo module in modules)
  116. {
  117. if (module.AllowedRoles.Count == 0)
  118. allowedModules.Add(module);
  119. else
  120. {
  121. foreach (string role in module.AllowedRoles)
  122. {
  123. if (Thread.CurrentPrincipal.IsInRole(role))
  124. {
  125. allowedModules.Add(module);
  126. break;
  127. }
  128. }
  129. }
  130. }
  131. return allowedModules.ToArray();
  132. }
  133. private List<ModuleMetadata> GetLoadOrder()
  134. {
  135. Dictionary<string, ModuleMetadata> indexedInfo = new Dictionary<string, ModuleMetadata>();
  136. ModuleDependencySolver solver = new ModuleDependencySolver();
  137. List<ModuleMetadata> result = new List<ModuleMetadata>();
  138. foreach (ModuleMetadata data in loadedModules.Values)
  139. {
  140. if (indexedInfo.ContainsKey(data.Name))
  141. throw new ModuleLoadException(String.Format(CultureInfo.CurrentCulture,
  142. Properties.Resources.DuplicatedModule, data.Name));
  143. indexedInfo.Add(data.Name, data);
  144. solver.AddModule(data.Name);
  145. foreach (string dependency in data.Dependencies)
  146. solver.AddDependency(data.Name, dependency);
  147. }
  148. if (solver.ModuleCount > 0)
  149. {
  150. string[] loadOrder = solver.Solve();
  151. for (int i = 0; i < loadOrder.Length; i++)
  152. result.Add(indexedInfo[loadOrder[i]]);
  153. }
  154. return result;
  155. }
  156. private void LoadAssemblies(IModuleInfo[] modules)
  157. {
  158. foreach (IModuleInfo module in modules)
  159. {
  160. GuardLegalAssemblyFile(module);
  161. Assembly assembly = LoadAssembly(module.AssemblyFile);
  162. if (!loadedModules.ContainsKey(assembly))
  163. loadedModules.Add(assembly, new ModuleMetadata(assembly, traceSource, module));
  164. }
  165. }
  166. private Assembly LoadAssembly(string assemblyFile)
  167. {
  168. Guard.ArgumentNotNullOrEmptyString(assemblyFile, "assemblyFile");
  169. assemblyFile = GetModulePath(assemblyFile);
  170. FileInfo file = new FileInfo(assemblyFile);
  171. Assembly assembly = null;
  172. try
  173. {
  174. assembly = Assembly.LoadFrom(file.FullName);
  175. }
  176. catch (Exception ex)
  177. {
  178. throw new ModuleLoadException(assemblyFile, ex.Message, ex);
  179. }
  180. if (traceSource != null)
  181. traceSource.TraceInformation(Properties.Resources.LogModuleAssemblyLoaded, file.FullName);
  182. return assembly;
  183. }
  184. private string GetModulePath(string assemblyFile)
  185. {
  186. if (!Path.IsPathRooted(assemblyFile))
  187. assemblyFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFile);
  188. return assemblyFile;
  189. }
  190. #region Guards
  191. private void GuardLegalAssemblyFile(IModuleInfo modInfo)
  192. {
  193. Guard.ArgumentNotNull(modInfo, "modInfo");
  194. Guard.ArgumentNotNull(modInfo.AssemblyFile, "modInfo.AssemblyFile");
  195. string assemblyFilePath = GetModulePath(modInfo.AssemblyFile);
  196. if (!File.Exists(assemblyFilePath))
  197. throw new ModuleLoadException(
  198. string.Format(CultureInfo.CurrentCulture,
  199. Properties.Resources.ModuleNotFound, assemblyFilePath));
  200. }
  201. #endregion
  202. #region Helper classes
  203. class ModuleMetadata
  204. {
  205. Assembly assembly;
  206. bool loadedServices = false;
  207. bool extensionsInitialized = false;
  208. bool modulesInitialzed = false;
  209. string name = null;
  210. bool notified = false;
  211. List<string> dependencies = new List<string>();
  212. List<Type> moduleTypes = new List<Type>();
  213. List<IModule> moduleClasses = new List<IModule>();
  214. List<string> roles = new List<string>();
  215. List<ServiceMetadata> services = new List<ServiceMetadata>();
  216. List<KeyValuePair<Type, Type>> workItemExtensions = new List<KeyValuePair<Type, Type>>();
  217. List<Type> workItemRootExtensions = new List<Type>();
  218. TraceSource traceSource;
  219. public ModuleMetadata(Assembly assembly, TraceSource traceSource, IModuleInfo moduleInfo)
  220. {
  221. this.assembly = assembly;
  222. this.traceSource = traceSource;
  223. if (moduleInfo is IDependentModuleInfo)
  224. {
  225. name = ((IDependentModuleInfo) moduleInfo).Name;
  226. dependencies.AddRange(((IDependentModuleInfo) moduleInfo).Dependencies);
  227. }
  228. else
  229. {
  230. foreach (ModuleAttribute attr in assembly.GetCustomAttributes(typeof (ModuleAttribute), true))
  231. name = attr.Name;
  232. foreach (ModuleDependencyAttribute attr in assembly.GetCustomAttributes(typeof (ModuleDependencyAttribute), true))
  233. dependencies.Add(attr.Name);
  234. }
  235. foreach (Type type in assembly.GetExportedTypes())
  236. {
  237. foreach (ServiceAttribute attr in type.GetCustomAttributes(typeof (ServiceAttribute), true))
  238. services.Add(new ServiceMetadata(type, attr.RegisterAs ?? type, attr.AddOnDemand));
  239. foreach (WorkItemExtensionAttribute attr in type.GetCustomAttributes(typeof (WorkItemExtensionAttribute), true))
  240. workItemExtensions.Add(new KeyValuePair<Type, Type>(attr.WorkItemType, type));
  241. foreach (
  242. RootWorkItemExtensionAttribute attr in type.GetCustomAttributes(typeof (RootWorkItemExtensionAttribute), true))
  243. workItemRootExtensions.Add(type);
  244. if (!type.IsAbstract && typeof (IModule).IsAssignableFrom(type))
  245. moduleTypes.Add(type);
  246. }
  247. }
  248. public IEnumerable<string> Dependencies
  249. {
  250. get { return dependencies; }
  251. }
  252. public string Name
  253. {
  254. get
  255. {
  256. if (name == null)
  257. name = assembly.FullName;
  258. return name;
  259. }
  260. set { name = value; }
  261. }
  262. public void LoadServices(WorkItem workItem)
  263. {
  264. if (loadedServices)
  265. return;
  266. loadedServices = true;
  267. EnsureModuleClassesExist(workItem);
  268. try
  269. {
  270. foreach (IModule moduleClass in moduleClasses)
  271. {
  272. moduleClass.AddServices();
  273. if (traceSource != null)
  274. traceSource.TraceInformation(Properties.Resources.AddServicesCalled, moduleClass.GetType());
  275. }
  276. foreach (ServiceMetadata svc in services)
  277. {
  278. if (svc.AddOnDemand)
  279. {
  280. workItem.Services.AddOnDemand(svc.InstanceType, svc.RegistrationType);
  281. if (traceSource != null)
  282. traceSource.TraceInformation(Properties.Resources.ServiceAddedOnDemand, Name, svc.InstanceType);
  283. }
  284. else
  285. {
  286. workItem.Services.AddNew(svc.InstanceType, svc.RegistrationType);
  287. if (traceSource != null)
  288. traceSource.TraceInformation(Properties.Resources.ServiceAdded, Name, svc.InstanceType);
  289. }
  290. }
  291. }
  292. catch (Exception ex)
  293. {
  294. ThrowModuleLoadException(ex);
  295. }
  296. }
  297. private void EnsureModuleClassesExist(WorkItem workItem)
  298. {
  299. if (moduleClasses.Count == moduleTypes.Count)
  300. return;
  301. try
  302. {
  303. foreach (Type moduleType in moduleTypes)
  304. {
  305. IModule module = (IModule) workItem.Items.AddNew(moduleType);
  306. moduleClasses.Add(module);
  307. if (traceSource != null)
  308. traceSource.TraceInformation(Properties.Resources.LogModuleAdded, moduleType);
  309. }
  310. }
  311. catch (FileNotFoundException ex)
  312. {
  313. ThrowModuleReferenceException(ex);
  314. }
  315. catch (Exception ex)
  316. {
  317. ThrowModuleLoadException(ex);
  318. }
  319. }
  320. public void InitializeModuleClasses(WorkItem workItem)
  321. {
  322. if (modulesInitialzed)
  323. return;
  324. modulesInitialzed = true;
  325. EnsureModuleClassesExist(workItem);
  326. try
  327. {
  328. foreach (IModule module in moduleClasses)
  329. {
  330. module.Load();
  331. if (traceSource != null)
  332. traceSource.TraceInformation(Properties.Resources.ModuleStartCalled, module.GetType());
  333. }
  334. }
  335. catch (FileNotFoundException ex)
  336. {
  337. ThrowModuleReferenceException(ex);
  338. }
  339. catch (Exception ex)
  340. {
  341. ThrowModuleLoadException(ex);
  342. }
  343. }
  344. public void InitializeWorkItemExtensions(WorkItem workItem)
  345. {
  346. if (extensionsInitialized)
  347. return;
  348. extensionsInitialized = true;
  349. IWorkItemExtensionService svc = workItem.Services.Get<IWorkItemExtensionService>();
  350. if (svc == null)
  351. return;
  352. foreach (KeyValuePair<Type, Type> kvp in workItemExtensions)
  353. svc.RegisterExtension(kvp.Key, kvp.Value);
  354. foreach (Type type in workItemRootExtensions)
  355. svc.RegisterRootExtension(type);
  356. }
  357. public void NotifyOfLoadedModule(Action<LoadedModuleInfo> action)
  358. {
  359. if (notified)
  360. return;
  361. notified = true;
  362. action(ToLoadedModuleInfo());
  363. }
  364. public LoadedModuleInfo ToLoadedModuleInfo()
  365. {
  366. return new LoadedModuleInfo(assembly, Name, roles, dependencies);
  367. }
  368. private void ThrowModuleLoadException(Exception innerException)
  369. {
  370. throw new ModuleLoadException(Name,
  371. String.Format(CultureInfo.CurrentCulture,
  372. Properties.Resources.FailedToLoadModule,
  373. assembly.FullName, innerException.Message),
  374. innerException);
  375. }
  376. private void ThrowModuleReferenceException(Exception innerException)
  377. {
  378. throw new ModuleLoadException(Name,
  379. Properties.Resources.ReferencedAssemblyNotFound,
  380. innerException);
  381. }
  382. }
  383. class ServiceMetadata
  384. {
  385. public bool AddOnDemand = false;
  386. public Type InstanceType = null;
  387. public Type RegistrationType = null;
  388. public ServiceMetadata(Type instanceType, Type registrationType, bool addOnDemand)
  389. {
  390. this.InstanceType = instanceType;
  391. this.RegistrationType = registrationType;
  392. this.AddOnDemand = addOnDemand;
  393. }
  394. }
  395. class ClassNameTraceSourceAttribute : TraceSourceAttribute
  396. {
  397. /// <summary>
  398. /// Initializes the attribute using the <see cref="IModuleLoaderService"/>
  399. /// interface namespace as the source name.
  400. /// </summary>
  401. public ClassNameTraceSourceAttribute()
  402. : base(typeof (ModuleLoaderService).FullName)
  403. {
  404. }
  405. }
  406. #endregion
  407. }
  408. }