Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/3.3/Manager/PluginManager.cs @ 6479

Last change on this file since 6479 was 5703, checked in by cneumuel, 14 years ago

#1429

  • ensure that assemblies are only searched in PrivateBinPath and not in ApplicationBase. This is important for a hive slave creating a AppDomain with assemblies from a path different from the path of the executing AppDomain
File size: 8.7 KB
RevLine 
[2]1#region License Information
2/* HeuristicLab
[5445]3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[2]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;
[4068]24using System.Linq;
[882]25using System.Reflection;
[4482]26using System.Security.Permissions;
[2]27
[2481]28namespace HeuristicLab.PluginInfrastructure.Manager {
[2]29
30  // must extend MarshalByRefObject because of event passing between Loader and PluginManager (each in it's own AppDomain)
[1189]31  /// <summary>
32  /// Class to manage different plugins.
33  /// </summary>
[4414]34  public sealed class PluginManager : MarshalByRefObject {
35    public event EventHandler<PluginInfrastructureEventArgs> PluginLoaded;
36    public event EventHandler<PluginInfrastructureEventArgs> PluginUnloaded;
37    public event EventHandler<PluginInfrastructureEventArgs> Initializing;
38    public event EventHandler<PluginInfrastructureEventArgs> Initialized;
39    public event EventHandler<PluginInfrastructureEventArgs> ApplicationStarting;
40    public event EventHandler<PluginInfrastructureEventArgs> ApplicationStarted;
[2]41
42    private string pluginDir;
43
[2481]44    private List<PluginDescription> plugins;
[2513]45    /// <summary>
46    /// Gets all installed plugins.
47    /// </summary>
[4414]48    public IEnumerable<PluginDescription> Plugins {
[2513]49      get { return plugins; }
50    }
[29]51
[2481]52    private List<ApplicationDescription> applications;
[1189]53    /// <summary>
54    /// Gets all installed applications.
55    /// </summary>
[5652]56    public IEnumerable<ApplicationDescription> Applications {
[2481]57      get { return applications; }
[2]58    }
59
[2503]60    private object locker = new object();
61    private bool initialized;
62
[4414]63    public PluginManager(string pluginDir) {
[2503]64      this.pluginDir = pluginDir;
65      plugins = new List<PluginDescription>();
66      applications = new List<ApplicationDescription>();
67      initialized = false;
68    }
69
[1189]70    /// <summary>
[2481]71    /// Determines installed plugins and checks if all plugins are loadable.
[1189]72    /// </summary>
[4414]73    public void DiscoverAndCheckPlugins() {
[2922]74      OnInitializing(PluginInfrastructureEventArgs.Empty);
[2]75      AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
76      setup.PrivateBinPath = pluginDir;
[5703]77      // probing should only occur in PrivateBinPath (not in ApplicationBase). This is enforced by the value string.Empty
78      setup.PrivateBinPathProbe = string.Empty;
[2497]79      AppDomain pluginDomain = null;
80      try {
81        pluginDomain = AppDomain.CreateDomain("plugin domain", null, setup);
[2504]82        Type pluginValidatorType = typeof(PluginValidator);
[5523]83        PluginValidator remoteValidator = (PluginValidator)pluginDomain.CreateInstanceAndUnwrap(pluginValidatorType.Assembly.FullName, pluginValidatorType.FullName, true, BindingFlags.NonPublic | BindingFlags.Instance, null, null, null, null);
[2503]84        remoteValidator.PluginDir = pluginDir;
85        // forward all events from the remoteValidator to listeners
86        remoteValidator.PluginLoaded +=
87          delegate(object sender, PluginInfrastructureEventArgs e) {
[2922]88            OnPluginLoaded(e);
[2503]89          };
90        // get list of plugins and applications from the validator
91        plugins.Clear(); applications.Clear();
92        plugins.AddRange(remoteValidator.Plugins);
93        applications.AddRange(remoteValidator.Applications);
[2497]94      }
95      finally {
96        // discard the AppDomain that was used for plugin discovery
[2503]97        AppDomain.Unload(pluginDomain);
98        // unload all plugins
[2922]99        foreach (var pluginDescription in plugins.Where(x => x.PluginState == PluginState.Loaded)) {
[2503]100          pluginDescription.Unload();
[2922]101          OnPluginUnloaded(new PluginInfrastructureEventArgs(pluginDescription));
102        }
[2503]103        initialized = true;
[2922]104        OnInitialized(PluginInfrastructureEventArgs.Empty);
[2497]105      }
[2]106    }
107
[2481]108
[2]109    /// <summary>
[2592]110    /// Starts an application in a separate AppDomain.
111    /// Loads all enabled plugins and starts the application via an ApplicationManager instance activated in the new AppDomain.
[2]112    /// </summary>
113    /// <param name="appInfo">application to run</param>
[5652]114    public void Run(ApplicationDescription appInfo) {
[2503]115      if (!initialized) throw new InvalidOperationException("PluginManager is not initialized. DiscoverAndCheckPlugins() must be called before Run()");
[2]116      // create a separate AppDomain for the application
[2488]117      // initialize the static ApplicationManager in the AppDomain
[2]118      // and remotely tell it to start the application
119
[2922]120      OnApplicationStarting(new PluginInfrastructureEventArgs(appInfo));
[766]121      AppDomain applicationDomain = null;
[241]122      try {
[2481]123        AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
124        setup.PrivateBinPath = pluginDir;
[2738]125        applicationDomain = AppDomain.CreateDomain(AppDomain.CurrentDomain.FriendlyName, null, setup);
[3247]126        Type applicationManagerType = typeof(DefaultApplicationManager);
127        DefaultApplicationManager applicationManager =
[5523]128          (DefaultApplicationManager)applicationDomain.CreateInstanceAndUnwrap(applicationManagerType.Assembly.FullName, applicationManagerType.FullName, true, BindingFlags.NonPublic | BindingFlags.Instance, null, null, null, null);
[2497]129        applicationManager.PluginLoaded += applicationManager_PluginLoaded;
[2503]130        applicationManager.PluginUnloaded += applicationManager_PluginUnloaded;
[2504]131        applicationManager.PrepareApplicationDomain(applications, plugins);
[2922]132        OnApplicationStarted(new PluginInfrastructureEventArgs(appInfo));
[2488]133        applicationManager.Run(appInfo);
[766]134      }
135      finally {
[241]136        // make sure domain is unloaded in all cases
[2503]137        AppDomain.Unload(applicationDomain);
[241]138      }
[2]139    }
140
[2503]141    private void applicationManager_PluginUnloaded(object sender, PluginInfrastructureEventArgs e) {
142      // unload the matching plugin description (
[2497]143      PluginDescription desc = (PluginDescription)e.Entity;
[2503]144
145      // access to plugin descriptions has to be synchronized because multiple applications
146      // can be started or stopped at the same time
147      lock (locker) {
[2922]148        // also unload the matching plugin description in this AppDomain
[2503]149        plugins.First(x => x.Equals(desc)).Unload();
[2497]150      }
[2922]151      OnPluginUnloaded(e);
[2497]152    }
[766]153
[2503]154    private void applicationManager_PluginLoaded(object sender, PluginInfrastructureEventArgs e) {
155      // load the matching plugin description (
156      PluginDescription desc = (PluginDescription)e.Entity;
157      // access to plugin descriptions has to be synchronized because multiple applications
158      // can be started or stopped at the same time
159      lock (locker) {
[2922]160        // also load the matching plugin description in this AppDomain
[2503]161        plugins.First(x => x.Equals(desc)).Load();
162      }
[2922]163      OnPluginLoaded(e);
[2497]164    }
[882]165
[2922]166    #region event raising methods
167    private void OnPluginLoaded(PluginInfrastructureEventArgs e) {
168      if (PluginLoaded != null) {
169        PluginLoaded(this, e);
[2]170      }
171    }
[2615]172
[2922]173    private void OnPluginUnloaded(PluginInfrastructureEventArgs e) {
174      if (PluginUnloaded != null) {
175        PluginUnloaded(this, e);
176      }
177    }
178
179    private void OnInitializing(PluginInfrastructureEventArgs e) {
180      if (Initializing != null) {
181        Initializing(this, e);
182      }
183    }
184
185    private void OnInitialized(PluginInfrastructureEventArgs e) {
186      if (Initialized != null) {
187        Initialized(this, e);
188      }
189    }
190
191    private void OnApplicationStarting(PluginInfrastructureEventArgs e) {
192      if (ApplicationStarting != null) {
193        ApplicationStarting(this, e);
194      }
195    }
196
197    private void OnApplicationStarted(PluginInfrastructureEventArgs e) {
198      if (ApplicationStarted != null) {
199        ApplicationStarted(this, e);
200      }
201    }
202    #endregion
203
[2615]204    // infinite lease time
205    /// <summary>
[2922]206    /// Make sure that the plugin manager is never disposed (necessary for cross-app-domain events)
[2615]207    /// </summary>
208    /// <returns><c>null</c>.</returns>
[4482]209    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
[2615]210    public override object InitializeLifetimeService() {
211      return null;
212    }
[2]213  }
214}
Note: See TracBrowser for help on using the repository browser.