#region License Information /* HeuristicLab * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using HeuristicLab.PluginInfrastructure.Manager; namespace HeuristicLab.PluginInfrastructure { /// /// The DefaultApplicationManager provides properties to retrieve the list of available plugins and applications. /// It also provides methods for type discovery and instantiation for types declared in plugins. /// The DefaultApplicationManager is registered as ApplicationManager.Manager singleton for each HL application /// started via the plugin infrastructure. /// internal sealed class DefaultApplicationManager : MarshalByRefObject, IApplicationManager { /// /// Fired when a plugin is loaded. /// internal event EventHandler PluginLoaded; /// /// Fired when a plugin is unloaded (when the application terminates). /// internal event EventHandler PluginUnloaded; // cache for the AssemblyResolveEvent // which must be handled when assemblies are loaded dynamically after the application start private Dictionary loadedAssemblies; private List loadedPlugins; private List plugins; /// /// Gets all plugins. /// public IEnumerable Plugins { get { return plugins.Cast(); } } private List applications; /// /// Gets all installed applications. /// public IEnumerable Applications { get { return applications.Cast(); } } internal DefaultApplicationManager() : base() { loadedAssemblies = new Dictionary(); loadedPlugins = new List(); AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { if (loadedAssemblies.ContainsKey(args.Name)) { return loadedAssemblies[args.Name]; } return null; }; } /// /// Prepares the application domain for the execution of an HL application. /// Pre-loads all . /// /// Enumerable of available HL applications. /// Enumerable of plugins that should be pre-loaded. internal void PrepareApplicationDomain(IEnumerable apps, IEnumerable plugins) { this.plugins = new List(plugins); this.applications = new List(apps); ApplicationManager.RegisterApplicationManager(this); LoadPlugins(plugins); } /// /// Loads the into this application domain. /// /// Enumerable of plugins that should be loaded. private void LoadPlugins(IEnumerable plugins) { // load all loadable plugins (all dependencies available) into the execution context foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) { foreach (string fileName in desc.AssemblyLocations) { var asm = Assembly.LoadFrom(fileName); RegisterLoadedAssembly(asm); // instantiate and load all plugins in this assembly foreach (var plugin in GetInstances(asm)) { plugin.OnLoad(); loadedPlugins.Add(plugin); } } OnPluginLoaded(new PluginInfrastructureEventArgs(desc)); desc.Load(); } } /// /// Runs the application declared in . /// This is a synchronous call. When the application is terminated all plugins are unloaded. /// /// Description of the application to run internal void Run(ApplicationDescription appInfo) { IApplication runnablePlugin = (IApplication)Activator.CreateInstance(appInfo.DeclaringAssemblyName, appInfo.DeclaringTypeName).Unwrap(); try { runnablePlugin.Run(); } finally { // unload plugins in reverse order foreach (var plugin in loadedPlugins.Reverse()) { plugin.OnUnload(); } foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) { desc.Unload(); OnPluginUnloaded(new PluginInfrastructureEventArgs(desc)); } } } /// /// Loads raw assemblies dynamically from a byte array /// /// bytearray of all raw assemblies that should be loaded internal void LoadAssemblies(IEnumerable assemblies) { foreach (byte[] asm in assemblies) { Assembly loadedAsm = Assembly.Load(asm); RegisterLoadedAssembly(loadedAsm); } } // register assembly in the assembly cache for the AssemblyResolveEvent private void RegisterLoadedAssembly(Assembly asm) { loadedAssemblies.Add(asm.FullName, asm); loadedAssemblies.Add(asm.GetName().Name, asm); // add short name } /// /// Creates an instance of all types that are subtypes or the same type of the specified type and declared in /// /// Most general type. /// Enumerable of the created instances. internal static IEnumerable GetInstances(IPluginDescription plugin) where T : class { List instances = new List(); foreach (Type t in GetTypes(typeof(T), plugin, true)) { T instance = null; try { instance = (T)Activator.CreateInstance(t); } catch { } if (instance != null) instances.Add(instance); } return instances; } /// /// Creates an instance of all types declared in assembly that are subtypes or the same type of the specified . /// /// Most general type. /// Declaring assembly. /// Enumerable of the created instances. private static IEnumerable GetInstances(Assembly asm) where T : class { List instances = new List(); foreach (Type t in GetTypes(typeof(T), asm, true)) { T instance = null; try { instance = (T)Activator.CreateInstance(t); } catch { } if (instance != null) instances.Add(instance); } return instances; } /// /// Creates an instance of all types that are subtypes or the same type of the specified type /// /// Most general type. /// Enumerable of the created instances. internal static IEnumerable GetInstances() where T : class { return from i in GetInstances(typeof(T)) select (T)i; } /// /// Creates an instance of all types that are subtypes or the same type of the specified type /// /// Most general type. /// Enumerable of the created instances. internal static IEnumerable GetInstances(Type type) { List instances = new List(); foreach (Type t in GetTypes(type, true)) { object instance = null; try { instance = Activator.CreateInstance(t); } catch { } if (instance != null) instances.Add(instance); } return instances; } /// /// Finds all types that are subtypes or equal to the specified type. /// /// Most general type for which to find matching types. /// Return only types that are instantiable /// (interfaces, abstract classes... are not returned) /// Enumerable of the discovered types. internal static IEnumerable GetTypes(Type type, bool onlyInstantiable) { return from asm in AppDomain.CurrentDomain.GetAssemblies() from t in GetTypes(type, asm, onlyInstantiable) select t; } /// /// Finds all types that are subtypes or equal to the specified type if they are part of the given /// . /// /// Most general type for which to find matching types. /// The plugin the subtypes must be part of. /// Return only types that are instantiable /// (interfaces, abstract classes... are not returned) /// Enumerable of the discovered types. internal static IEnumerable GetTypes(Type type, IPluginDescription pluginDescription, bool onlyInstantiable) { PluginDescription pluginDesc = (PluginDescription)pluginDescription; return from asm in AppDomain.CurrentDomain.GetAssemblies() where !IsDynamicAssembly(asm) where pluginDesc.AssemblyLocations.Any(location => location.Equals(Path.GetFullPath(asm.Location), StringComparison.CurrentCultureIgnoreCase)) from t in GetTypes(type, asm, onlyInstantiable) select t; } private static bool IsDynamicAssembly(Assembly asm) { return (asm is System.Reflection.Emit.AssemblyBuilder) || string.IsNullOrEmpty(asm.Location); } /// /// Gets types that are assignable (same of subtype) to the specified type only from the given assembly. /// /// Most general type we want to find. /// Assembly that should be searched for types. /// Return only types that are instantiable /// (interfaces, abstract classes... are not returned) /// Enumerable of the discovered types. private static IEnumerable GetTypes(Type type, Assembly assembly, bool onlyInstantiable) { return from t in assembly.GetTypes() where CheckTypeCompatibility(type, t) where onlyInstantiable == false || (!t.IsAbstract && !t.IsInterface && !t.HasElementType) select BuildType(t, type); } private static bool CheckTypeCompatibility(Type type, Type other) { if (type.IsAssignableFrom(other)) return true; if (type.IsGenericType && other.IsGenericType) { try { if (type.IsAssignableFrom(other.GetGenericTypeDefinition().MakeGenericType(type.GetGenericArguments()))) return true; } catch (Exception) { } } return false; } private static Type BuildType(Type type, Type protoType) { if (type.IsGenericType && protoType.IsGenericType) return type.GetGenericTypeDefinition().MakeGenericType(protoType.GetGenericArguments()); else return type; } private void OnPluginLoaded(PluginInfrastructureEventArgs e) { if (PluginLoaded != null) PluginLoaded(this, e); } private void OnPluginUnloaded(PluginInfrastructureEventArgs e) { if (PluginUnloaded != null) PluginUnloaded(this, e); } // infinite lease time /// /// Initializes the life time service with infinite lease time. /// /// null. public override object InitializeLifetimeService() { return null; } #region IApplicationManager Members IEnumerable IApplicationManager.GetInstances() { return GetInstances(); } IEnumerable IApplicationManager.GetInstances(Type type) { return GetInstances(type); } IEnumerable IApplicationManager.GetTypes(Type type) { return GetTypes(type, true); } IEnumerable IApplicationManager.GetTypes(Type type, bool onlyInstantiable) { return GetTypes(type, onlyInstantiable); } IEnumerable IApplicationManager.GetTypes(Type type, IPluginDescription plugin) { return GetTypes(type, plugin, true); } IEnumerable IApplicationManager.GetTypes(Type type, IPluginDescription plugin, bool onlyInstantiable) { return GetTypes(type, plugin, onlyInstantiable); } /// /// Finds the plugin that declares the type. /// /// The type of interest. /// The description of the plugin that declares the given type or null if the type has not been declared by a known plugin. public IPluginDescription GetDeclaringPlugin(Type type) { if (type == null) throw new ArgumentNullException("type"); foreach (PluginDescription info in Plugins) { if (info.AssemblyLocations.Contains(Path.GetFullPath(type.Assembly.Location))) return info; } return null; } #endregion } }