Exemplo n.º 1
0
        public async IAsyncEnumerable <IntegrationRegistration> GetIntegrations()
        {
            var rootDirectory = Path.GetFullPath(this.Configuration.GetValue <string>("Mifs:IntegrationPath"));

            var integrationRegistrations      = new List <IntegrationRegistration>();
            var configurationFilesByDirectory = Directory.EnumerateFiles(rootDirectory, "*appsettings.json", SearchOption.AllDirectories)
                                                .GroupBy(file => Path.GetDirectoryName(file));

            foreach (var configurationFiles in configurationFilesByDirectory)
            {
                var directory                = Path.GetFullPath(configurationFiles.Key !);
                var configuration            = this.BuildConfigurationForIntegration(configurationFiles);
                var integrationConfiguration = configuration.GetSection("Integration")
                                               .Get <IntegrationConfiguration>();

                if (!this.IsIntegrationConfigurationValid(integrationConfiguration))
                {
                    this.Logger.LogWarning("Failed to register Integration in directory '{directory}' because it was invalid. Check its configuration contains the Integration Name and Entry Assembly.", directory);
                    continue;
                }

                this.Logger.LogInformation("Found Integration {integrationName} in directory {directory}.", integrationConfiguration.Name, directory);
                var integrationRegistration = new IntegrationRegistration(directory, configuration);
                yield return(integrationRegistration);

                await Task.Yield();
            }
        }
        public void Register(IntegrationRegistration integrationRegistration)
        {
            var integrationName = integrationRegistration.Name;

            if (this.TryGetRegistration(integrationName, out var existingRegistration))
            {
                if (existingRegistration is not null && integrationRegistration.Directory != existingRegistration.Directory)
                {
                    this.Logger.LogWarning("Trying to register {integrationName} from directory {newDirectory} but it is already registered for directory {existingDirectory}. If you intended to change the directory deregister the old one first.",
                                           integrationName, integrationRegistration.Directory, existingRegistration.Directory);
                }
                else
                {
                    this.Logger.LogInformation("Attempted to register {integrationName} but it is already registered.", integrationName);
                }

                return;
            }
        private async Task <IntegrationHostData> CreateAndStartIntegrationHost(ICacheEntry cacheEntry, IntegrationRegistration integrationRegistration, CancellationToken cancellationToken = default)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new IntegrationHostData(null, HostStatus.Errored)
                {
                    Message = "Startup cancelled."
                });
            }

            // Pull a fresh copy of the IntegrationConfiguration to do some validation
            var integrationConfiguration = integrationRegistration.IntegrationConfiguration;
            var integrationName          = integrationConfiguration.Name;
            var entryAssembly            = integrationConfiguration.EntryAssembly;

            if (integrationName is null)
            {
                return(new IntegrationHostData(null, HostStatus.Errored)
                {
                    Message = "Integration Name not set properly in configuration."
                });
            }

            if (entryAssembly is null)
            {
                return(new IntegrationHostData(null, HostStatus.Errored)
                {
                    Message = "Entry assembly not set properly in configuration."
                });
            }

            if (!integrationConfiguration.IsEnabled)
            {
                this.Logger.LogInformation("Did not start {integrationName}. The Integration is currently disabled.", integrationName);
                return(new IntegrationHostData(null, HostStatus.Disabled));
            }

            var entryAssemblyPath = Path.Combine(integrationRegistration.Directory, entryAssembly);

            if (!File.Exists(entryAssemblyPath))
            {
                this.Logger.LogError("Failed to Start {integrationName} because the entry assembly {entryAssemblyPath} could not be found", integrationName, entryAssemblyPath);
                return(new IntegrationHostData(null, HostStatus.Errored)
                {
                    Message = $"Failed to Start integration {integrationName} because the entry assembly {entryAssemblyPath} could not be found"
                });
            }

            // Use a custom Assembly load context to allow each Host to use its own dependencies.
            // If a custom dependency is not defined in the same folder, it will fallback to using any Assemblies loaded in the default context.
            // The only requirement is that the Mifs.dll cannot be different because that would result in different instances of IInitializationInstance's
            // making FindAndCreateIntegrationInitializationInstance never find a type to instantiate.
            var assemblyLoadContext = new IntegrationAssemblyLoadContext(integrationRegistration.Directory);
            var bootstrapInstance   = this.FindAndCreateIntegrationBootstrapInstance(entryAssemblyPath, AssemblyLoadContext.Default);

            if (bootstrapInstance is null)
            {
                return(new IntegrationHostData(null, HostStatus.Errored)
                {
                    Message = ""
                });
            }

            var host = await this.IntegrationHostFactory.CreateAndStart(bootstrapInstance, integrationRegistration.Configuration, cancellationToken);

            if (host is null)
            {
                return(new IntegrationHostData(host, HostStatus.Errored)
                {
                    Message = $"Host failed to create"
                });
            }

            // Make sure that when the host is shut down the assembly load context gets unloaded.
            var applicationLifetime = host.Services.GetRequiredService <IHostApplicationLifetime>();

            applicationLifetime.ApplicationStopping.Register(() => assemblyLoadContext.Unload());

            cacheEntry.RegisterPostEvictionCallback(this.OnCacheEntryEvicted);

            this.Logger.LogInformation("{integrationName} Started.", integrationName);
            return(new IntegrationHostData(host, HostStatus.Started));
        }