#region License Information /* HeuristicLab * Copyright (C) 2002-2011 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 SandboxApplicationManager 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 SandboxApplicationManager is used in sandboxed Application Domains where permissions are restricted and /// only partially-trusted code can be executed. /// internal class SandboxApplicationManager : 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 protected internal 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 SandboxApplicationManager() : base() { loadedAssemblies = new Dictionary(); loadedPlugins = new List(); } /// /// 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) { // load assembly reflection only first to get the full assembly name var reflectionOnlyAssembly = Assembly.ReflectionOnlyLoadFrom(fileName); // load the assembly into execution context using full assembly name var asm = Assembly.Load(reflectionOnlyAssembly.FullName); 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)); } } } // register assembly in the assembly cache for the AssemblyResolveEvent private void RegisterLoadedAssembly(Assembly asm) { if (loadedAssemblies.ContainsKey(asm.FullName) || loadedAssemblies.ContainsKey(asm.GetName().Name)) { throw new ArgumentException("An assembly with the name " + asm.GetName().Name + " has been registered already.", "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; } internal static IEnumerable GetTypes(IEnumerable types, bool onlyInstantiable, bool assignableToAllTypes) { IEnumerable result = GetTypes(types.First(), onlyInstantiable); foreach (Type type in types.Skip(1)) { IEnumerable discoveredTypes = GetTypes(type, onlyInstantiable); if (assignableToAllTypes) result = result.Intersect(discoveredTypes); else result = result.Union(discoveredTypes); } return result; } /// /// 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; } internal static IEnumerable GetTypes(IEnumerable types, IPluginDescription pluginDescription, bool onlyInstantiable, bool assignableToAllTypes) { IEnumerable result = GetTypes(types.First(), pluginDescription, onlyInstantiable); foreach (Type type in types.Skip(1)) { IEnumerable discoveredTypes = GetTypes(type, pluginDescription, onlyInstantiable); if (assignableToAllTypes) result = result.Intersect(discoveredTypes); else result = result.Union(discoveredTypes); } return result; } 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) { var buildTypes = from t in assembly.GetTypes() where !IsNonDiscoverableType(t) where CheckTypeCompatibility(type, t) where onlyInstantiable == false || (!t.IsAbstract && !t.IsInterface && !t.HasElementType) select BuildType(t, type); return from t in buildTypes where onlyInstantiable == false || !t.IsGenericTypeDefinition select t; } private static bool IsNonDiscoverableType(Type t) { return t.GetCustomAttributes(typeof(NonDiscoverableTypeAttribute), false).Any(); } 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); } #region IApplicationManager Members IEnumerable IApplicationManager.GetInstances() { return GetInstances(); } IEnumerable IApplicationManager.GetInstances(Type type) { return GetInstances(type); } IEnumerable IApplicationManager.GetTypes(Type type, bool onlyInstantiable) { return GetTypes(type, onlyInstantiable); } IEnumerable IApplicationManager.GetTypes(IEnumerable types, bool onlyInstantiable, bool assignableToAllTypes) { return GetTypes(types, onlyInstantiable, assignableToAllTypes); } IEnumerable IApplicationManager.GetTypes(Type type, IPluginDescription plugin, bool onlyInstantiable) { return GetTypes(type, plugin, onlyInstantiable); } IEnumerable IApplicationManager.GetTypes(IEnumerable types, IPluginDescription plugin, bool onlyInstantiable, bool assignableToAllTypes) { return GetTypes(types, plugin, onlyInstantiable, assignableToAllTypes); } /// /// 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 } }