Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/3.3/DefaultApplicationManager.cs @ 5352

Last change on this file since 5352 was 5131, checked in by swagner, 14 years ago

Fixed exceptions in plugin infrastructure when trying to instantiate types which do not provide a parameterless constructor (#1309).

File size: 14.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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 DefaultApplicationManager 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 DefaultApplicationManager is registered as ApplicationManager.Manager singleton for each HL application
35  /// started via the plugin infrastructure.
36  /// </summary>
37  internal sealed class DefaultApplicationManager : 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    private 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 DefaultApplicationManager()
70      : base() {
71      loadedAssemblies = new Dictionary<string, Assembly>();
72      loadedPlugins = new List<IPlugin>();
73      AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
74        if (loadedAssemblies.ContainsKey(args.Name)) {
75          return loadedAssemblies[args.Name];
76        }
77        return null;
78      };
79    }
80
81    /// <summary>
82    /// Prepares the application domain for the execution of an HL application.
83    /// Pre-loads all <paramref name="plugins"/>.
84    /// </summary>
85    /// <param name="apps">Enumerable of available HL applications.</param>
86    /// <param name="plugins">Enumerable of plugins that should be pre-loaded.</param>
87    internal void PrepareApplicationDomain(IEnumerable<ApplicationDescription> apps, IEnumerable<PluginDescription> plugins) {
88      this.plugins = new List<PluginDescription>(plugins);
89      this.applications = new List<ApplicationDescription>(apps);
90      ApplicationManager.RegisterApplicationManager(this);
91      LoadPlugins(plugins);
92    }
93
94    /// <summary>
95    /// Loads the <paramref name="plugins"/> into this application domain.
96    /// </summary>
97    /// <param name="plugins">Enumerable of plugins that should be loaded.</param>
98    private void LoadPlugins(IEnumerable<PluginDescription> plugins) {
99      // load all loadable plugins (all dependencies available) into the execution context
100      foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
101        foreach (string fileName in desc.AssemblyLocations) {
102          var asm = Assembly.LoadFrom(fileName);
103          RegisterLoadedAssembly(asm);
104          // instantiate and load all plugins in this assembly
105          foreach (var plugin in GetInstances<IPlugin>(asm)) {
106            plugin.OnLoad();
107            loadedPlugins.Add(plugin);
108          }
109        }
110        OnPluginLoaded(new PluginInfrastructureEventArgs(desc));
111        desc.Load();
112      }
113    }
114
115    /// <summary>
116    /// Runs the application declared in <paramref name="appInfo"/>.
117    /// This is a synchronous call. When the application is terminated all plugins are unloaded.
118    /// </summary>
119    /// <param name="appInfo">Description of the application to run</param>
120    internal void Run(ApplicationDescription appInfo) {
121      IApplication runnablePlugin = (IApplication)Activator.CreateInstance(appInfo.DeclaringAssemblyName, appInfo.DeclaringTypeName).Unwrap();
122      try {
123        runnablePlugin.Run();
124      }
125      finally {
126        // unload plugins in reverse order
127        foreach (var plugin in loadedPlugins.Reverse<IPlugin>()) {
128          plugin.OnUnload();
129        }
130        foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
131          desc.Unload();
132          OnPluginUnloaded(new PluginInfrastructureEventArgs(desc));
133        }
134      }
135    }
136
137    /// <summary>
138    /// Loads raw assemblies dynamically from a byte array
139    /// </summary>
140    /// <param name="assemblies">bytearray of all raw assemblies that should be loaded</param>
141    internal void LoadAssemblies(IEnumerable<byte[]> assemblies) {
142      foreach (byte[] asm in assemblies) {
143        Assembly loadedAsm = Assembly.Load(asm);
144        RegisterLoadedAssembly(loadedAsm);
145      }
146    }
147
148    // register assembly in the assembly cache for the AssemblyResolveEvent
149    private void RegisterLoadedAssembly(Assembly asm) {
150      loadedAssemblies.Add(asm.FullName, asm);
151      loadedAssemblies.Add(asm.GetName().Name, asm); // add short name
152    }
153
154    /// <summary>
155    /// Creates an instance of all types that are subtypes or the same type of the specified type and declared in <paramref name="plugin"/>
156    /// </summary>
157    /// <typeparam name="T">Most general type.</typeparam>
158    /// <returns>Enumerable of the created instances.</returns>
159    internal static IEnumerable<T> GetInstances<T>(IPluginDescription plugin) where T : class {
160      List<T> instances = new List<T>();
161      foreach (Type t in GetTypes(typeof(T), plugin, true)) {
162        T instance = null;
163        try { instance = (T)Activator.CreateInstance(t); }
164        catch { }
165        if (instance != null) instances.Add(instance);
166      }
167      return instances;
168    }
169    /// <summary>
170    /// 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"/>.
171    /// </summary>
172    /// <typeparam name="T">Most general type.</typeparam>
173    /// <param name="asm">Declaring assembly.</param>
174    /// <returns>Enumerable of the created instances.</returns>
175    private static IEnumerable<T> GetInstances<T>(Assembly asm) where T : class {
176      List<T> instances = new List<T>();
177      foreach (Type t in GetTypes(typeof(T), asm, true)) {
178        T instance = null;
179        try { instance = (T)Activator.CreateInstance(t); }
180        catch { }
181        if (instance != null) instances.Add(instance);
182      }
183      return instances;
184    }
185    /// <summary>
186    /// Creates an instance of all types that are subtypes or the same type of the specified type
187    /// </summary>
188    /// <typeparam name="T">Most general type.</typeparam>
189    /// <returns>Enumerable of the created instances.</returns>
190    internal static IEnumerable<T> GetInstances<T>() where T : class {
191      return from i in GetInstances(typeof(T))
192             select (T)i;
193    }
194
195    /// <summary>
196    /// Creates an instance of all types that are subtypes or the same type of the specified type
197    /// </summary>
198    /// <param name="type">Most general type.</param>
199    /// <returns>Enumerable of the created instances.</returns>
200    internal static IEnumerable<object> GetInstances(Type type) {
201      List<object> instances = new List<object>();
202      foreach (Type t in GetTypes(type, true)) {
203        object instance = null;
204        try { instance = Activator.CreateInstance(t); }
205        catch { }
206        if (instance != null) instances.Add(instance);
207      }
208      return instances;
209    }
210
211    /// <summary>
212    /// Finds all types that are subtypes or equal to the specified type.
213    /// </summary>
214    /// <param name="type">Most general type for which to find matching types.</param>
215    /// <param name="onlyInstantiable">Return only types that are instantiable
216    /// (interfaces, abstract classes... are not returned)</param>
217    /// <returns>Enumerable of the discovered types.</returns>
218    internal static IEnumerable<Type> GetTypes(Type type, bool onlyInstantiable) {
219      return from asm in AppDomain.CurrentDomain.GetAssemblies()
220             from t in GetTypes(type, asm, onlyInstantiable)
221             select t;
222    }
223
224    /// <summary>
225    /// Finds all types that are subtypes or equal to the specified type if they are part of the given
226    /// <paramref name="pluginDescription"/>.
227    /// </summary>
228    /// <param name="type">Most general type for which to find matching types.</param>
229    /// <param name="pluginDescription">The plugin the subtypes must be part of.</param>
230    /// <param name="onlyInstantiable">Return only types that are instantiable
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) {
234      PluginDescription pluginDesc = (PluginDescription)pluginDescription;
235      return from asm in AppDomain.CurrentDomain.GetAssemblies()
236             where !IsDynamicAssembly(asm)
237             where pluginDesc.AssemblyLocations.Any(location => location.Equals(Path.GetFullPath(asm.Location), StringComparison.CurrentCultureIgnoreCase))
238             from t in GetTypes(type, asm, onlyInstantiable)
239             select t;
240    }
241
242    private static bool IsDynamicAssembly(Assembly asm) {
243      return (asm is System.Reflection.Emit.AssemblyBuilder) || string.IsNullOrEmpty(asm.Location);
244    }
245
246    /// <summary>
247    /// Gets types that are assignable (same of subtype) to the specified type only from the given assembly.
248    /// </summary>
249    /// <param name="type">Most general type we want to find.</param>
250    /// <param name="assembly">Assembly that should be searched for types.</param>
251    /// <param name="onlyInstantiable">Return only types that are instantiable
252    /// (interfaces, abstract classes...  are not returned)</param>
253    /// <returns>Enumerable of the discovered types.</returns>
254    private static IEnumerable<Type> GetTypes(Type type, Assembly assembly, bool onlyInstantiable) {
255      return from t in assembly.GetTypes()
256             where CheckTypeCompatibility(type, t)
257             where onlyInstantiable == false || (!t.IsAbstract && !t.IsInterface && !t.HasElementType)
258             select BuildType(t, type);
259    }
260
261    private static bool CheckTypeCompatibility(Type type, Type other) {
262      if (type.IsAssignableFrom(other))
263        return true;
264      if (type.IsGenericType && other.IsGenericType) {
265        try {
266          if (type.IsAssignableFrom(other.GetGenericTypeDefinition().MakeGenericType(type.GetGenericArguments())))
267            return true;
268        }
269        catch (Exception) { }
270      }
271      return false;
272    }
273    private static Type BuildType(Type type, Type protoType) {
274      if (type.IsGenericType && protoType.IsGenericType)
275        return type.GetGenericTypeDefinition().MakeGenericType(protoType.GetGenericArguments());
276      else
277        return type;
278    }
279
280    private void OnPluginLoaded(PluginInfrastructureEventArgs e) {
281      if (PluginLoaded != null) PluginLoaded(this, e);
282    }
283
284    private void OnPluginUnloaded(PluginInfrastructureEventArgs e) {
285      if (PluginUnloaded != null) PluginUnloaded(this, e);
286    }
287
288    // infinite lease time
289    /// <summary>
290    /// Initializes the life time service with infinite lease time.
291    /// </summary>
292    /// <returns><c>null</c>.</returns>
293    public override object InitializeLifetimeService() {
294      return null;
295    }
296
297    #region IApplicationManager Members
298
299    IEnumerable<T> IApplicationManager.GetInstances<T>() {
300      return GetInstances<T>();
301    }
302
303    IEnumerable<object> IApplicationManager.GetInstances(Type type) {
304      return GetInstances(type);
305    }
306
307    IEnumerable<Type> IApplicationManager.GetTypes(Type type) {
308      return GetTypes(type, true);
309    }
310
311    IEnumerable<Type> IApplicationManager.GetTypes(Type type, bool onlyInstantiable) {
312      return GetTypes(type, onlyInstantiable);
313    }
314
315    IEnumerable<Type> IApplicationManager.GetTypes(Type type, IPluginDescription plugin) {
316      return GetTypes(type, plugin, true);
317    }
318
319    IEnumerable<Type> IApplicationManager.GetTypes(Type type, IPluginDescription plugin, bool onlyInstantiable) {
320      return GetTypes(type, plugin, onlyInstantiable);
321    }
322
323
324    /// <summary>
325    /// Finds the plugin that declares the <paramref name="type">type</paramref>.
326    /// </summary>
327    /// <param name="type">The type of interest.</param>
328    /// <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>
329    public IPluginDescription GetDeclaringPlugin(Type type) {
330      if (type == null) throw new ArgumentNullException("type");
331      foreach (PluginDescription info in Plugins) {
332        if (info.AssemblyLocations.Contains(Path.GetFullPath(type.Assembly.Location))) return info;
333      }
334      return null;
335    }
336    #endregion
337  }
338}
Note: See TracBrowser for help on using the repository browser.