Example #1
0
        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);
                    }
                }
            }
        }
Example #2
0
        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);
                    }
                }
            }
        }