Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/ApplicationManager.cs @ 2783

Last change on this file since 2783 was 2748, checked in by gkronber, 15 years ago

Fixed issues in plugin infrastructure as identified by abeham. #863 (Code review)

File size: 13.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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.Security.Policy;
25using System.Reflection;
26using System.Diagnostics;
27using System.Security.Permissions;
28using System.Security;
29using System.Linq;
30using HeuristicLab.PluginInfrastructure.Manager;
31using System.IO;
32
33namespace HeuristicLab.PluginInfrastructure {
34
35  /// <summary>
36  /// The ApplicationManager has a reference to the application manager singleton.
37  /// The application manager provides
38  /// </summary>
39  public sealed class ApplicationManager : MarshalByRefObject, IApplicationManager {
40    private static IApplicationManager appManager;
41    /// <summary>
42    /// Gets the application manager singleton.
43    /// </summary>
44    public static IApplicationManager Manager {
45      get { return appManager; }
46    }
47
48    /// <summary>
49    /// Fired when a plugin is loaded.
50    /// </summary>
51    internal event EventHandler<PluginInfrastructureEventArgs> PluginLoaded;
52    /// <summary>
53    /// Fired when a plugin is unloaded (when the application terminates).
54    /// </summary>
55    internal event EventHandler<PluginInfrastructureEventArgs> PluginUnloaded;
56
57    // cache for the AssemblyResolveEvent
58    // which must be handled when assemblies are loaded dynamically after the application start
59    private Dictionary<string, Assembly> loadedAssemblies;
60
61    private List<IPlugin> loadedPlugins;
62
63    private List<PluginDescription> plugins;
64    /// <summary>
65    /// Gets all plugins.
66    /// </summary>
67    public IEnumerable<IPluginDescription> Plugins {
68      get { return plugins.Cast<IPluginDescription>(); }
69    }
70
71    private List<ApplicationDescription> applications;
72    /// <summary>
73    /// Gets all installed applications.
74    /// </summary>
75    public IEnumerable<IApplicationDescription> Applications {
76      get { return applications.Cast<IApplicationDescription>(); }
77    }
78
79    internal ApplicationManager()
80      : base() {
81      loadedAssemblies = new Dictionary<string, Assembly>();
82      loadedPlugins = new List<IPlugin>();
83      AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
84        if (loadedAssemblies.ContainsKey(args.Name)) {
85          return loadedAssemblies[args.Name];
86        }
87        return null;
88      };
89    }
90
91    /// <summary>
92    /// Prepares the application domain for the execution of an HL application.
93    /// Pre-loads all <paramref name="plugins"/>.
94    /// </summary>
95    /// <param name="apps">Enumerable of available HL applications.</param>
96    /// <param name="plugins">Enumerable of plugins that should be pre-loaded.</param>
97    internal void PrepareApplicationDomain(IEnumerable<ApplicationDescription> apps, IEnumerable<PluginDescription> plugins) {
98      this.plugins = new List<PluginDescription>(plugins);
99      this.applications = new List<ApplicationDescription>(apps);
100      RegisterApplicationManager((IApplicationManager)this);
101      LoadPlugins(plugins);
102    }
103
104    /// <summary>
105    /// Registers a new application manager.
106    /// </summary>
107    /// <param name="manager"></param>
108    internal static void RegisterApplicationManager(IApplicationManager manager) {
109      if (appManager != null) throw new InvalidOperationException("The application manager has already been set.");
110      appManager = manager;
111    }
112
113    /// <summary>
114    /// Loads the <paramref name="plugins"/> into this application domain.
115    /// </summary>
116    /// <param name="plugins">Enumerable of plugins that should be loaded.</param>
117    private void LoadPlugins(IEnumerable<PluginDescription> plugins) {
118      // load all loadable plugins (all dependencies available) into the execution context
119      foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
120        foreach (string fileName in desc.AssemblyLocations) {
121          var asm = Assembly.LoadFrom(fileName);
122          RegisterLoadedAssembly(asm);
123          // instantiate and load all plugins in this assembly
124          foreach (var plugin in GetInstances<IPlugin>(asm)) {
125            plugin.OnLoad();
126            loadedPlugins.Add(plugin);
127          }
128        }
129        OnPluginLoaded(new PluginInfrastructureEventArgs("Plugin loaded", desc));
130        desc.Load();
131      }
132    }
133
134    /// <summary>
135    /// Runs the application declared in <paramref name="appInfo"/>.
136    /// This is a synchronous call. When the application is terminated all plugins are unloaded.
137    /// </summary>
138    /// <param name="appInfo">Description of the application to run</param>
139    internal void Run(ApplicationDescription appInfo) {
140      IApplication runnablePlugin = (IApplication)Activator.CreateInstance(appInfo.DeclaringAssemblyName, appInfo.DeclaringTypeName).Unwrap();
141      try {
142        runnablePlugin.Run();
143      }
144      finally {
145        // unload plugins in reverse order
146        foreach (var plugin in loadedPlugins.Reverse<IPlugin>()) {
147          plugin.OnUnload();
148        }
149        foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) {
150          desc.Unload();
151          OnPluginUnloaded(new PluginInfrastructureEventArgs("Plugin unloaded", desc));
152        }
153      }
154    }
155
156    /// <summary>
157    /// Loads assemblies dynamically from a byte array
158    /// </summary>
159    /// <param name="plugins">bytearray of all assemblies that should be loaded</param>
160    internal void LoadAssemblies(IEnumerable<byte[]> assemblies) {
161      foreach (byte[] asm in assemblies) {
162        Assembly loadedAsm = Assembly.Load(asm);
163        RegisterLoadedAssembly(loadedAsm);
164      }
165    }
166
167    // register assembly in the assembly cache for the AssemblyResolveEvent
168    private void RegisterLoadedAssembly(Assembly asm) {
169      loadedAssemblies.Add(asm.FullName, asm);
170      loadedAssemblies.Add(asm.GetName().Name, asm); // add short name
171    }
172
173    /// <summary>
174    /// Creates an instance of all types that are subtypes or the same type of the specified type and declared in <paramref name="plugin"/>
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>(IPluginDescription plugin) where T : class {
179      return from t in GetTypes(typeof(T), plugin, true)
180             select (T)Activator.CreateInstance(t);
181    }
182    /// <summary>
183    /// Creates an instance of all types declared in assembly <param name="asm"/> that are subtypes or the same type of the specified type and declared in <paramref name="plugin"/>
184    /// </summary>
185    /// <typeparam name="T">Most general type.</typeparam>
186    /// <param name="asm">Declaring assembly.</param>
187    /// <returns>Enumerable of the created instances.</returns>
188    private static IEnumerable<T> GetInstances<T>(Assembly asm) where T : class {
189      return from t in GetTypes(typeof(T), asm, true)
190             select (T)Activator.CreateInstance(t);
191    }
192    /// <summary>
193    /// Creates an instance of all types that are subtypes or the same type of the specified type
194    /// </summary>
195    /// <typeparam name="T">Most general type.</typeparam>
196    /// <returns>Enumerable of the created instances.</returns>
197    internal static IEnumerable<T> GetInstances<T>() where T : class {
198      return from i in GetInstances(typeof(T))
199             select (T)i;
200    }
201
202    /// <summary>
203    /// Creates an instance of all types that are subtypes or the same type of the specified type
204    /// </summary>
205    /// <typeparam name="type">Most general type.</typeparam>
206    /// <returns>Enumerable of the created instances.</returns>
207    internal static IEnumerable<object> GetInstances(Type type) {
208      return (from t in GetTypes(type, true)
209              select Activator.CreateInstance(t)).ToList();
210    }
211
212    /// <summary>
213    /// Finds all types that are subtypes or equal to the specified type.
214    /// </summary>
215    /// <param name="type">Most general type for which to find matching types.</param>
216    /// <param name="onlyInstantiable">Return only types that are instantiable
217    /// (interfaces, abstract classes... are not returned)</param>
218    /// <returns>Enumerable of the discovered types.</returns>
219    internal static IEnumerable<Type> GetTypes(Type type, bool onlyInstantiable) {
220      return from asm in AppDomain.CurrentDomain.GetAssemblies()
221             from t in GetTypes(type, asm, onlyInstantiable)
222             select t;
223    }
224
225    /// <summary>
226    /// Finds all types that are subtypes or equal to the specified type if they are part of the given
227    /// <paramref name="plugin"/>.
228    /// </summary>
229    /// <param name="type">Most general type for which to find matching types.</param>
230    /// <param name="plugin">The plugin the subtypes must be part of.</param>
231    /// <param name="onlyInstantiable">Return only types that are instantiable
232    /// (interfaces, abstract classes... are not returned)</param>
233    /// <returns>Enumerable of the discovered types.</returns>
234    internal static IEnumerable<Type> GetTypes(Type type, IPluginDescription pluginDescription, bool onlyInstantiable) {
235      PluginDescription pluginDesc = (PluginDescription)pluginDescription;
236      return from asm in AppDomain.CurrentDomain.GetAssemblies()
237             where !string.IsNullOrEmpty(asm.Location) // ignore dynamically generated assemblies
238             where pluginDesc.AssemblyLocations.Any(location => location.Equals(Path.GetFullPath(asm.Location), StringComparison.CurrentCultureIgnoreCase))
239             from t in GetTypes(type, asm, onlyInstantiable)
240             select t;
241    }
242
243    /// <summary>
244    /// Gets types that are assignable (same of subtype) to the specified type only from the given assembly.
245    /// </summary>
246    /// <param name="type">Most general type we want to find.</param>
247    /// <param name="assembly">Assembly that should be searched for types.</param>
248    /// <param name="onlyInstantiable">Return only types that are instantiable
249    /// (interfaces, abstract classes...  are not returned)</param>
250    /// <returns>Enumerable of the discovered types.</returns>
251    private static IEnumerable<Type> GetTypes(Type type, Assembly assembly, bool onlyInstantiable) {
252      return from t in assembly.GetTypes()
253             where type.IsAssignableFrom(t)
254             where onlyInstantiable == false || (!t.IsAbstract && !t.IsInterface && !t.HasElementType)
255             select t;
256    }
257
258    private void OnPluginLoaded(PluginInfrastructureEventArgs e) {
259      if (PluginLoaded != null) PluginLoaded(this, e);
260    }
261
262    private void OnPluginUnloaded(PluginInfrastructureEventArgs e) {
263      if (PluginUnloaded != null) PluginUnloaded(this, e);
264    }
265
266    // infinite lease time
267    /// <summary>
268    /// Initializes the life time service with infinite lease time.
269    /// </summary>
270    /// <returns><c>null</c>.</returns>
271    public override object InitializeLifetimeService() {
272      return null;
273    }
274
275    #region IApplicationManager Members
276
277    IEnumerable<T> IApplicationManager.GetInstances<T>() {
278      return GetInstances<T>();
279    }
280
281    IEnumerable<object> IApplicationManager.GetInstances(Type type) {
282      return GetInstances(type);
283    }
284
285    IEnumerable<Type> IApplicationManager.GetTypes(Type type) {
286      return GetTypes(type, true);
287    }
288
289    IEnumerable<Type> IApplicationManager.GetTypes(Type type, bool onlyInstantiable) {
290      return GetTypes(type, onlyInstantiable);
291    }
292
293    IEnumerable<Type> IApplicationManager.GetTypes(Type type, IPluginDescription plugin) {
294      return GetTypes(type, plugin, true);
295    }
296
297    IEnumerable<Type> IApplicationManager.GetTypes(Type type, IPluginDescription plugin, bool onlyInstantiable) {
298      return GetTypes(type, plugin, onlyInstantiable);
299    }
300
301
302    /// <summary>
303    /// Finds the plugin that declares the <paramref name="type">type</paramref>.
304    /// </summary>
305    /// <param name="type">The type of interest.</param>
306    /// <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>
307    public IPluginDescription GetDeclaringPlugin(Type type) {
308      foreach (PluginDescription info in Plugins) {
309        if (info.AssemblyLocations.Contains(Path.GetFullPath(type.Assembly.Location))) return info;
310      }
311      return null;
312    }
313    #endregion
314  }
315}
Note: See TracBrowser for help on using the repository browser.