source: branches/RefactorPluginInfrastructure-2522/HeuristicLab.PluginInfrastructure/3.3/SandboxApplicationManager.cs @ 13389

Last change on this file since 13389 was 13389, checked in by gkronber, 4 years ago

#2522: removed Starter form and instead init plugin discovery and launch of application from Startup project (.exe)

File size: 17.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.IO;
25using System.Linq;
26using System.Reflection;
27
28namespace HeuristicLab.PluginInfrastructure {
29
30  /// <summary>
31  /// The SandboxApplicationManager provides properties to retrieve the list of available plugins and applications.
32  /// It also provides methods for type discovery and instantiation for types declared in plugins.
33  /// The SandboxApplicationManager is used in sandboxed Application Domains where permissions are restricted and
34  /// only partially-trusted code can be executed.
35  /// </summary>
36  internal class SandboxApplicationManager : MarshalByRefObject, IApplicationManager {
37    /// <summary>
38    /// Fired when a plugin is loaded.
39    /// </summary>
40    internal event EventHandler<PluginInfrastructureEventArgs> PluginLoaded;
41    /// <summary>
42    /// Fired when a plugin is unloaded (when the application terminates).
43    /// </summary>
44    internal event EventHandler<PluginInfrastructureEventArgs> PluginUnloaded;
45
46    // cache for the AssemblyResolveEvent
47    // which must be handled when assemblies are loaded dynamically after the application start
48    protected internal Dictionary<string, Assembly> loadedAssemblies;
49
50    private List<IPlugin> loadedPlugins;
51
52    private List<PluginDescription> plugins;
53    /// <summary>
54    /// Gets all plugins.
55    /// </summary>
56    public IEnumerable<IPluginDescription> Plugins {
57      get { return plugins.Cast<IPluginDescription>(); }
58    }
59
60    private List<ApplicationDescription> applications;
61    /// <summary>
62    /// Gets all installed applications.
63    /// </summary>
64    public IEnumerable<IApplicationDescription> Applications {
65      get { return applications.Cast<IApplicationDescription>(); }
66    }
67
68    internal SandboxApplicationManager()
69      : base() {
70      loadedAssemblies = new Dictionary<string, Assembly>();
71      loadedPlugins = new List<IPlugin>();
72    }
73
74    /// <summary>
75    /// Prepares the application domain for the execution of an HL application.
76    /// Pre-loads all <paramref name="plugins"/>.
77    /// </summary>
78    /// <param name="apps">Enumerable of available HL applications.</param>
79    /// <param name="plugins">Enumerable of plugins that should be pre-loaded.</param> 
80    internal void PrepareApplicationDomain(IEnumerable<ApplicationDescription> apps, IEnumerable<PluginDescription> plugins) {
81      this.plugins = new List<PluginDescription>(plugins);
82      this.applications = new List<ApplicationDescription>(apps);
83      ApplicationManager.RegisterApplicationManager(this);
84      LoadPlugins(plugins);
85    }
86
87    /// <summary>
88    /// Loads the <paramref name="plugins"/> into this application domain.
89    /// </summary>
90    /// <param name="plugins">Enumerable of plugins that should be loaded.</param>
91    private void LoadPlugins(IEnumerable<PluginDescription> plugins) {
92      // load all loadable plugins (all dependencies available) into the execution context
93      foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
94        foreach (string fileName in desc.AssemblyLocations) {
95          // load assembly reflection only first to get the full assembly name
96          var reflectionOnlyAssembly = Assembly.ReflectionOnlyLoadFrom(fileName);
97          // load the assembly into execution context using full assembly name
98          var asm = Assembly.Load(reflectionOnlyAssembly.FullName);
99          RegisterLoadedAssembly(asm);
100          // instantiate and load all plugins in this assembly
101          foreach (var plugin in GetInstances<IPlugin>(asm)) {
102            plugin.OnLoad();
103            loadedPlugins.Add(plugin);
104          }
105        }
106        OnPluginLoaded(new PluginInfrastructureEventArgs(desc));
107        desc.Load();
108      }
109    }
110
111    /// <summary>
112    /// Runs the application declared in <paramref name="appInfo"/>.
113    /// This is a synchronous call. When the application is terminated all plugins are unloaded.
114    /// </summary>
115    /// <param name="appInfo">Description of the application to run</param>
116    internal void Run(ApplicationDescription appInfo, ICommandLineArgument[] args) {
117      IApplication runnablePlugin = (IApplication)Activator.CreateInstance(appInfo.DeclaringAssemblyName, appInfo.DeclaringTypeName).Unwrap();
118      try {
119        runnablePlugin.Run(args);
120      }
121      finally {
122        // unload plugins in reverse order
123        foreach (var plugin in loadedPlugins.Reverse<IPlugin>()) {
124          plugin.OnUnload();
125        }
126        foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
127          desc.Unload();
128          OnPluginUnloaded(new PluginInfrastructureEventArgs(desc));
129        }
130      }
131    }
132
133    // register assembly in the assembly cache for the AssemblyResolveEvent
134    private void RegisterLoadedAssembly(Assembly asm) {
135      if (loadedAssemblies.ContainsKey(asm.FullName) || loadedAssemblies.ContainsKey(asm.GetName().Name)) {
136        throw new ArgumentException("An assembly with the name " + asm.GetName().Name + " has been registered already.", "asm");
137      }
138      loadedAssemblies.Add(asm.FullName, asm);
139      loadedAssemblies.Add(asm.GetName().Name, asm); // add short name
140    }
141
142    /// <summary>
143    /// Creates an instance of all types that are subtypes or the same type of the specified type and declared in <paramref name="plugin"/>
144    /// </summary>
145    /// <typeparam name="T">Most general type.</typeparam>
146    /// <returns>Enumerable of the created instances.</returns>
147    internal static IEnumerable<T> GetInstances<T>(IPluginDescription plugin) where T : class {
148      List<T> instances = new List<T>();
149      foreach (Type t in GetTypes(typeof(T), plugin, onlyInstantiable: true, includeGenericTypeDefinitions: false)) {
150        T instance = null;
151        try { instance = (T)Activator.CreateInstance(t); }
152        catch { }
153        if (instance != null) instances.Add(instance);
154      }
155      return instances;
156    }
157    /// <summary>
158    /// Creates an instance of all types declared in assembly <paramref name="asm"/> that are subtypes or the same type of the specified <typeparamref name="type"/>.
159    /// </summary>
160    /// <typeparam name="T">Most general type.</typeparam>
161    /// <param name="asm">Declaring assembly.</param>
162    /// <returns>Enumerable of the created instances.</returns>
163    private static IEnumerable<T> GetInstances<T>(Assembly asm) where T : class {
164      List<T> instances = new List<T>();
165      foreach (Type t in GetTypes(typeof(T), asm, onlyInstantiable: true, includeGenericTypeDefinitions: false)) {
166        T instance = null;
167        try { instance = (T)Activator.CreateInstance(t); }
168        catch { }
169        if (instance != null) instances.Add(instance);
170      }
171      return instances;
172    }
173    /// <summary>
174    /// Creates an instance of all types that are subtypes or the same type of the specified type
175    /// </summary>
176    /// <typeparam name="T">Most general type.</typeparam>
177    /// <returns>Enumerable of the created instances.</returns>
178    internal static IEnumerable<T> GetInstances<T>() where T : class {
179      return from i in GetInstances(typeof(T))
180             select (T)i;
181    }
182
183    /// <summary>
184    /// Creates an instance of all types that are subtypes or the same type of the specified type
185    /// </summary>
186    /// <param name="type">Most general type.</param>
187    /// <returns>Enumerable of the created instances.</returns>
188    internal static IEnumerable<object> GetInstances(Type type) {
189      List<object> instances = new List<object>();
190      foreach (Type t in GetTypes(type, onlyInstantiable: true, includeGenericTypeDefinitions: false)) {
191        object instance = null;
192        try { instance = Activator.CreateInstance(t); }
193        catch { }
194        if (instance != null) instances.Add(instance);
195      }
196      return instances;
197    }
198
199    /// <summary>
200    /// Finds all types that are subtypes or equal to the specified type.
201    /// </summary>
202    /// <param name="type">Most general type for which to find matching types.</param>
203    /// <param name="onlyInstantiable">Return only types that are instantiable
204    /// <param name="includeGenericTypeDefinitions">Specifies if generic type definitions shall be included</param>
205    /// (interfaces, abstract classes... are not returned)</param>
206    /// <returns>Enumerable of the discovered types.</returns>
207    internal static IEnumerable<Type> GetTypes(Type type, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
208      return from asm in AppDomain.CurrentDomain.GetAssemblies()
209             from t in GetTypes(type, asm, onlyInstantiable, includeGenericTypeDefinitions)
210             select t;
211    }
212
213    internal static IEnumerable<Type> GetTypes(IEnumerable<Type> types, bool onlyInstantiable, bool includeGenericTypeDefinitions, bool assignableToAllTypes) {
214      IEnumerable<Type> result = GetTypes(types.First(), onlyInstantiable, includeGenericTypeDefinitions);
215      foreach (Type type in types.Skip(1)) {
216        IEnumerable<Type> discoveredTypes = GetTypes(type, onlyInstantiable, includeGenericTypeDefinitions);
217        if (assignableToAllTypes) result = result.Intersect(discoveredTypes);
218        else result = result.Union(discoveredTypes);
219      }
220      return result;
221    }
222
223    /// <summary>
224    /// Finds all types that are subtypes or equal to the specified type if they are part of the given
225    /// <paramref name="pluginDescription"/>.
226    /// </summary>
227    /// <param name="type">Most general type for which to find matching types.</param>
228    /// <param name="pluginDescription">The plugin the subtypes must be part of.</param>
229    /// <param name="onlyInstantiable">Return only types that are instantiable
230    /// <param name="includeGenericTypeDefinitions">Specifies if generic type definitions shall be included</param>
231    /// (interfaces, abstract classes... are not returned)</param>
232    /// <returns>Enumerable of the discovered types.</returns>
233    internal static IEnumerable<Type> GetTypes(Type type, IPluginDescription pluginDescription, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
234      PluginDescription pluginDesc = (PluginDescription)pluginDescription;
235      return from asm in AppDomain.CurrentDomain.GetAssemblies()
236             where !asm.IsDynamic && !string.IsNullOrEmpty(asm.Location)
237             where pluginDesc.AssemblyLocations.Any(location => location.Equals(Path.GetFullPath(asm.Location), StringComparison.CurrentCultureIgnoreCase))
238             from t in GetTypes(type, asm, onlyInstantiable, includeGenericTypeDefinitions)
239             select t;
240    }
241
242    internal static IEnumerable<Type> GetTypes(IEnumerable<Type> types, IPluginDescription pluginDescription, bool onlyInstantiable, bool includeGenericTypeDefinitions, bool assignableToAllTypes) {
243      IEnumerable<Type> result = GetTypes(types.First(), pluginDescription, onlyInstantiable, includeGenericTypeDefinitions);
244      foreach (Type type in types.Skip(1)) {
245        IEnumerable<Type> discoveredTypes = GetTypes(type, pluginDescription, onlyInstantiable, includeGenericTypeDefinitions);
246        if (assignableToAllTypes) result = result.Intersect(discoveredTypes);
247        else result = result.Union(discoveredTypes);
248      }
249      return result;
250    }
251
252    /// <summary>
253    /// Gets types that are assignable (same of subtype) to the specified type only from the given assembly.
254    /// </summary>
255    /// <param name="type">Most general type we want to find.</param>
256    /// <param name="assembly">Assembly that should be searched for types.</param>
257    /// <param name="onlyInstantiable">Return only types that are instantiable
258    /// (interfaces, abstract classes...  are not returned)</param>
259    /// <param name="includeGenericTypeDefinitions">Specifies if generic type definitions shall be included</param>
260    /// <returns>Enumerable of the discovered types.</returns>
261    internal static IEnumerable<Type> GetTypes(Type type, Assembly assembly, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
262      var matchingTypes = from assemblyType in assembly.GetTypes()
263                          let t = assemblyType.BuildType(type)
264                          where t != null
265                          where t.IsSubTypeOf(type)
266                          where !t.IsNonDiscoverableType()
267                          where onlyInstantiable == false || (!t.IsAbstract && !t.IsInterface && !t.HasElementType)
268                          where includeGenericTypeDefinitions || !t.IsGenericTypeDefinition
269                          select t;
270
271      return matchingTypes;
272    }
273
274    /// <summary>
275    /// Discovers all types implementing or inheriting all or any type in <paramref name="types"/> (directly and indirectly) that are declared in the assembly <paramref name="assembly"/>.
276    /// </summary>
277    /// <param name="types">The types to discover.</param>
278    /// <param name="assembly">The declaring assembly.</param>
279    /// <param name="onlyInstantiable">Return only types that are instantiable (instance, abstract... are not returned)</param>
280    /// /// <param name="assignableToAllTypes">Specifies if discovered types must implement or inherit all given <paramref name="types"/>.</param>
281    /// <returns>An enumerable of discovered types.</returns>
282    internal static IEnumerable<Type> GetTypes(IEnumerable<Type> types, Assembly assembly, bool onlyInstantiable = true, bool includeGenericTypeDefinitions = false, bool assignableToAllTypes = true) {
283      IEnumerable<Type> result = GetTypes(types.First(), assembly, onlyInstantiable, includeGenericTypeDefinitions);
284      foreach (Type type in types.Skip(1)) {
285        IEnumerable<Type> discoveredTypes = GetTypes(type, assembly, onlyInstantiable, includeGenericTypeDefinitions);
286        if (assignableToAllTypes) result = result.Intersect(discoveredTypes);
287        else result = result.Union(discoveredTypes);
288      }
289      return result;
290    }
291
292    private void OnPluginLoaded(PluginInfrastructureEventArgs e) {
293      if (PluginLoaded != null) PluginLoaded(this, e);
294    }
295
296    private void OnPluginUnloaded(PluginInfrastructureEventArgs e) {
297      if (PluginUnloaded != null) PluginUnloaded(this, e);
298    }
299
300    #region IApplicationManager Members
301
302    IEnumerable<T> IApplicationManager.GetInstances<T>() {
303      return GetInstances<T>();
304    }
305
306    IEnumerable<object> IApplicationManager.GetInstances(Type type) {
307      return GetInstances(type);
308    }
309
310    IEnumerable<Type> IApplicationManager.GetTypes(Type type, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
311      return GetTypes(type, onlyInstantiable, includeGenericTypeDefinitions);
312    }
313    IEnumerable<Type> IApplicationManager.GetTypes(IEnumerable<Type> types, bool onlyInstantiable, bool includeGenericTypeDefinitions, bool assignableToAllTypes) {
314      return GetTypes(types, onlyInstantiable, includeGenericTypeDefinitions, assignableToAllTypes);
315    }
316
317    IEnumerable<Type> IApplicationManager.GetTypes(Type type, IPluginDescription plugin, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
318      return GetTypes(type, plugin, onlyInstantiable, includeGenericTypeDefinitions);
319    }
320    IEnumerable<Type> IApplicationManager.GetTypes(IEnumerable<Type> types, IPluginDescription plugin, bool onlyInstantiable, bool includeGenericTypeDefinitions, bool assignableToAllTypes) {
321      return GetTypes(types, plugin, onlyInstantiable, includeGenericTypeDefinitions, assignableToAllTypes);
322    }
323
324    IEnumerable<Type> IApplicationManager.GetTypes(Type type, Assembly assembly, bool onlyInstantiable, bool includeGenericTypeDefinitions) {
325      return GetTypes(type, assembly, onlyInstantiable, includeGenericTypeDefinitions);
326    }
327    IEnumerable<Type> IApplicationManager.GetTypes(IEnumerable<Type> types, Assembly assembly, bool onlyInstantiable, bool includeGenericTypeDefinitions, bool assignableToAllTypes) {
328      return GetTypes(types, assembly, onlyInstantiable, includeGenericTypeDefinitions, assignableToAllTypes);
329    }
330
331    /// <summary>
332    /// Finds the plugin that declares the <paramref name="type">type</paramref>.
333    /// </summary>
334    /// <param name="type">The type of interest.</param>
335    /// <returns>The description of the plugin that declares the given type or null if the type has not been declared by a known plugin.</returns>
336    public IPluginDescription GetDeclaringPlugin(Type type) {
337      if (type == null) throw new ArgumentNullException("type");
338      foreach (PluginDescription info in Plugins) {
339        if (info.AssemblyLocations.Contains(Path.GetFullPath(type.Assembly.Location))) return info;
340      }
341      return null;
342    }
343    #endregion
344  }
345}
Note: See TracBrowser for help on using the repository browser.