#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) { 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"); if (remoteLoader != null) { remoteRunner.LoadPlugins(remoteLoader.ActivePlugins); } else if (LoadedPlugins != null && LoadedPlugins.Count > 0) { remoteRunner.LoadPlugins(LoadedPlugins); } 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)); } } } }