Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3247 was 3247, checked in by gkronber, 14 years ago

Implemented a LightweightApplicationManager for unit testing and extracted the DefaultApplicationManager out of class ApplicationManager. #954 (ApplicationManager.Manager should have a default instance to enable unit testing)

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