Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 7080 was 6998, checked in by ascheibe, 13 years ago

#1672 Changed again how plugin discovery works because of Hive. The reason is that it must be possible to move the plugin and working directories away from the original slave working directory. This is needed for the Slave App and also in the future for the windows service because we don't want it to run as the LocalSystem user.
I have removed setting the PrivateBinPath and am now setting the ApplicationBase. This doesn't effect HL (because ApplicationBase is set by default to pluginDir anyway) but makes Hive work. The reason why setting the PrivateBinPath doesn't work with moving plugin and working directories is (from msdn): "Private assemblies are deployed in the same directory structure as the application. If the directories specified for PrivateBinPath are not under ApplicationBase, they are ignored."

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