source: trunk/HeuristicLab.PluginInfrastructure/3.3/SandboxApplicationManager.cs @ 17782

Last change on this file since 17782 was 17782, checked in by gkronber, 20 months ago

#3088: ignore dynamic assemblies and assemblies with empty location in GetTypes(Type type, bool onlyInstantiable, bool includeGenericTypeDefinitions) method.

Reasoning: when hive job manager is loaded at least one dynamic assembly is loaded for WCF call wrappers. This causes an exception in PluginInfrastructure when trying to load the types in the dynamic assembly.

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