Merge pull request #2480 from mark-monteiro/support-injecting-iconfiguration

Support Injecting IConfiguration
This commit is contained in:
Bond-009 2020-03-11 15:43:39 +01:00 committed by GitHub
commit 0b51de3af5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 50 deletions

View File

@ -325,8 +325,6 @@ namespace Emby.Server.Implementations
private IMediaSourceManager MediaSourceManager { get; set; } private IMediaSourceManager MediaSourceManager { get; set; }
private readonly IConfiguration _configuration;
/// <summary> /// <summary>
/// Gets the installation manager. /// Gets the installation manager.
/// </summary> /// </summary>
@ -364,11 +362,8 @@ namespace Emby.Server.Implementations
IStartupOptions options, IStartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
IImageEncoder imageEncoder, IImageEncoder imageEncoder,
INetworkManager networkManager, INetworkManager networkManager)
IConfiguration configuration)
{ {
_configuration = configuration;
XmlSerializer = new MyXmlSerializer(); XmlSerializer = new MyXmlSerializer();
NetworkManager = networkManager; NetworkManager = networkManager;
@ -584,7 +579,8 @@ namespace Emby.Server.Implementations
} }
} }
public async Task InitAsync(IServiceCollection serviceCollection) /// <inheritdoc/>
public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig)
{ {
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@ -617,7 +613,7 @@ namespace Emby.Server.Implementations
DiscoverTypes(); DiscoverTypes();
await RegisterResources(serviceCollection).ConfigureAwait(false); await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false);
ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath; ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
if (string.IsNullOrEmpty(ContentRoot)) if (string.IsNullOrEmpty(ContentRoot))
@ -656,7 +652,7 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Registers resources that classes will depend on /// Registers resources that classes will depend on
/// </summary> /// </summary>
protected async Task RegisterResources(IServiceCollection serviceCollection) protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig)
{ {
serviceCollection.AddMemoryCache(); serviceCollection.AddMemoryCache();
@ -665,8 +661,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton<IConfiguration>(_configuration);
serviceCollection.AddSingleton(JsonSerializer); serviceCollection.AddSingleton(JsonSerializer);
// TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed // TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed
@ -758,7 +752,7 @@ namespace Emby.Server.Implementations
ProcessFactory, ProcessFactory,
LocalizationManager, LocalizationManager,
() => SubtitleEncoder, () => SubtitleEncoder,
_configuration, startupConfig,
StartupOptions.FFmpegPath); StartupOptions.FFmpegPath);
serviceCollection.AddSingleton(MediaEncoder); serviceCollection.AddSingleton(MediaEncoder);
@ -780,7 +774,7 @@ namespace Emby.Server.Implementations
this, this,
LoggerFactory.CreateLogger<HttpListenerHost>(), LoggerFactory.CreateLogger<HttpListenerHost>(),
ServerConfigurationManager, ServerConfigurationManager,
_configuration, startupConfig,
NetworkManager, NetworkManager,
JsonSerializer, JsonSerializer,
XmlSerializer, XmlSerializer,

View File

@ -23,23 +23,20 @@ namespace Jellyfin.Server
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="imageEncoder">The <see cref="IImageEncoder" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="imageEncoder">The <see cref="IImageEncoder" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="configuration">The <see cref="IConfiguration" /> to be used by the <see cref="CoreAppHost" />.</param>
public CoreAppHost( public CoreAppHost(
ServerApplicationPaths applicationPaths, ServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
StartupOptions options, StartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
IImageEncoder imageEncoder, IImageEncoder imageEncoder,
INetworkManager networkManager, INetworkManager networkManager)
IConfiguration configuration)
: base( : base(
applicationPaths, applicationPaths,
loggerFactory, loggerFactory,
options, options,
fileSystem, fileSystem,
imageEncoder, imageEncoder,
networkManager, networkManager)
configuration)
{ {
} }

View File

