Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4414 was 4414, checked in by cneumuel, 13 years ago

Changes to PluginInfrastructure for Hive compatibility (#1191)

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