diff options
Diffstat (limited to 'Emby.Server.Implementations/ApplicationHost.cs')
| -rw-r--r-- | Emby.Server.Implementations/ApplicationHost.cs | 785 |
1 files changed, 750 insertions, 35 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 69a190fcb..318f7d912 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,11 +1,5 @@ using Emby.Common.Implementations; -using Emby.Common.Implementations.Archiving; -using Emby.Common.Implementations.IO; -using Emby.Common.Implementations.Reflection; -using Emby.Common.Implementations.ScheduledTasks; using Emby.Common.Implementations.Serialization; -using Emby.Common.Implementations.TextEncoding; -using Emby.Common.Implementations.Xml; using Emby.Dlna; using Emby.Dlna.ConnectionManager; using Emby.Dlna.ContentDirectory; @@ -110,12 +104,28 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net; using System.Reflection; +using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.Archiving; +using Emby.Server.Implementations.Cryptography; +using Emby.Server.Implementations.Diagnostics; +using Emby.Server.Implementations.Net; +using Emby.Server.Implementations.Reflection; +using Emby.Server.Implementations.ScheduledTasks; +using Emby.Server.Implementations.Serialization; +using Emby.Server.Implementations.Threading; +using Emby.Server.Implementations.Xml; using Emby.Server.MediaEncoding.Subtitles; using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Threading; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; namespace Emby.Server.Implementations @@ -123,9 +133,125 @@ namespace Emby.Server.Implementations /// <summary> /// Class CompositionRoot /// </summary> - public abstract class ApplicationHost : BaseApplicationHost<ServerApplicationPaths>, IServerApplicationHost, IDependencyContainer + public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer { /// <summary> + /// Gets a value indicating whether this instance can self restart. + /// </summary> + /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> + public abstract bool CanSelfRestart { get; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can self update. + /// </summary> + /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value> + public virtual bool CanSelfUpdate + { + get + { + return false; + } + } + + /// <summary> + /// Occurs when [has pending restart changed]. + /// </summary> + public event EventHandler HasPendingRestartChanged; + + /// <summary> + /// Occurs when [application updated]. + /// </summary> + public event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated; + + /// <summary> + /// Gets or sets a value indicating whether this instance has changes that require the entire application to restart. + /// </summary> + /// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value> + public bool HasPendingRestart { get; private set; } + + /// <summary> + /// Gets or sets the logger. + /// </summary> + /// <value>The logger.</value> + protected ILogger Logger { get; set; } + + /// <summary> + /// Gets or sets the plugins. + /// </summary> + /// <value>The plugins.</value> + public IPlugin[] Plugins { get; protected set; } + + /// <summary> + /// Gets or sets the log manager. + /// </summary> + /// <value>The log manager.</value> + public ILogManager LogManager { get; protected set; } + + /// <summary> + /// Gets the application paths. + /// </summary> + /// <value>The application paths.</value> + protected ServerApplicationPaths ApplicationPaths { get; set; } + + /// <summary> + /// Gets assemblies that failed to load + /// </summary> + /// <value>The failed assemblies.</value> + public List<string> FailedAssemblies { get; protected set; } + + /// <summary> + /// Gets all concrete types. + /// </summary> + /// <value>All concrete types.</value> + public Type[] AllConcreteTypes { get; protected set; } + + /// <summary> + /// The disposable parts + /// </summary> + protected readonly List<IDisposable> DisposableParts = new List<IDisposable>(); + + /// <summary> + /// Gets a value indicating whether this instance is first run. + /// </summary> + /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value> + public bool IsFirstRun { get; private set; } + + /// <summary> + /// Gets the configuration manager. + /// </summary> + /// <value>The configuration manager.</value> + protected IConfigurationManager ConfigurationManager { get; set; } + + public IFileSystem FileSystemManager { get; set; } + + protected IEnvironmentInfo EnvironmentInfo { get; set; } + + public PackageVersionClass SystemUpdateLevel + { + get + { + +#if BETA + return PackageVersionClass.Beta; +#endif + return PackageVersionClass.Release; + } + } + + public virtual string OperatingSystemDisplayName + { + get { return EnvironmentInfo.OperatingSystemName; } + } + + /// <summary> + /// The container + /// </summary> + protected readonly SimpleInjector.Container Container = new SimpleInjector.Container(); + + protected ISystemEvents SystemEvents { get; set; } + protected IMemoryStreamFactory MemoryStreamFactory { get; set; } + + /// <summary> /// Gets the server configuration manager. /// </summary> /// <value>The server configuration manager.</value> @@ -138,7 +264,7 @@ namespace Emby.Server.Implementations /// Gets the configuration manager. /// </summary> /// <returns>IConfigurationManager.</returns> - protected override IConfigurationManager GetConfigurationManager() + protected IConfigurationManager GetConfigurationManager() { return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager); } @@ -244,6 +370,17 @@ namespace Emby.Server.Implementations private readonly Action<string, string, string> _certificateGenerator; private readonly Func<string> _defaultUserNameFactory; + protected IProcessFactory ProcessFactory { get; private set; } + protected ITimerFactory TimerFactory { get; private set; } + protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); + protected readonly IXmlSerializer XmlSerializer; + + protected ISocketFactory SocketFactory { get; private set; } + protected ITaskManager TaskManager { get; private set; } + public IHttpClient HttpClient { get; private set; } + protected INetworkManager NetworkManager { get; set; } + public IJsonSerializer JsonSerializer { get; private set; } + protected IIsoManager IsoManager { get; private set; } /// <summary> /// Initializes a new instance of the <see cref="ApplicationHost" /> class. @@ -261,14 +398,28 @@ namespace Emby.Server.Implementations INetworkManager networkManager, Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) - : base(applicationPaths, - logManager, - fileSystem, - environmentInfo, - systemEvents, - memoryStreamFactory, - networkManager) { + // hack alert, until common can target .net core + BaseExtensions.CryptographyProvider = CryptographyProvider; + + XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer")); + + NetworkManager = networkManager; + EnvironmentInfo = environmentInfo; + SystemEvents = systemEvents; + MemoryStreamFactory = memoryStreamFactory; + + FailedAssemblies = new List<string>(); + + ApplicationPaths = applicationPaths; + LogManager = logManager; + FileSystemManager = fileSystem; + + ConfigurationManager = GetConfigurationManager(); + + // Initialize this early in case the -v command line option is used + Logger = LogManager.GetLogger("App"); + StartupOptions = options; _certificateGenerator = certificateGenerator; _releaseAssetFilename = releaseAssetFilename; @@ -292,7 +443,7 @@ namespace Emby.Server.Implementations /// Gets the current application version /// </summary> /// <value>The application version.</value> - public override Version ApplicationVersion + public Version ApplicationVersion { get { @@ -308,11 +459,25 @@ namespace Emby.Server.Implementations } } + private DeviceId _deviceId; + public string SystemId + { + get + { + if (_deviceId == null) + { + _deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager); + } + + return _deviceId.Value; + } + } + /// <summary> /// Gets the name. /// </summary> /// <value>The name.</value> - public override string Name + public string Name { get { @@ -341,6 +506,159 @@ namespace Emby.Server.Implementations } } + /// <summary> + /// Creates an instance of type and resolves all constructor dependancies + /// </summary> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + public object CreateInstance(Type type) + { + try + { + return Container.GetInstance(type); + } + catch (Exception ex) + { + Logger.ErrorException("Error creating {0}", ex, type.FullName); + + throw; + } + } + + /// <summary> + /// Creates the instance safe. + /// </summary> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + protected object CreateInstanceSafe(Type type) + { + try + { + return Container.GetInstance(type); + } + catch (Exception ex) + { + Logger.ErrorException("Error creating {0}", ex, type.FullName); + // Don't blow up in release mode + return null; + } + } + + /// <summary> + /// Registers the specified obj. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="obj">The obj.</param> + /// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param> + protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true) + where T : class + { + Container.RegisterSingleton(obj); + + if (manageLifetime) + { + var disposable = obj as IDisposable; + + if (disposable != null) + { + DisposableParts.Add(disposable); + } + } + } + + /// <summary> + /// Registers the single instance. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="func">The func.</param> + protected void RegisterSingleInstance<T>(Func<T> func) + where T : class + { + Container.RegisterSingleton(func); + } + + /// <summary> + /// Resolves this instance. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>``0.</returns> + public T Resolve<T>() + { + return (T)Container.GetRegistration(typeof(T), true).GetInstance(); + } + + /// <summary> + /// Resolves this instance. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>``0.</returns> + public T TryResolve<T>() + { + var result = Container.GetRegistration(typeof(T), false); + + if (result == null) + { + return default(T); + } + return (T)result.GetInstance(); + } + + /// <summary> + /// Loads the assembly. + /// </summary> + /// <param name="file">The file.</param> + /// <returns>Assembly.</returns> + protected Assembly LoadAssembly(string file) + { + try + { + return Assembly.Load(File.ReadAllBytes(file)); + } + catch (Exception ex) + { + FailedAssemblies.Add(file); + Logger.ErrorException("Error loading assembly {0}", ex, file); + return null; + } + } + + /// <summary> + /// Gets the export types. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>IEnumerable{Type}.</returns> + public IEnumerable<Type> GetExportTypes<T>() + { + var currentType = typeof(T); + + return AllConcreteTypes.Where(currentType.IsAssignableFrom); + } + + /// <summary> + /// Gets the exports. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param> + /// <returns>IEnumerable{``0}.</returns> + public IEnumerable<T> GetExports<T>(bool manageLiftime = true) + { + var parts = GetExportTypes<T>() + .Select(CreateInstanceSafe) + .Where(i => i != null) + .Cast<T>() + .ToList(); + + if (manageLiftime) + { + lock (DisposableParts) + { + DisposableParts.AddRange(parts.OfType<IDisposable>()); + } + } + + return parts; + } + private void SetBaseExceptionMessage() { var builder = GetBaseExceptionMessage(ApplicationPaths); @@ -354,9 +672,13 @@ namespace Emby.Server.Implementations /// <summary> /// Runs the startup tasks. /// </summary> - public override async Task RunStartupTasks() + public async Task RunStartupTasks() { - await base.RunStartupTasks().ConfigureAwait(false); + Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false)); + + ConfigureAutorun(); + + ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; await MediaEncoder.Init().ConfigureAwait(false); @@ -395,7 +717,22 @@ namespace Emby.Server.Implementations LogManager.RemoveConsoleOutput(); } - protected override IJsonSerializer CreateJsonSerializer() + /// <summary> + /// Configures the autorun. + /// </summary> + private void ConfigureAutorun() + { + try + { + ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup); + } + catch (Exception ex) + { + Logger.ErrorException("Error configuring autorun", ex); + } + } + + private IJsonSerializer CreateJsonSerializer() { try { @@ -410,7 +747,7 @@ namespace Emby.Server.Implementations return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer")); } - public override Task Init(IProgress<double> progress) + public async Task Init(IProgress<double> progress) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -422,7 +759,64 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - return base.Init(progress); + progress.Report(1); + + JsonSerializer = CreateJsonSerializer(); + + OnLoggerLoaded(true); + LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false); + + IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted; + progress.Report(2); + + LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging + ? LogSeverity.Debug + : LogSeverity.Info; + + progress.Report(3); + + DiscoverTypes(); + progress.Report(14); + + SetHttpLimit(); + progress.Report(15); + + var innerProgress = new ActionableProgress<double>(); + innerProgress.RegisterAction(p => progress.Report(.8 * p + 15)); + + await RegisterResources(innerProgress).ConfigureAwait(false); + + FindParts(); + progress.Report(95); + + await InstallIsoMounters(CancellationToken.None).ConfigureAwait(false); + + progress.Report(100); + } + + protected virtual void OnLoggerLoaded(bool isFirstLoad) + { + Logger.Info("Application version: {0}", ApplicationVersion); + + if (!isFirstLoad) + { + LogEnvironmentInfo(Logger, ApplicationPaths, false); + } + + // Put the app config in the log for troubleshooting purposes + Logger.LogMultiline("Application configuration:", LogSeverity.Info, new StringBuilder(JsonSerializer.SerializeToString(ConfigurationManager.CommonConfiguration))); + + if (Plugins != null) + { + var pluginBuilder = new StringBuilder(); + + foreach (var plugin in Plugins) + { + pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version)); + } + + Logger.LogMultiline("Plugins:", LogSeverity.Info, pluginBuilder); + } } protected abstract IConnectManager CreateConnectManager(); @@ -431,9 +825,47 @@ namespace Emby.Server.Implementations /// <summary> /// Registers resources that classes will depend on /// </summary> - protected override async Task RegisterResources(IProgress<double> progress) + protected async Task RegisterResources(IProgress<double> progress) { - await base.RegisterResources(progress).ConfigureAwait(false); + RegisterSingleInstance(ConfigurationManager); + RegisterSingleInstance<IApplicationHost>(this); + + RegisterSingleInstance<IApplicationPaths>(ApplicationPaths); + + RegisterSingleInstance(JsonSerializer); + RegisterSingleInstance(MemoryStreamFactory); + RegisterSingleInstance(SystemEvents); + + RegisterSingleInstance(LogManager); + RegisterSingleInstance(Logger); + + RegisterSingleInstance(EnvironmentInfo); + + RegisterSingleInstance(FileSystemManager); + + HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent); + RegisterSingleInstance(HttpClient); + + RegisterSingleInstance(NetworkManager); + + IsoManager = new IsoManager(); + RegisterSingleInstance(IsoManager); + + TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager, SystemEvents); + RegisterSingleInstance(TaskManager); + + RegisterSingleInstance(XmlSerializer); + + ProcessFactory = new ProcessFactory(); + RegisterSingleInstance(ProcessFactory); + + TimerFactory = new TimerFactory(); + RegisterSingleInstance(TimerFactory); + + RegisterSingleInstance(CryptographyProvider); + + SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory")); + RegisterSingleInstance(SocketFactory); RegisterSingleInstance(PowerManagement); @@ -460,7 +892,7 @@ namespace Emby.Server.Implementations StringExtensions.LocalizationManager = LocalizationManager; RegisterSingleInstance(LocalizationManager); - ITextEncoding textEncoding = new TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer); + ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer); RegisterSingleInstance(textEncoding); Utilities.EncodingHelper = textEncoding; RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding)); @@ -627,6 +1059,106 @@ namespace Emby.Server.Implementations await ((UserManager)UserManager).Initialize().ConfigureAwait(false); } + public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, bool isStartup) + { + logger.LogMultiline("Emby", LogSeverity.Info, GetBaseExceptionMessage(appPaths)); + } + + protected static StringBuilder GetBaseExceptionMessage(IApplicationPaths appPaths) + { + var builder = new StringBuilder(); + + builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs()))); + + builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion)); + builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem)); + builder.AppendLine(string.Format("64-Bit Process: {0}", Environment.Is64BitProcess)); + + Type type = Type.GetType("Mono.Runtime"); + if (type != null) + { + MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if (displayName != null) + { + builder.AppendLine("Mono: " + displayName.Invoke(null, null)); + } + } + + builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount)); + builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath)); + builder.AppendLine(string.Format("Application directory: {0}", appPaths.ProgramSystemPath)); + + return builder; + } + + private void SetHttpLimit() + { + try + { + // Increase the max http request limit + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); + } + catch (Exception ex) + { + Logger.ErrorException("Error setting http limit", ex); + } + } + + /// <summary> + /// Installs the iso mounters. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task InstallIsoMounters(CancellationToken cancellationToken) + { + var list = new List<IIsoMounter>(); + + foreach (var isoMounter in GetExports<IIsoMounter>()) + { + try + { + if (isoMounter.RequiresInstallation && !isoMounter.IsInstalled) + { + Logger.Info("Installing {0}", isoMounter.Name); + + await isoMounter.Install(cancellationToken).ConfigureAwait(false); + } + + list.Add(isoMounter); + } + catch (Exception ex) + { + Logger.ErrorException("{0} failed to load.", ex, isoMounter.Name); + } + } + + IsoManager.AddParts(list); + } + + private string GetDefaultUserAgent() + { + var name = FormatAttribute(Name); + + return name + "/" + ApplicationVersion; + } + + private string FormatAttribute(string str) + { + var arr = str.ToCharArray(); + + arr = Array.FindAll<char>(arr, (c => (char.IsLetterOrDigit(c) + || char.IsWhiteSpace(c)))); + + var result = new string(arr); + + if (string.IsNullOrWhiteSpace(result)) + { + result = "Emby"; + } + + return result; + } + protected virtual bool SupportsDualModeSockets { get @@ -889,7 +1421,7 @@ namespace Emby.Server.Implementations /// <summary> /// Finds the parts. /// </summary> - protected override void FindParts() + protected void FindParts() { if (!ServerConfigurationManager.Configuration.IsPortAuthorized) { @@ -900,7 +1432,8 @@ namespace Emby.Server.Implementations RegisterModules(); - base.FindParts(); + ConfigurationManager.AddParts(GetExports<IConfigurationFactory>()); + Plugins = GetExports<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray(); HttpServer.Init(GetExports<IService>(false)); @@ -937,6 +1470,104 @@ namespace Emby.Server.Implementations SyncManager.AddParts(GetExports<ISyncProvider>()); } + private IPlugin LoadPlugin(IPlugin plugin) + { + try + { + var assemblyPlugin = plugin as IPluginAssembly; + + if (assemblyPlugin != null) + { + var assembly = plugin.GetType().Assembly; + var assemblyName = assembly.GetName(); + + var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0]; + var assemblyId = new Guid(attribute.Value); + + var assemblyFileName = assemblyName.Name + ".dll"; + var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName); + + assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId); + } + + var isFirstRun = !File.Exists(plugin.ConfigurationFilePath); + plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s)); + } + catch (Exception ex) + { + Logger.ErrorException("Error loading plugin {0}", ex, plugin.GetType().FullName); + return null; + } + + return plugin; + } + + /// <summary> + /// Discovers the types. + /// </summary> + protected void DiscoverTypes() + { + FailedAssemblies.Clear(); + + var assemblies = GetComposablePartAssemblies().ToList(); + + foreach (var assembly in assemblies) + { + Logger.Info("Loading {0}", assembly.FullName); + } + + AllConcreteTypes = assemblies + .SelectMany(GetTypes) + .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType) + .ToArray(); + } + + /// <summary> + /// Gets a list of types within an assembly + /// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference + /// </summary> + /// <param name="assembly">The assembly.</param> + /// <returns>IEnumerable{Type}.</returns> + /// <exception cref="System.ArgumentNullException">assembly</exception> + protected List<Type> GetTypes(Assembly assembly) + { + if (assembly == null) + { + return new List<Type>(); + } + + try + { + // This null checking really shouldn't be needed but adding it due to some + // unhandled exceptions in mono 5.0 that are a little hard to hunt down + var types = assembly.GetTypes() ?? new Type[] { }; + return types.Where(t => t != null).ToList(); + } + catch (ReflectionTypeLoadException ex) + { + if (ex.LoaderExceptions != null) + { + foreach (var loaderException in ex.LoaderExceptions) + { + if (loaderException != null) + { + Logger.Error("LoaderException: " + loaderException.Message); + } + } + } + + // If it fails we can still get a list of the Types it was able to resolve + var types = ex.Types ?? new Type[] { }; + return types.Where(t => t != null).ToList(); + } + catch (Exception ex) + { + Logger.ErrorException("Error loading types from assembly", ex); + + return new List<Type>(); + } + } + private CertificateInfo CertificateInfo { get; set; } private ICertificate Certificate { get; set; } @@ -1043,9 +1674,9 @@ namespace Emby.Server.Implementations /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> - protected override void OnConfigurationUpdated(object sender, EventArgs e) + protected void OnConfigurationUpdated(object sender, EventArgs e) { - base.OnConfigurationUpdated(sender, e); + ConfigureAutorun(); var requiresRestart = false; @@ -1089,9 +1720,26 @@ namespace Emby.Server.Implementations } /// <summary> + /// Notifies that the kernel that a change has been made that requires a restart + /// </summary> + public void NotifyPendingRestart() + { + Logger.Info("App needs to be restarted."); + + var changed = !HasPendingRestart; + + HasPendingRestart = true; + + if (changed) + { + EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger); + } + } + + /// <summary> /// Restarts this instance. /// </summary> - public override async Task Restart() + public async Task Restart() { if (!CanSelfRestart) { @@ -1118,7 +1766,7 @@ namespace Emby.Server.Implementations /// Gets the composable part assemblies. /// </summary> /// <returns>IEnumerable{Assembly}.</returns> - protected override IEnumerable<Assembly> GetComposablePartAssemblies() + protected IEnumerable<Assembly> GetComposablePartAssemblies() { var list = GetPluginAssemblies() .ToList(); @@ -1442,7 +2090,7 @@ namespace Emby.Server.Implementations /// <summary> /// Shuts down. /// </summary> - public override async Task Shutdown() + public async Task Shutdown() { try { @@ -1504,12 +2152,23 @@ namespace Emby.Server.Implementations } /// <summary> + /// Removes the plugin. + /// </summary> + /// <param name="plugin">The plugin.</param> + public void RemovePlugin(IPlugin plugin) + { + var list = Plugins.ToList(); + list.Remove(plugin); + Plugins = list.ToArray(); + } + + /// <summary> /// Checks for update. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress.</param> /// <returns>Task{CheckForUpdateResult}.</returns> - public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress) + public async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress) { var cacheLength = TimeSpan.FromHours(1); var updateLevel = SystemUpdateLevel; @@ -1533,7 +2192,7 @@ namespace Emby.Server.Implementations /// <param name="package">The package that contains the update</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress.</param> - public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress) + public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress) { await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false); @@ -1546,7 +2205,7 @@ namespace Emby.Server.Implementations /// Configures the automatic run at startup. /// </summary> /// <param name="autorun">if set to <c>true</c> [autorun].</param> - protected override void ConfigureAutoRunAtStartup(bool autorun) + protected void ConfigureAutoRunAtStartup(bool autorun) { if (SupportsAutoRunAtStartup) { @@ -1641,6 +2300,62 @@ namespace Emby.Server.Implementations } } + /// <summary> + /// Called when [application updated]. + /// </summary> + /// <param name="package">The package.</param> + protected void OnApplicationUpdated(PackageVersionInfo package) + { + Logger.Info("Application has been updated to version {0}", package.versionStr); + + EventHelper.FireEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<PackageVersionInfo> + { + Argument = package + + }, Logger); + + NotifyPendingRestart(); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + var type = GetType(); + + Logger.Info("Disposing " + type.Name); + + var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList(); + DisposableParts.Clear(); + + foreach (var part in parts) + { + Logger.Info("Disposing " + part.GetType().Name); + + try + { + part.Dispose(); + } + catch (Exception ex) + { + Logger.ErrorException("Error disposing {0}", ex, part.GetType().Name); + } + } + } + } + void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime) { RegisterSingleInstance(obj, manageLifetime); |