@ -112,10 +112,12 @@ namespace Jellyfin.Server
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); // Create an instance of the application configuration to use for application startup
await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
CreateLogger(appConfig, appPaths); IConfiguration startupConfig = CreateAppConfiguration(appPaths);
// Initialize logging framework
InitializeLoggingFramework(startupConfig, appPaths);
_logger = _loggerFactory.CreateLogger("Main"); _logger = _loggerFactory.CreateLogger("Main");
// Log uncaught exceptions to the logging instead of std error // Log uncaught exceptions to the logging instead of std error
@ -180,23 +182,22 @@ namespace Jellyfin.Server
options, options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths), new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
GetImageEncoder(appPaths), GetImageEncoder(appPaths),
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()), new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
appConfig);
try try
{ {
ServiceCollection serviceCollection = new ServiceCollection(); ServiceCollection serviceCollection = new ServiceCollection();
await appHost.InitAsync(serviceCollection).ConfigureAwait(false); await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
var host = CreateWebHostBuilder(appHost, serviceCollection).Build(); var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build();
// A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = host.Services; appHost.ServiceProvider = webHost.Services;
appHost.FindParts(); appHost.FindParts();
Migrations.MigrationRunner.Run(appHost, _loggerFactory); Migrations.MigrationRunner.Run(appHost, _loggerFactory);
try try
{ {
await host.StartAsync().ConfigureAwait(false); await webHost.StartAsync().ConfigureAwait(false);
} }
catch catch
{ {
@ -232,7 +233,7 @@ namespace Jellyfin.Server
} }
} }
private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection) private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths)
{ {
return new WebHostBuilder() return new WebHostBuilder()
.UseKestrel(options => .UseKestrel(options =>
@ -272,6 +273,7 @@ namespace Jellyfin.Server
} }
} }
}) })
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
.UseSerilog() .UseSerilog()
.UseContentRoot(appHost.ContentRoot) .UseContentRoot(appHost.ContentRoot)
.ConfigureServices(services => .ConfigureServices(services =>
@ -445,38 +447,51 @@ namespace Jellyfin.Server
return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir); return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir);
} }
private static async Task<IConfiguration> CreateConfiguration(IApplicationPaths appPaths) /// <summary>
/// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist
/// already.
/// </summary>
private static async Task InitLoggingConfigFile(IApplicationPaths appPaths)
{ {
const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; // Do nothing if the config file already exists
string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault);
if (File.Exists(configPath))
if (!File.Exists(configPath))
{ {
// For some reason the csproj name is used instead of the assembly name return;
await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath);
if (resource == null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Invalid resource path: '{0}'",
ResourcePath));
} }
// Get a stream of the resource contents
// NOTE: The .csproj name is used instead of the assembly name in the resource path
const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
// Copy the resource contents to the expected file path for the config file
await using Stream dst = File.Open(configPath, FileMode.CreateNew); await using Stream dst = File.Open(configPath, FileMode.CreateNew);
await resource.CopyToAsync(dst).ConfigureAwait(false); await resource.CopyToAsync(dst).ConfigureAwait(false);
} }
private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths)
{
return new ConfigurationBuilder() return new ConfigurationBuilder()
.ConfigureAppConfiguration(appPaths)
.Build();
}
private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths)
{
return config
.SetBasePath(appPaths.ConfigurationDirectoryPath) .SetBasePath(appPaths.ConfigurationDirectoryPath)
.AddInMemoryCollection(ConfigurationOptions.Configuration) .AddInMemoryCollection(ConfigurationOptions.Configuration)
.AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
.AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true) .AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
.AddEnvironmentVariables("JELLYFIN_") .AddEnvironmentVariables("JELLYFIN_");
.Build();
} }
private static void CreateLogger(IConfiguration configuration, IApplicationPaths appPaths) /// <summary>
/// Initialize Serilog using configuration and fall back to defaults on failure.
/// </summary>
private static void InitializeLoggingFramework(IConfiguration configuration, IApplicationPaths appPaths)
{ {
try try
{ {

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Updates; using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common namespace MediaBrowser.Common
@ -121,11 +122,12 @@ namespace MediaBrowser.Common
void RemovePlugin(IPlugin plugin); void RemovePlugin(IPlugin plugin);
/// <summary> /// <summary>
/// Inits this instance. /// Initializes this instance.
/// </summary> /// </summary>
/// <param name="serviceCollection">The service collection.</param> /// <param name="serviceCollection">The service collection.</param>
/// <param name="startupConfig">The configuration to use for initialization.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
Task InitAsync(IServiceCollection serviceCollection); Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig);
/// <summary> /// <summary>
/// Creates the instance. /// Creates the instance.

View File

@ -12,6 +12,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.1" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" /> <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
</ItemGroup> </ItemGroup>