/// <summary> /// Initialize /// </summary> public static void Initialize(ApplicationPartManager applicationPartManager, GrandConfig config) { if (applicationPartManager == null) { throw new ArgumentNullException("applicationPartManager"); } if (config == null) { throw new ArgumentNullException("config"); } using (new WriteLockDisposable(Locker)) { // TODO: Add verbose exception handling / raising here since this is happening on app startup and could // prevent app from starting altogether var pluginFolder = new DirectoryInfo(CommonHelper.MapPath(PluginsPath)); _shadowCopyFolder = new DirectoryInfo(CommonHelper.MapPath(ShadowCopyPath)); var referencedPlugins = new List <PluginDescriptor>(); var incompatiblePlugins = new List <string>(); try { var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath()); Debug.WriteLine("Creating shadow copy folder and querying for dlls"); //ensure folders are created Directory.CreateDirectory(pluginFolder.FullName); Directory.CreateDirectory(_shadowCopyFolder.FullName); //get list of all files in bin var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories); if (config.ClearPluginShadowDirectoryOnStartup) { //clear out shadow copied plugins foreach (var f in binFiles) { Debug.WriteLine("Deleting " + f.Name); try { //ignore index.htm var fileName = Path.GetFileName(f.FullName); if (fileName.Equals("index.htm", StringComparison.OrdinalIgnoreCase)) { continue; } File.Delete(f.FullName); } catch (Exception exc) { Debug.WriteLine("Error deleting file " + f.Name + ". Exception: " + exc); } } } //load description files foreach (var dfd in GetDescriptionFilesAndDescriptors(pluginFolder)) { var descriptionFile = dfd.Key; var pluginDescriptor = dfd.Value; //ensure that version of plugin is valid if (!pluginDescriptor.SupportedVersions.Contains(GrandVersion.CurrentVersion, StringComparer.OrdinalIgnoreCase)) { incompatiblePlugins.Add(pluginDescriptor.SystemName); continue; } //some validation if (String.IsNullOrWhiteSpace(pluginDescriptor.SystemName)) { throw new Exception(string.Format("A plugin '{0}' has no system name. Try assigning the plugin a unique name and recompiling.", descriptionFile.FullName)); } if (referencedPlugins.Contains(pluginDescriptor)) { throw new Exception(string.Format("A plugin with '{0}' system name is already defined", pluginDescriptor.SystemName)); } //set 'Installed' property pluginDescriptor.Installed = installedPluginSystemNames .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.OrdinalIgnoreCase)) != null; try { if (descriptionFile.Directory == null) { throw new Exception(string.Format("Directory cannot be resolved for '{0}' description file", descriptionFile.Name)); } //get list of all DLLs in plugins (not in bin!) var pluginFiles = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories) //just make sure we're not registering shadow copied plugins .Where(x => !binFiles.Select(q => q.FullName).Contains(x.FullName)) .Where(x => IsPackagePluginFolder(x.Directory)) .ToList(); //TO DO - copy files only from plugins - description.txt ? //other plugin description info var mainPluginFile = pluginFiles .FirstOrDefault(x => x.Name.Equals(pluginDescriptor.PluginFileName, StringComparison.OrdinalIgnoreCase)); pluginDescriptor.OriginalAssemblyFile = mainPluginFile; //shadow copy main plugin file pluginDescriptor.ReferencedAssembly = PerformFileDeploy(mainPluginFile, applicationPartManager); //load all other referenced assemblies now foreach (var plugin in pluginFiles .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.OrdinalIgnoreCase)) .Where(x => !IsAlreadyLoaded(x))) { PerformFileDeploy(plugin, applicationPartManager); } //init plugin type (only one plugin per assembly is allowed) foreach (var t in pluginDescriptor.ReferencedAssembly.GetTypes()) { if (typeof(IPlugin).IsAssignableFrom(t)) { if (!t.GetTypeInfo().IsInterface) { if (t.GetTypeInfo().IsClass&& !t.GetTypeInfo().IsAbstract) { pluginDescriptor.PluginType = t; break; } } } } referencedPlugins.Add(pluginDescriptor); } catch (ReflectionTypeLoadException ex) { //add a plugin name. this way we can easily identify a problematic plugin var msg = string.Format("Plugin '{0}'. ", pluginDescriptor.FriendlyName); foreach (var e in ex.LoaderExceptions) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); throw fail; } catch (Exception ex) { //add a plugin name. this way we can easily identify a problematic plugin var msg = string.Format("Plugin '{0}'. {1}", pluginDescriptor.FriendlyName, ex.Message); var fail = new Exception(msg, ex); throw fail; } } } catch (Exception ex) { var msg = string.Empty; for (var e = ex; e != null; e = e.InnerException) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); throw fail; } ReferencedPlugins = referencedPlugins; IncompatiblePlugins = incompatiblePlugins; } }
public static void Initialize(IMvcCoreBuilder mvcCoreBuilder, GrandConfig config) { lock (_synLock) { if (mvcCoreBuilder == null) { throw new ArgumentNullException("mvcCoreBuilder"); } _config = config ?? throw new ArgumentNullException("config"); _pluginFolder = new DirectoryInfo(CommonHelper.MapPath(PluginsPath)); _shadowCopyFolder = new DirectoryInfo(CommonHelper.MapPath(ShadowCopyPath)); var referencedPlugins = new List <PluginDescriptor>(); var incompatiblePlugins = new List <string>(); try { var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath()); Log.Information("Creating shadow copy folder and querying for dlls"); //ensure folders are created Directory.CreateDirectory(_pluginFolder.FullName); Directory.CreateDirectory(_shadowCopyFolder.FullName); //get list of all files in bin var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories); if (config.ClearPluginShadowDirectoryOnStartup) { //clear out shadow copied plugins foreach (var f in binFiles) { Log.Information($"Deleting {f.Name}"); try { //ignore index.htm var fileName = Path.GetFileName(f.FullName); if (fileName.Equals("index.htm", StringComparison.OrdinalIgnoreCase)) { continue; } File.Delete(f.FullName); } catch (Exception exc) { Log.Error(exc, "PluginManager"); } } } //load description files foreach (var pluginDescriptor in GetDescriptions()) { //ensure that version of plugin is valid if (!pluginDescriptor.SupportedVersions.Contains(GrandVersion.SupportedPluginVersion, StringComparer.OrdinalIgnoreCase)) { incompatiblePlugins.Add(pluginDescriptor.SystemName); continue; } //some validation if (string.IsNullOrWhiteSpace(pluginDescriptor.SystemName)) { throw new Exception(string.Format("A plugin '{0}' has no system name. Try assigning the plugin a unique name and recompiling.", pluginDescriptor.SystemName)); } if (referencedPlugins.Contains(pluginDescriptor)) { throw new Exception(string.Format("A plugin with '{0}' system name is already defined", pluginDescriptor.SystemName)); } //set 'Installed' property pluginDescriptor.Installed = installedPluginSystemNames .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.OrdinalIgnoreCase)) != null; try { if (!config.PluginShadowCopy) { //remove deps.json files var depsFiles = pluginDescriptor.OriginalAssemblyFile.Directory.GetFiles("*.deps.json", SearchOption.TopDirectoryOnly); foreach (var f in depsFiles) { try { File.Delete(f.FullName); } catch (Exception exc) { Log.Error(exc, "PluginManager"); } } } //main plugin file AddApplicationPart(mvcCoreBuilder, pluginDescriptor.ReferencedAssembly, pluginDescriptor.SystemName, pluginDescriptor.PluginFileName); //init plugin type (only one plugin per assembly is allowed) foreach (var t in pluginDescriptor.ReferencedAssembly.GetTypes()) { if (typeof(IPlugin).IsAssignableFrom(t)) { if (!t.GetTypeInfo().IsInterface) { if (t.GetTypeInfo().IsClass&& !t.GetTypeInfo().IsAbstract) { pluginDescriptor.PluginType = t; break; } } } } referencedPlugins.Add(pluginDescriptor); } catch (ReflectionTypeLoadException ex) { //add a plugin name. this way we can easily identify a problematic plugin var msg = string.Format("Plugin '{0}'. ", pluginDescriptor.FriendlyName); foreach (var e in ex.LoaderExceptions) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); throw fail; } catch (Exception ex) { //add a plugin name. this way we can easily identify a problematic plugin var msg = string.Format("Plugin '{0}'. {1}", pluginDescriptor.FriendlyName, ex.Message); var fail = new Exception(msg, ex); throw fail; } } } catch (Exception ex) { var msg = string.Empty; for (var e = ex; e != null; e = e.InnerException) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); throw fail; } ReferencedPlugins = referencedPlugins; IncompatiblePlugins = incompatiblePlugins; } }