Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/PluginManager.cs @ 314

Last change on this file since 314 was 241, checked in by gkronber, 16 years ago

Stability improvement.
also makes debugging easier (related to #149). Should be merged into the trunk after testing.

  • added exception handling in PluginManager to graciously unload AppDomains when an uncaught exception is occurred
  • added exception handling in the starter-form to display an error message-box
File size: 6.7 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;
24
25namespace HeuristicLab.PluginInfrastructure {
26
27  // must extend MarshalByRefObject because of event passing between Loader and PluginManager (each in it's own AppDomain)
28  public class PluginManager : MarshalByRefObject {
29
30    // singleton: only one manager allowed in each AppDomain
31    private static PluginManager manager = new PluginManager();
32    public static PluginManager Manager {
33      get { return manager; }
34    }
35
36    // singleton: only one control manager allowed in each applicatoin (i.e. AppDomain)
37    private static IControlManager controlManager;
38    public static IControlManager ControlManager {
39      get { return controlManager; }
40      set { controlManager = value; }
41    }
42
43    public event PluginManagerActionEventHandler Action;
44
45    // holds a proxy for the loader in the special AppDomain for PluginManagament
46    private Loader remoteLoader;
47    private AppDomain pluginDomain;
48    private string pluginDir;
49
50    // singleton pattern
51    private PluginManager() {
52      this.pluginDir = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.PluginDir;
53    }
54
55    public ICollection<PluginInfo> InstalledPlugins {
56      get { return remoteLoader.InstalledPlugins; }
57    }
58
59    public ICollection<PluginInfo> DisabledPlugins {
60      get { return remoteLoader.DisabledPlugins; }
61    }
62
63    public ICollection<PluginInfo> ActivePlugins {
64      get { return remoteLoader.ActivePlugins; }
65    }
66
67    public ICollection<ApplicationInfo> InstalledApplications {
68      get { return remoteLoader.InstalledApplications; }
69    }
70
71    private ICollection<PluginInfo> loadedPlugins;
72    public ICollection<PluginInfo> LoadedPlugins {
73      get { return loadedPlugins; }
74      internal set { loadedPlugins = value; }
75    }
76
77    /// <summary>
78    /// Creates a dedicated AppDomain for loading all plugins and checking dependencies.
79    /// </summary>
80    public void Initialize() {
81      NotifyListeners(PluginManagerAction.Initializing, "-");
82      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
83      setup.PrivateBinPath = pluginDir;
84      pluginDomain = AppDomain.CreateDomain("plugin domain", null, setup);
85      remoteLoader = (Loader)pluginDomain.CreateInstanceAndUnwrap("HeuristicLab.PluginInfraStructure", "HeuristicLab.PluginInfrastructure.Loader");
86      remoteLoader.PluginAction += delegate(object sender, PluginManagerActionEventArgs args) { if(Action != null) Action(this, args); };
87      remoteLoader.Init();
88      NotifyListeners(PluginManagerAction.Initialized, "-");
89    }
90
91    /// <summary>
92    /// Creates a separate AppDomain.
93    /// Loads all active plugin assemblies and starts the application in the new AppDomain via a PluginRunner instance activated in the new AppDomain
94    /// </summary>
95    /// <param name="appInfo">application to run</param>
96    public void Run(ApplicationInfo appInfo) {
97      // create a separate AppDomain for the application
98      // activate a PluginRunner instance in the application
99      // and remotely tell it to start the application
100
101      NotifyListeners(PluginManagerAction.Starting, appInfo.Name);
102      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
103      setup.PrivateBinPath = pluginDir;
104      AppDomain applicationDomain = AppDomain.CreateDomain(appInfo.Name + " AppDomain", null, setup);
105      try {
106        Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap("HeuristicLab.PluginInfrastructure", "HeuristicLab.PluginInfrastructure.Runner");
107        NotifyListeners(PluginManagerAction.Initializing, "All plugins");
108        remoteRunner.LoadPlugins(remoteLoader.ActivePlugins);
109        NotifyListeners(PluginManagerAction.Initialized, "All plugins");
110        remoteRunner.Run(appInfo);
111      } catch(Exception ex) {
112        // can't handle exception here -> rethrow
113        throw new ApplicationException("Exception in "+appInfo.Name, ex);
114      } finally {
115        // make sure domain is unloaded in all cases
116        AppDomain.Unload(applicationDomain);
117      }
118    }
119
120    /// <summary>
121    /// Calculates a set of plugins that directly or transitively depend on the plugin given in the argument.
122    /// </summary>
123    /// <param name="pluginInfo"></param>
124    /// <returns>a list of plugins that are directly of transitively dependent.</returns>
125    public List<PluginInfo> GetDependentPlugins(PluginInfo pluginInfo) {
126      List<PluginInfo> mergedList = new List<PluginInfo>();
127      foreach(PluginInfo plugin in InstalledPlugins) {
128        if(plugin.Dependencies.Contains(pluginInfo)) {
129          if(!mergedList.Contains(plugin)) {
130            mergedList.Add(plugin);
131          }
132          // for each of the dependent plugins add the list of transitively dependent plugins
133          // make sure that only one entry for each plugin is added to the merged list
134          GetDependentPlugins(plugin).ForEach(delegate(PluginInfo dependentPlugin) {
135            if(!mergedList.Contains(dependentPlugin)) {
136              mergedList.Add(dependentPlugin);
137            }
138          });
139        }
140      }
141      return mergedList;
142    }
143
144    public void UnloadAllPlugins() {
145      AppDomain.Unload(pluginDomain);
146    }
147
148    public void LoadAllPlugins() {
149      Initialize();
150    }
151
152    public void OnDelete(PluginInfo pluginInfo) {
153      remoteLoader.OnDelete(pluginInfo);
154    }
155
156    public void OnInstall(PluginInfo pluginInfo) {
157      remoteLoader.OnInstall(pluginInfo);
158    }
159
160    public void OnPreUpdate(PluginInfo pluginInfo) {
161      remoteLoader.OnPreUpdate(pluginInfo);
162    }
163
164    public void OnPostUpdate(PluginInfo pluginInfo) {
165      remoteLoader.OnPostUpdate(pluginInfo);
166    }
167
168    private void NotifyListeners(PluginManagerAction action, string text) {
169      if(Action != null) {
170        Action(this, new PluginManagerActionEventArgs(text, action));
171      }
172    }
173  }
174}
Note: See TracBrowser for help on using the repository browser.