#region License Information
/* HeuristicLab
* Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
namespace HeuristicLab.PluginInfrastructure {
// must extend MarshalByRefObject because of event passing between Loader and PluginManager (each in it's own AppDomain)
public class PluginManager : MarshalByRefObject {
// singleton: only one manager allowed in each AppDomain
private static PluginManager manager = new PluginManager();
public static PluginManager Manager {
get { return manager; }
}
// singleton: only one control manager allowed in each applicatoin (i.e. AppDomain)
private static IControlManager controlManager;
public static IControlManager ControlManager {
get { return controlManager; }
set { controlManager = value; }
}
public event PluginManagerActionEventHandler Action;
// holds a proxy for the loader in the special AppDomain for PluginManagament
private Loader remoteLoader;
private AppDomain pluginDomain;
private string pluginDir;
// singleton pattern
private PluginManager() {
this.pluginDir = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.PluginDir;
}
public ICollection InstalledPlugins {
get { return remoteLoader.InstalledPlugins; }
}
public ICollection DisabledPlugins {
get { return remoteLoader.DisabledPlugins; }
}
public ICollection ActivePlugins {
get { return remoteLoader.ActivePlugins; }
}
public ICollection InstalledApplications {
get { return remoteLoader.InstalledApplications; }
}
private ICollection loadedPlugins;
public ICollection LoadedPlugins {
get { return loadedPlugins; }
internal set { loadedPlugins = value; }
}
///
/// Creates a dedicated AppDomain for loading all plugins and checking dependencies.
///
public void Initialize() {
NotifyListeners(PluginManagerAction.Initializing, "-");
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.PrivateBinPath = pluginDir;
pluginDomain = AppDomain.CreateDomain("plugin domain", null, setup);
remoteLoader = (Loader)pluginDomain.CreateInstanceAndUnwrap("HeuristicLab.PluginInfraStructure", "HeuristicLab.PluginInfrastructure.Loader");
remoteLoader.PluginAction += delegate(object sender, PluginManagerActionEventArgs args) { if(Action != null) Action(this, args); };
remoteLoader.Init();
NotifyListeners(PluginManagerAction.Initialized, "-");
}
///
/// Creates a separate AppDomain.
/// Loads all active plugin assemblies and starts the application in the new AppDomain via a PluginRunner instance activated in the new AppDomain
///
/// application to run
public void Run(ApplicationInfo appInfo) {
// create a separate AppDomain for the application
// activate a PluginRunner instance in the application
// and remotely tell it to start the application
NotifyListeners(PluginManagerAction.Starting, appInfo.Name);
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.PrivateBinPath = pluginDir;
AppDomain applicationDomain = AppDomain.CreateDomain(appInfo.Name + " AppDomain", null, setup);
try {
Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap("HeuristicLab.PluginInfrastructure", "HeuristicLab.PluginInfrastructure.Runner");
NotifyListeners(PluginManagerAction.Initializing, "All plugins");
remoteRunner.LoadPlugins(remoteLoader.ActivePlugins);
NotifyListeners(PluginManagerAction.Initialized, "All plugins");
remoteRunner.Run(appInfo);
} catch(Exception ex) {
// can't handle exception here -> rethrow
throw new ApplicationException("Exception in "+appInfo.Name, ex);
} finally {
// make sure domain is unloaded in all cases
AppDomain.Unload(applicationDomain);
}
}
///
/// Calculates a set of plugins that directly or transitively depend on the plugin given in the argument.
///
///
/// a list of plugins that are directly of transitively dependent.
public List GetDependentPlugins(PluginInfo pluginInfo) {
List mergedList = new List();
foreach(PluginInfo plugin in InstalledPlugins) {
if(plugin.Dependencies.Contains(pluginInfo)) {
if(!mergedList.Contains(plugin)) {
mergedList.Add(plugin);
}
// for each of the dependent plugins add the list of transitively dependent plugins
// make sure that only one entry for each plugin is added to the merged list
GetDependentPlugins(plugin).ForEach(delegate(PluginInfo dependentPlugin) {
if(!mergedList.Contains(dependentPlugin)) {
mergedList.Add(dependentPlugin);
}
});
}
}
return mergedList;
}
public void UnloadAllPlugins() {
AppDomain.Unload(pluginDomain);
}
public void LoadAllPlugins() {
Initialize();
}
public void OnDelete(PluginInfo pluginInfo) {
remoteLoader.OnDelete(pluginInfo);
}
public void OnInstall(PluginInfo pluginInfo) {
remoteLoader.OnInstall(pluginInfo);
}
public void OnPreUpdate(PluginInfo pluginInfo) {
remoteLoader.OnPreUpdate(pluginInfo);
}
public void OnPostUpdate(PluginInfo pluginInfo) {
remoteLoader.OnPostUpdate(pluginInfo);
}
private void NotifyListeners(PluginManagerAction action, string text) {
if(Action != null) {
Action(this, new PluginManagerActionEventArgs(text, action));
}
}
}
}