Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1548 was 1501, checked in by kgrading, 16 years ago

just noticed i've ruined gkronbergers GetDependendPlugins method. Restored the old method and renamed mine (#570)

File size: 13.0 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;
29
30namespace HeuristicLab.PluginInfrastructure {
31
32  // must extend MarshalByRefObject because of event passing between Loader and PluginManager (each in it's own AppDomain)
33  /// <summary>
34  /// Class to manage different plugins.
35  /// </summary>
36  public class PluginManager : MarshalByRefObject {
37
38    // singleton: only one manager allowed in each AppDomain
39    private static PluginManager manager = new PluginManager();
40    /// <summary>
41    /// Gets the plugin manager (is a singleton).
42    /// </summary>
43    public static PluginManager Manager {
44      get { return manager; }
45    }
46
47    // singleton: only one control manager allowed in each applicatoin (i.e. AppDomain)
48    private static IControlManager controlManager;
49    /// <summary>
50    /// Gets or sets the control manager (is a singleton).
51    /// </summary>
52    public static IControlManager ControlManager {
53      get { return controlManager; }
54      set { controlManager = value; }
55    }
56
57    /// <summary>
58    /// Event handler for actions in the plugin manager.
59    /// </summary>
60    public event PluginManagerActionEventHandler Action;
61
62    // holds a proxy for the loader in the special AppDomain for PluginManagament
63    private Loader remoteLoader;
64    private AppDomain pluginDomain;
65    private string pluginDir;
66
67    // singleton pattern
68    private PluginManager() {
69      this.pluginDir = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.PluginDir;
70    }
71
72    /// <summary>
73    /// Gets all installed plugins.
74    /// </summary>
75    /// <remarks>This information is provided by a <see cref="Loader"/>.</remarks>
76    public ICollection<PluginInfo> InstalledPlugins {
77      get { return remoteLoader.InstalledPlugins; }
78    }
79
80    /// <summary>
81    /// Gets all disabled plugins.
82    /// </summary>
83    /// <remarks>This information is provided by a <see cref="Loader"/>.</remarks>
84    public ICollection<PluginInfo> DisabledPlugins {
85      get { return remoteLoader.DisabledPlugins; }
86    }
87
88    /// <summary>
89    /// Gets all active plugins.
90    /// </summary>
91    /// <remarks>This information is provided by a <see cref="Loader"/>.</remarks>
92    public ICollection<PluginInfo> ActivePlugins {
93      get { return remoteLoader.ActivePlugins; }
94    }
95
96    /// <summary>
97    /// Gets all installed applications.
98    /// </summary>
99    /// <remarks>This information is provided by a <see cref="Loader"/>.</remarks>
100    public ICollection<ApplicationInfo> InstalledApplications {
101      get { return remoteLoader.InstalledApplications; }
102    }
103
104    private ICollection<PluginInfo> loadedPlugins;
105    /// <summary>
106    /// Gets or (internally) sets the loaded plugins.
107    /// </summary>
108    public ICollection<PluginInfo> LoadedPlugins {
109      get { return loadedPlugins; }
110      internal set { loadedPlugins = value; }
111    }
112
113    /// <summary>
114    /// Creates a dedicated AppDomain for loading all plugins and checking dependencies.
115    /// </summary>
116    public void Initialize() {
117      NotifyListeners(PluginManagerAction.Initializing, "-");
118      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
119      setup.PrivateBinPath = pluginDir;
120      pluginDomain = AppDomain.CreateDomain("plugin domain", null, setup);
121      remoteLoader = (Loader)pluginDomain.CreateInstanceAndUnwrap("HeuristicLab.PluginInfraStructure", "HeuristicLab.PluginInfrastructure.Loader");
122      remoteLoader.PluginAction += delegate(object sender, PluginManagerActionEventArgs args) { if (Action != null) Action(this, args); };
123      remoteLoader.Init();
124      NotifyListeners(PluginManagerAction.Initialized, "-");
125    }
126
127    /// <summary>
128    /// Creates a separate AppDomain.
129    /// Loads all active plugin assemblies and starts the application in the new AppDomain via a PluginRunner instance activated in the new AppDomain
130    /// </summary>
131    /// <param name="appInfo">application to run</param>
132    public void Run(ApplicationInfo appInfo) {
133      // create a separate AppDomain for the application
134      // activate a PluginRunner instance in the application
135      // and remotely tell it to start the application
136
137      NotifyListeners(PluginManagerAction.Starting, appInfo.Name);
138      AppDomain applicationDomain = null;
139      try {
140        applicationDomain = CreateAndInitAppDomain(appInfo.Name);
141        Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
142        remoteRunner.Run(appInfo);
143      }
144      finally {
145        // make sure domain is unloaded in all cases
146        if (applicationDomain != null) AppDomain.Unload(applicationDomain);
147      }
148    }
149
150    /// <summary>
151    /// Creates a new AppDomain with all plugins preloaded.
152    /// </summary>
153    /// <param name="friendlyName">Name of the new AppDomain</param>
154    /// <returns>the new AppDomain with all plugins preloaded.</returns>
155    public AppDomain CreateAndInitAppDomain(string friendlyName) {
156      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
157      setup.PrivateBinPath = pluginDir;
158      AppDomain applicationDomain = AppDomain.CreateDomain(friendlyName, null, setup);
159      Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
160      NotifyListeners(PluginManagerAction.Initializing, "All plugins");
161      if (remoteLoader != null) {
162        remoteRunner.LoadPlugins(remoteLoader.ActivePlugins);
163      } else if (LoadedPlugins != null && LoadedPlugins.Count > 0) {
164        remoteRunner.LoadPlugins(LoadedPlugins);
165      }
166      NotifyListeners(PluginManagerAction.Initialized, "All plugins");
167      return applicationDomain;
168    }
169
170    /// <summary>
171    /// Creates a new AppDomain with all plugins preloaded and Sandboxing capability.
172    /// </summary>
173    /// <param name="assembly">Assembly reference</param>
174    /// <returns>the strongname of the assembly</returns>
175    private StrongName CreateStrongName(Assembly assembly) {
176      if (assembly == null)
177        throw new ArgumentNullException("assembly");
178
179      AssemblyName assemblyName = assembly.GetName();
180      Debug.Assert(assemblyName != null, "Could not get assembly name");
181
182      // get the public key blob
183      byte[] publicKey = assemblyName.GetPublicKey();
184      if (publicKey == null || publicKey.Length == 0)
185        throw new InvalidOperationException("Assembly is not strongly named");
186
187      StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
188
189      // and create the StrongName
190      return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
191    }
192
193    public AppDomain CreateAndInitAppDomainWithSandbox(string friendlyName, bool sandboxed, Type jobType) {
194      PermissionSet pset;
195
196      DiscoveryService dService = new DiscoveryService();
197      //get the declaring plugin of the job
198      PluginInfo jobPlugin = dService.GetDeclaringPlugin(jobType);
199
200      //get all the plugins that have dependencies with the jobplugin
201      List<PluginInfo> depPlugins = GetDependentPluginsRec(jobPlugin);
202      //insert all jobs into one list
203      depPlugins.Add(jobPlugin);
204     
205      if (sandboxed) {
206        pset = new PermissionSet(PermissionState.None);
207        pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
208        pset.AddPermission(new ReflectionPermission(PermissionState.Unrestricted));
209        FileIOPermission fPerm = new FileIOPermission(PermissionState.None);
210             
211        foreach (PluginInfo plugin in depPlugins) {
212            foreach(String assemblies in plugin.Assemblies)
213              fPerm.AddPathList(FileIOPermissionAccess.AllAccess, assemblies);
214        }
215       
216        pset.AddPermission(fPerm);
217
218      } else {
219        pset = new PermissionSet(PermissionState.Unrestricted);
220      }
221      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
222      setup.PrivateBinPath = pluginDir;
223      setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;     
224      AppDomain applicationDomain = AppDomain.CreateDomain(friendlyName, AppDomain.CurrentDomain.Evidence, setup, pset, CreateStrongName(Assembly.GetExecutingAssembly()));
225                     
226      Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
227      NotifyListeners(PluginManagerAction.Initializing, "All plugins");
228
229      if (depPlugins != null && depPlugins.Count > 0) {
230        remoteRunner.LoadPlugins(depPlugins);
231      }
232      NotifyListeners(PluginManagerAction.Initialized, "All plugins");
233      return applicationDomain;
234    }
235
236    /// <summary>
237    /// Calculates a set of plugins that directly or transitively depend on the plugin given in the argument.
238    /// </summary>
239    /// <param name="pluginInfo">The plugin the other depend on.</param>
240    /// <returns>a list of plugins that are directly of transitively dependent.</returns>
241    public List<PluginInfo> GetDependentPlugins(PluginInfo pluginInfo) {
242      List<PluginInfo> mergedList = new List<PluginInfo>();
243      foreach (PluginInfo plugin in InstalledPlugins) {
244        if (plugin.Dependencies.Contains(pluginInfo)) {
245          if (!mergedList.Contains(plugin)) {
246            mergedList.Add(plugin);
247          }
248          // for each of the dependent plugins add the list of transitively dependent plugins
249          // make sure that only one entry for each plugin is added to the merged list
250          GetDependentPlugins(plugin).ForEach(delegate(PluginInfo dependentPlugin) {
251            if (!mergedList.Contains(dependentPlugin)) {
252              mergedList.Add(dependentPlugin);
253            }
254          });
255        }
256      }
257      return mergedList;
258    }
259
260
261    /// <summary>
262    /// Calculates a set of plugins that directly or transitively depend on the plugin given in the argument.
263    /// </summary>
264    /// <param name="pluginInfo">The plugin the other depend on.</param>
265    /// <returns>a list of plugins that are directly of transitively dependent.</returns>
266    public List<PluginInfo> GetDependentPluginsRec(PluginInfo pluginInfo) {
267      List<PluginInfo> mergedList = new List<PluginInfo>();
268      //Bugfix the hell out of this...
269      //Bugfixed the hell out of this...
270      foreach (PluginInfo info in pluginInfo.Dependencies) {
271        if (!mergedList.Contains(info)) {
272          mergedList.Add(info);
273          AddDependenciesRecursive(info, mergedList);       
274        }       
275      }
276      return mergedList;
277    }
278
279    private void AddDependenciesRecursive(PluginInfo info, List<PluginInfo> mergedList) {
280      //if we've already processed this element => STOP IT!
281      if(!mergedList.Contains(info)) {
282        mergedList.Add(info);
283        return;
284      }
285      foreach (PluginInfo depinfo in info.Dependencies)
286        AddDependenciesRecursive(depinfo, mergedList);       
287    }
288
289    /// <summary>
290    /// Unloads all plugins.
291    /// </summary>
292    public void UnloadAllPlugins() {
293      AppDomain.Unload(pluginDomain);
294    }
295
296    /// <summary>
297    /// Loads all plugins.
298    /// </summary>
299    public void LoadAllPlugins() {
300      Initialize();
301    }
302
303    /// <inheritdoc cref="Loader.OnDelete"/>
304    public void OnDelete(PluginInfo pluginInfo) {
305      remoteLoader.OnDelete(pluginInfo);
306    }
307
308    /// <inheritdoc cref="Loader.OnInstall"/>
309    public void OnInstall(PluginInfo pluginInfo) {
310      remoteLoader.OnInstall(pluginInfo);
311    }
312
313    /// <inheritdoc cref="Loader.OnPreUpdate"/>
314    public void OnPreUpdate(PluginInfo pluginInfo) {
315      remoteLoader.OnPreUpdate(pluginInfo);
316    }
317
318    /// <inheritdoc cref="Loader.OnPostUpdate"/>
319    public void OnPostUpdate(PluginInfo pluginInfo) {
320      remoteLoader.OnPostUpdate(pluginInfo);
321    }
322
323    private void NotifyListeners(PluginManagerAction action, string text) {
324      if (Action != null) {
325        Action(this, new PluginManagerActionEventArgs(text, action));
326      }
327    }
328  }
329}
Note: See TracBrowser for help on using the repository browser.