//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Practices.Composite.Logging;
using Microsoft.Practices.Composite.Properties;
namespace Microsoft.Practices.Composite.Modularity
{
/// <summary>
/// Component responsible for coordinating the modules' type loading and module initialization process.
/// </summary>
public partial class ModuleManager : IModuleManager
{
private readonly IModuleInitializer moduleInitializer;
private readonly IModuleCatalog moduleCatalog;
private readonly ILoggerFacade loggerFacade;
private IEnumerable<IModuleTypeLoader> typeLoaders;
/// <summary>
/// Initializes an instance of the <see cref="ModuleManager"/> class.
/// </summary>
/// <param name="moduleInitializer">Service used for initialization of modules.</param>
/// <param name="moduleCatalog">Catalog that enumerates the modules to be loaded and initialized.</param>
/// <param name="loggerFacade">Logger used during the load and initialization of modules.</param>
public ModuleManager(IModuleInitializer moduleInitializer, IModuleCatalog moduleCatalog, ILoggerFacade loggerFacade)
{
if (moduleInitializer == null)
{
throw new ArgumentNullException("moduleInitializer");
}
if (moduleCatalog == null)
{
throw new ArgumentNullException("moduleCatalog");
}
if (loggerFacade == null)
{
throw new ArgumentNullException("loggerFacade");
}
this.moduleInitializer = moduleInitializer;
this.moduleCatalog = moduleCatalog;
this.loggerFacade = loggerFacade;
}
/// <summary>
/// Initializes the modules marked as <see cref="InitializationMode.WhenAvailable"/> on the <see cref="ModuleCatalog"/>.
/// </summary>
public void Run()
{
this.moduleCatalog.Initialize();
this.LoadModulesWhenAvailable();
}
/// <summary>
/// Initializes the module on the <see cref="ModuleCatalog"/> with the name <paramref name="moduleName"/>.
/// </summary>
/// <param name="moduleName">Name of the module requested for initialization.</param>
public void LoadModule(string moduleName)
{
IEnumerable<ModuleInfo> module = this.moduleCatalog.Modules.Where(m => m.ModuleName == moduleName);
if (module == null || module.Count() != 1)
{
throw new ModuleNotFoundException(moduleName, string.Format(CultureInfo.CurrentCulture, Resources.ModuleNotFound, moduleName));
}
IEnumerable<ModuleInfo> modulesToLoad = this.moduleCatalog.CompleteListWithDependencies(module);
this.LoadModuleTypes(modulesToLoad);
}
/// <summary>
/// Checks if the module needs to be retrieved before it's initialized.
/// </summary>
/// <param name="moduleInfo">Module that is being checked if needs retrieval.</param>
/// <returns></returns>
protected virtual bool ModuleNeedsRetrieval(ModuleInfo moduleInfo)
{
if (moduleInfo.State == ModuleState.NotStarted)
{
// If we can instantiate the type, that means the module's assembly is already loaded into
// the AppDomain and we don't need to retrieve it.
bool isAvailable = Type.GetType(moduleInfo.ModuleType) != null;
if (isAvailable)
{
moduleInfo.State = ModuleState.ReadyForInitialization;
}
return !isAvailable;
}
return false;
}
/// <summary>
/// Handles any exception ocurred in the module typeloading process,
/// logs the error using the <seealso cref="ILoggerFacade"/> and throws a <seealso cref="ModuleTypeLoadingException"/>.
/// This method can be overriden to provide a different behavior.
/// </summary>
/// <param name="moduleInfo">The module metadata where the error happenened.</param>
/// <param name="exception">The exception thrown that is the cause of the current error.</param>
/// <exception cref="ModuleTypeLoadingException"></exception>
protected virtual void HandleModuleTypeLoadingError(ModuleInfo moduleInfo, Exception exception)
{
ModuleTypeLoadingException moduleTypeLoadingException = exception as ModuleTypeLoadingException;
if (moduleTypeLoadingException == null)
{
moduleTypeLoadingException = new ModuleTypeLoadingException(moduleInfo.ModuleName, exception.Message, exception);
}
this.loggerFacade.Log(moduleTypeLoadingException.Message, Category.Exception, Priority.High);
throw moduleTypeLoadingException;
}
private void LoadModulesWhenAvailable()
{
IEnumerable<ModuleInfo> whenAvailableModules = this.moduleCatalog.Modules.Where(m => m.InitializationMode == InitializationMode.WhenAvailable);
IEnumerable<ModuleInfo> modulesToLoadTypes = this.moduleCatalog.CompleteListWithDependencies(whenAvailableModules);
if (modulesToLoadTypes != null)
{
this.LoadModuleTypes(modulesToLoadTypes);
}
}
private void LoadModuleTypes(IEnumerable<ModuleInfo> moduleInfos)
{
if (moduleInfos == null)
{
return;
}
foreach (ModuleInfo moduleInfo in moduleInfos)
{
if (moduleInfo.State == ModuleState.NotStarted)
{
if (this.ModuleNeedsRetrieval(moduleInfo))
{
this.BeginRetrievingModule(moduleInfo);
}
else
{
moduleInfo.State = ModuleState.ReadyForInitialization;
}
}
}
this.LoadModulesThatAreReadyForLoad();
}
private void BeginRetrievingModule(ModuleInfo moduleInfo)
{
ModuleInfo moduleInfoToLoadType = moduleInfo;
IModuleTypeLoader moduleTypeLoader = this.GetTypeLoaderForModule(moduleInfoToLoadType);
moduleInfoToLoadType.State = ModuleState.LoadingTypes;
moduleTypeLoader.BeginLoadModuleType(moduleInfo, this.OnModuleTypeLoaded);
}
private void OnModuleTypeLoaded(ModuleInfo typeLoadedModuleInfo, Exception error)
{
if (error == null)
{
typeLoadedModuleInfo.State = ModuleState.ReadyForInitialization;
// This callback may call back on the UI thread, but we are not guaranteeing it.
// If you were to add a custom retriever that retrieved in the background, you
// would need to consider dispatching to the UI thread.
this.LoadModulesThatAreReadyForLoad();
}
else
{
this.HandleModuleTypeLoadingError(typeLoadedModuleInfo, error);
}
}
private void LoadModulesThatAreReadyForLoad()
{
bool keepLoading = true;
while (keepLoading)
{
keepLoading = false;
IEnumerable<ModuleInfo> availableModules = this.moduleCatalog.Modules.Where(m => m.State == ModuleState.ReadyForInitialization);
foreach (ModuleInfo moduleInfo in availableModules)
{
if (this.AreDependenciesLoaded(moduleInfo))
{
moduleInfo.State = ModuleState.Initializing;
this.InitializeModule(moduleInfo);
keepLoading = true;
break;
}
}
}
}
private bool AreDependenciesLoaded(ModuleInfo moduleInfo)
{
IEnumerable<ModuleInfo> requiredModules = this.moduleCatalog.GetDependentModules(moduleInfo);
if (requiredModules == null)
{
return true;
}
int notReadyRequiredModuleCount =
requiredModules.Count(requiredModule => requiredModule.State != ModuleState.Initialized);
return notReadyRequiredModuleCount == 0;
}
private IModuleTypeLoader GetTypeLoaderForModule(ModuleInfo moduleInfo)
{
foreach (IModuleTypeLoader typeLoader in this.ModuleTypeLoaders)
{
if (typeLoader.CanLoadModuleType(moduleInfo))
{
return typeLoader;
}
}
throw new ModuleTypeLoaderNotFoundException(moduleInfo.ModuleName, String.Format(CultureInfo.CurrentCulture, Resources.NoRetrieverCanRetrieveModule, moduleInfo.ModuleName), null);
}
private void InitializeModule(ModuleInfo moduleInfo)
{
if (moduleInfo.State == ModuleState.Initializing)
{
this.moduleInitializer.Initialize(moduleInfo);
moduleInfo.State = ModuleState.Initialized;
}
}
}
}