private void InitPlugins(object appEntryPoint, string localPluginsList, bool preferTestPluginEntryPoints) { using (tracer.NewFrame) { string thisPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string pluginsDirectory = Path.Combine(thisPath, "Plugins"); bool pluginsDirectoryExists = Directory.Exists(pluginsDirectory); tracer.Info("plugins directory: {0}{1}", pluginsDirectory, !pluginsDirectoryExists ? " (MISSING!)" : ""); var localPluginDirs = localPluginsList.Split(new [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(dir => Path.IsPathRooted(dir) ? dir : Path.GetFullPath(Path.Combine(thisPath, dir))) .Select(dir => (dir, exists: Directory.Exists(dir))) .ToArray(); tracer.Info("local plugin directories: {0}", string.Join(",", localPluginDirs.Select(d => $"{d.dir}{(d.exists ? "" : " (MISSING!)")}")) ); var manifests = (pluginsDirectoryExists ? Directory.EnumerateDirectories(pluginsDirectory) : new string[0]) .Union(localPluginDirs.Where(d => d.exists).Select(d => d.dir)) .Select(pluginDirectory => { tracer.Info("---> plugin found {0}", pluginDirectory); IPluginManifest manifest = null; try { manifest = new PluginManifest(pluginDirectory); manifest.ValidateFilesExist(); } catch (Exception e) { tracer.Error(e, "Bad manifest"); telemetry.ReportException(e, "Bad manifest in " + pluginDirectory); } return(manifest); }).Where(manifest => manifest != null).ToDictionarySafe( manifest => manifest.Id, manifest => manifest, (exisingManifest, newManifest) => newManifest ); void LoadPlugin(IPluginManifest manifest) { var pluginEntry = (preferTestPluginEntryPoints ? manifest.TestEntry : null) ?? manifest.Entry; var pluginPath = pluginEntry.AbsolulePath; tracer.Info("Loading plugin {0} from '{1}'", manifest.Id, pluginPath); Stopwatch sw = Stopwatch.StartNew(); pluginFormatsManager.RegisterPluginFormats(manifest); var formatsLoadTime = sw.Elapsed; sw.Restart(); Assembly pluginAsm; try { pluginAsm = Assembly.LoadFrom(pluginPath); } catch (Exception e) { throw new Exception("Failed to load plugin asm", e); } var loadTime = sw.Elapsed; sw.Restart(); Type pluginType; try { pluginType = pluginAsm.GetType("LogJoint.Plugin"); } catch (Exception e) { throw new Exception("Failed to load plugin type", e); } var typeLoadTime = sw.Elapsed; if (pluginType == null) { throw new Exception("plugin class not found in plugin assembly"); } var modelEntryPoint = appEntryPoint.GetType().InvokeMember("Model", BindingFlags.GetProperty, null, appEntryPoint, new object[0]); if (modelEntryPoint == null) { throw new Exception("Model is missing from app entry point"); } var presentationEntryPoint = appEntryPoint.GetType().InvokeMember("Presentation", BindingFlags.GetProperty, null, appEntryPoint, new object[0]); if (presentationEntryPoint == null) { throw new Exception("Presentation is missing from app entry point"); } TimeSpan instantiationTime = TimeSpan.Zero; object plugin = null; bool TryCtr(params object[] @params) { var ctr = pluginType.GetConstructor(@params.Select(p => p.GetType()).ToArray()); if (ctr == null) { return(false); } sw.Restart(); try { plugin = ctr.Invoke(@params); } catch (Exception e) { throw new Exception("failed to create an instance of plugin", e); } instantiationTime = sw.Elapsed; return(true); } if (!TryCtr(appEntryPoint) && !TryCtr(modelEntryPoint) && !TryCtr(modelEntryPoint, presentationEntryPoint)) { throw new Exception("plugin class does not implement ctr with LogJoint.IApplication argument, or with LogJoint.IModel argument, or with IModel and IPresentation arguments"); } tracer.Info("plugin {0} accepted. times: loading formats={1}, loading dll={2}, type loading={3}, instantiation={4}", Path.GetFileName(pluginPath), formatsLoadTime, loadTime, typeLoadTime, instantiationTime); plugins.Add(plugin); pluginManifests = pluginManifests.Add(manifest); } var visitedPluginIds = new HashSet <string>(); void LoadPluginAndDependencies(string id) { if (visitedPluginIds.Add(id)) { if (!manifests.TryGetValue(id, out var manifest)) { throw new Exception($"Required plugin '{id}' not found"); } foreach (var dep in manifest.Dependencies) { LoadPluginAndDependencies(dep); } var sdks = manifest.Dependencies.SelectMany(dep => { if (!manifests.TryGetValue(dep, out var depManifest)) { throw new Exception($"Plugin {manifest.Id} requires {dep} that is not found"); } return(depManifest.Files.Where(f => f.Type == PluginFileType.SDK)); }).ToArray(); Assembly dependencyResolveHandler(object s, ResolveEventArgs e) { var fileName = $"{(new AssemblyName(e.Name)).Name}.dll"; var sdkFile = sdks.FirstOrDefault(f => Path.GetFileName(f.RelativePath) == fileName); try { return(sdkFile != null?Assembly.LoadFrom(sdkFile.AbsolulePath) : null); } catch (Exception ex) { throw new Exception($"Failed to load SDK asm '{sdkFile.AbsolulePath}' requested by {manifest.Id}", ex); } } AppDomain.CurrentDomain.AssemblyResolve += dependencyResolveHandler; try { LoadPlugin(manifest); } finally { AppDomain.CurrentDomain.AssemblyResolve -= dependencyResolveHandler; } } } foreach (string pluginId in manifests.Keys) { try { LoadPluginAndDependencies(pluginId); } catch (Exception e) { tracer.Error(e, $"Failed to load plugin '{pluginId}' or its dependencies"); telemetry.ReportException(e, "Loading of plugin " + pluginId); } } } }
private void InitPlugins(object entryPoint) { using (tracer.NewFrame) { string thisPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string pluginsDirectory = Path.Combine(thisPath, "Plugins"); bool pluginsDirectoryExists = Directory.Exists(pluginsDirectory); tracer.Info("plugins directory: {0}{1}", pluginsDirectory, !pluginsDirectoryExists ? " (MISSING!)" : ""); if (!pluginsDirectoryExists) { return; } var manifests = Directory.EnumerateDirectories(pluginsDirectory).Select(pluginDirectory => { tracer.Info("---> plugin found {0}", pluginDirectory); IPluginManifest manifest = null; try { manifest = new PluginManifest(pluginDirectory); manifest.ValidateFilesExist(); } catch (Exception e) { tracer.Error(e, "Bad manifest"); telemetry.ReportException(e, "Bad manifest in " + pluginDirectory); } return(manifest); }).Where(manifest => manifest != null).ToDictionary(manifest => manifest.Id); void LoadPlugin(IPluginManifest manifest) { var pluginPath = manifest.Entry.AbsolulePath; tracer.Info("Loading plugin {0} from '{1}'", manifest.Id, pluginPath); Stopwatch sw = Stopwatch.StartNew(); pluginFormatsManager.RegisterPluginFormats(manifest); var formatsLoadTime = sw.Elapsed; sw.Restart(); Assembly pluginAsm; try { pluginAsm = Assembly.LoadFrom(pluginPath); } catch (Exception e) { throw new Exception("Failed to load plugin asm", e); } var loadTime = sw.Elapsed; sw.Restart(); Type pluginType; try { pluginType = pluginAsm.GetType("LogJoint.Plugin"); } catch (Exception e) { throw new Exception("Failed to load plugin type", e); } var typeLoadTime = sw.Elapsed; if (pluginType == null) { throw new Exception("plugin class not found in plugin assembly"); } var ctr = pluginType.GetConstructor(new[] { entryPoint.GetType() }); if (ctr == null) { throw new Exception("plugin class does not implement ctr with LogJoint.IApplication argument"); } sw.Restart(); object plugin; try { plugin = ctr.Invoke(new[] { entryPoint }); } catch (Exception e) { throw new Exception("failed to create an instance of plugin", e); } var instantiationTime = sw.Elapsed; tracer.Info("plugin {0} accepted. times: loading formats={1}, loading dll={2}, type loading={3}, instantiation={4}", Path.GetFileName(pluginPath), formatsLoadTime, loadTime, typeLoadTime, instantiationTime); plugins.Add(plugin); pluginManifests = pluginManifests.Add(manifest); } var visitedPluginIds = new HashSet <string>(); void LoadPluginAndDependencies(string id) { if (visitedPluginIds.Add(id)) { if (!manifests.TryGetValue(id, out var manifest)) { throw new Exception($"Required plugin '{id}' not found"); } foreach (var dep in manifest.Dependencies) { LoadPluginAndDependencies(dep); } var sdks = manifest.Dependencies.SelectMany(dep => { if (!manifests.TryGetValue(dep, out var depManifest)) { throw new Exception($"Plugin {manifest.Id} requires {dep} that is not found"); } return(depManifest.Files.Where(f => f.Type == PluginFileType.SDK)); }).ToArray(); Assembly dependencyResolveHandler(object s, ResolveEventArgs e) { var fileName = $"{(new AssemblyName(e.Name)).Name}.dll"; var sdkFile = sdks.FirstOrDefault(f => Path.GetFileName(f.RelativePath) == fileName); try { return(sdkFile != null?Assembly.LoadFrom(sdkFile.AbsolulePath) : null); } catch (Exception ex) { throw new Exception($"Failed to load SDK asm '{sdkFile.AbsolulePath}' requested by {manifest.Id}", ex); } } AppDomain.CurrentDomain.AssemblyResolve += dependencyResolveHandler; try { LoadPlugin(manifest); } finally { AppDomain.CurrentDomain.AssemblyResolve -= dependencyResolveHandler; } } } foreach (string pluginId in manifests.Keys) { try { LoadPluginAndDependencies(pluginId); } catch (Exception e) { tracer.Error(e, $"Failed to load plugin '{pluginId}' or its dependencies"); telemetry.ReportException(e, "Loading of plugin " + pluginId); } } } }