Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceReintegration/HeuristicLab.PluginInfrastructure/3.3/Manager/PluginManager.cs @ 15866

Last change on this file since 15866 was 14927, checked in by gkronber, 8 years ago

#2520: changed all usages of StorableClass to use StorableType with an auto-generated GUID (did not add StorableType to other type definitions yet)

File size: 8.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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      } 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    public void Run(ApplicationDescription appInfo, ICommandLineArgument[] args) {
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);
126        applicationManager.PluginLoaded += applicationManager_PluginLoaded;
127        applicationManager.PluginUnloaded += applicationManager_PluginUnloaded;
128        applicationManager.PrepareApplicationDomain(applications, plugins);
129        OnApplicationStarted(new PluginInfrastructureEventArgs(appInfo));
130        applicationManager.Run(appInfo, args);
131      } finally {
132        // make sure domain is unloaded in all cases
133        AppDomain.Unload(applicationDomain);
134      }
135    }
136
137    private void applicationManager_PluginUnloaded(object sender, PluginInfrastructureEventArgs e) {
138      // unload the matching plugin description (
139      PluginDescription desc = (PluginDescription)e.Entity;
140
141      // access to plugin descriptions has to be synchronized because multiple applications
142      // can be started or stopped at the same time
143      lock (locker) {
144        // also unload the matching plugin description in this AppDomain
145        plugins.First(x => x.Equals(desc)).Unload();
146      }
147      OnPluginUnloaded(e);
148    }
149
150    private void applicationManager_PluginLoaded(object sender, PluginInfrastructureEventArgs e) {
151      // load the matching plugin description (
152      PluginDescription desc = (PluginDescription)e.Entity;
153      // access to plugin descriptions has to be synchronized because multiple applications
154      // can be started or stopped at the same time
155      lock (locker) {
156        // also load the matching plugin description in this AppDomain
157        plugins.First(x => x.Equals(desc)).Load();
158      }
159      OnPluginLoaded(e);
160    }
161
162    #region event raising methods
163    private void OnPluginLoaded(PluginInfrastructureEventArgs e) {
164      if (PluginLoaded != null) {
165        PluginLoaded(this, e);
166      }
167    }
168
169    private void OnPluginUnloaded(PluginInfrastructureEventArgs e) {
170      if (PluginUnloaded != null) {
171        PluginUnloaded(this, e);
172      }
173    }
174
175    private void OnInitializing(PluginInfrastructureEventArgs e) {
176      if (Initializing != null) {
177        Initializing(this, e);
178      }
179    }
180
181    private void OnInitialized(PluginInfrastructureEventArgs e) {
182      if (Initialized != null) {
183        Initialized(this, e);
184      }
185    }
186
187    private void OnApplicationStarting(PluginInfrastructureEventArgs e) {
188      if (ApplicationStarting != null) {
189        ApplicationStarting(this, e);
190      }
191    }
192
193    private void OnApplicationStarted(PluginInfrastructureEventArgs e) {
194      if (ApplicationStarted != null) {
195        ApplicationStarted(this, e);
196      }
197    }
198    #endregion
199
200    // infinite lease time
201    /// <summary>
202    /// Make sure that the plugin manager is never disposed (necessary for cross-app-domain events)
203    /// </summary>
204    /// <returns><c>null</c>.</returns>
205    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
206    public override object InitializeLifetimeService() {
207      return null;
208    }
209  }
210}
Note: See TracBrowser for help on using the repository browser.