#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;
using System.Security.Policy;
using System.Reflection;
using System.Diagnostics;
using System.Security.Permissions;
using System.Security;
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);
AppDomain applicationDomain = null;
try {
applicationDomain = CreateAndInitAppDomain(appInfo.Name + " AppDomain");
Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
remoteRunner.Run(appInfo);
}
finally {
// make sure domain is unloaded in all cases
if (applicationDomain != null) AppDomain.Unload(applicationDomain);
}
}
///
/// Creates a new AppDomain with all plugins preloaded.
///
/// Name of the new AppDomain
/// the new AppDomain with all plugins preloaded.
public AppDomain CreateAndInitAppDomain(string friendlyName) {
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.PrivateBinPath = pluginDir;
AppDomain applicationDomain = AppDomain.CreateDomain(friendlyName, null, setup);
Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
NotifyListeners(PluginManagerAction.Initializing, "All plugins");
if (remoteLoader != null) {
remoteRunner.LoadPlugins(remoteLoader.ActivePlugins);
} else if (LoadedPlugins != null && LoadedPlugins.Count > 0) {
remoteRunner.LoadPlugins(LoadedPlugins);
}
NotifyListeners(PluginManagerAction.Initialized, "All plugins");
return applicationDomain;
}
///
/// Creates a new AppDomain with all plugins preloaded and Sandboxing capability
///
/// Assembly reference
/// the strongname of the assembly
private StrongName CreateStrongName(Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
AssemblyName assemblyName = assembly.GetName();
Debug.Assert(assemblyName != null, "Could not get assembly name");
// get the public key blob
byte[] publicKey = assemblyName.GetPublicKey();
if (publicKey == null || publicKey.Length == 0)
throw new InvalidOperationException("Assembly is not strongly named");
StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
// and create the StrongName
return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
}
public AppDomain CreateAndInitAppDomainWithSandbox(string friendlyName, bool sandboxed, Type jobType) {
PermissionSet pset;
if (sandboxed) {
pset = new PermissionSet(PermissionState.None);
pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
} else {
pset = new PermissionSet(PermissionState.Unrestricted);
}
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.PrivateBinPath = pluginDir;
setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
AppDomain applicationDomain = AppDomain.CreateDomain(friendlyName, AppDomain.CurrentDomain.Evidence, setup, pset, CreateStrongName(Assembly.GetExecutingAssembly()));
Runner remoteRunner = (Runner)applicationDomain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.GetName().Name, typeof(Runner).FullName);
NotifyListeners(PluginManagerAction.Initializing, "All plugins");
DiscoveryService dService = new DiscoveryService();
PluginInfo jobPlugin = dService.GetDeclaringPlugin(jobType);
List depPlugins = GetDependentPlugins(jobPlugin);
if (depPlugins != null && depPlugins.Count > 0) {
remoteRunner.LoadPlugins(depPlugins);
}
NotifyListeners(PluginManagerAction.Initialized, "All plugins");
return 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));
}
}
}
}