using BDInfo; using MediaBrowser.ClickOnce; using MediaBrowser.Common.Implementations.ScheduledTasks; using MediaBrowser.Common.Implementations.Serialization; using MediaBrowser.Common.IO; using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.IsoMounter; using MediaBrowser.Logging.Nlog; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Networking.HttpManager; using MediaBrowser.Networking.HttpServer; using MediaBrowser.Networking.Management; using MediaBrowser.Networking.Udp; using MediaBrowser.Networking.WebSocket; using MediaBrowser.Server.Implementations; using MediaBrowser.ServerApplication.Implementations; using SimpleInjector; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.ServerApplication { /// /// Class CompositionRoot /// public class ApplicationHost : IApplicationHost, IDisposable { /// /// Gets or sets the logger. /// /// The logger. private ILogger Logger { get; set; } /// /// Gets or sets the log file path. /// /// The log file path. public string LogFilePath { get; private set; } /// /// The container /// private readonly Container _container = new Container(); /// /// Gets or sets the kernel. /// /// The kernel. public Kernel Kernel { get; private set; } private readonly List _failedAssemblies = new List(); /// /// Gets assemblies that failed to load /// public IEnumerable FailedAssemblies { get { return _failedAssemblies; } } /// /// Gets all types within all running assemblies /// /// All types. public Type[] AllTypes { get; private set; } /// /// Gets all concrete types. /// /// All concrete types. public Type[] AllConcreteTypes { get; private set; } /// /// The disposable parts /// private readonly List _disposableParts = new List(); /// /// The json serializer /// private readonly IJsonSerializer _jsonSerializer = new JsonSerializer(); /// /// The _XML serializer /// private readonly IXmlSerializer _xmlSerializer = new XmlSerializer(); /// /// The _application paths /// private readonly IServerApplicationPaths _applicationPaths = new ServerApplicationPaths(); /// /// The _task manager /// private readonly ITaskManager _taskManager; /// /// Initializes a new instance of the class. /// /// The logger. public ApplicationHost(ILogger logger) { Logger = logger; _taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger); Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, _taskManager, Logger); ReloadLogger(); RegisterResources(); FindParts(); } /// /// Registers resources that classes will depend on /// internal void RegisterResources() { DiscoverTypes(); RegisterSingleInstance(Kernel); RegisterSingleInstance(Kernel); RegisterSingleInstance(this); RegisterSingleInstance(Logger); RegisterSingleInstance(_applicationPaths); RegisterSingleInstance(_applicationPaths); RegisterSingleInstance(_taskManager); RegisterSingleInstance(() => new PismoIsoManager(Logger)); RegisterSingleInstance(() => new BdInfoExaminer()); RegisterSingleInstance(() => new HttpManager(_applicationPaths, Logger)); RegisterSingleInstance(() => new NetworkManager()); RegisterSingleInstance(() => new DotNetZipClient()); RegisterSingleInstance(() => new AlchemyServer(Logger)); RegisterSingleInstance(_jsonSerializer); RegisterSingleInstance(_xmlSerializer); RegisterSingleInstance(() => ProtobufSerializer); Register(typeof(IUdpServer), typeof(UdpServer)); RegisterSingleInstance(() => ServerFactory.CreateServer(this, Kernel, ProtobufSerializer, Logger, "Media Browser", "index.html")); } /// /// Discovers the types. /// private void DiscoverTypes() { _failedAssemblies.Clear(); AllTypes = GetComposablePartAssemblies().SelectMany(GetTypes).ToArray(); AllConcreteTypes = AllTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); } /// /// Finds the parts. /// private void FindParts() { _taskManager.AddTasks(GetExports(false)); } /// /// 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 /// /// The assembly. /// IEnumerable{Type}. /// assembly private IEnumerable GetTypes(Assembly assembly) { if (assembly == null) { throw new ArgumentNullException("assembly"); } try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { // If it fails we can still get a list of the Types it was able to resolve return ex.Types.Where(t => t != null); } } /// /// The _protobuf serializer initialized /// private bool _protobufSerializerInitialized; /// /// The _protobuf serializer sync lock /// private object _protobufSerializerSyncLock = new object(); /// /// Gets a dynamically compiled generated serializer that can serialize protocontracts without reflection /// private ProtobufSerializer _protobufSerializer; /// /// Gets the protobuf serializer. /// /// The protobuf serializer. public ProtobufSerializer ProtobufSerializer { get { // Lazy load LazyInitializer.EnsureInitialized(ref _protobufSerializer, ref _protobufSerializerInitialized, ref _protobufSerializerSyncLock, () => ProtobufSerializer.Create(AllTypes)); return _protobufSerializer; } private set { _protobufSerializer = value; _protobufSerializerInitialized = value != null; } } /// /// Creates an instance of type and resolves all constructor dependancies /// /// The type. /// System.Object. public object CreateInstance(Type type) { try { return _container.GetInstance(type); } catch { Logger.Error("Error creating {0}", type.Name); throw; } } /// /// Registers the specified obj. /// /// /// The obj. public void RegisterSingleInstance(T obj) where T : class { _container.RegisterSingle(obj); } /// /// Registers the specified func. /// /// /// The func. public void Register(Func func) where T : class { _container.Register(func); } /// /// Registers the single instance. /// /// /// The func. public void RegisterSingleInstance(Func func) where T : class { _container.RegisterSingle(func); } /// /// Resolves this instance. /// /// /// ``0. public T Resolve() { return (T)_container.GetRegistration(typeof(T), true).GetInstance(); } /// /// Resolves this instance. /// /// /// ``0. public T TryResolve() { var result = _container.GetRegistration(typeof(T), false); if (result == null) { return default(T); } return (T)result.GetInstance(); } /// /// Registers the specified service type. /// /// Type of the service. /// Type of the concrete. public void Register(Type serviceType, Type implementation) { _container.Register(serviceType, implementation); } /// /// Restarts this instance. /// /// public void Restart() { App.Instance.Restart(); } /// /// Reloads the logger. /// /// public void ReloadLogger() { LogFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, "Server-" + DateTime.Now.Ticks + ".log"); NlogManager.AddFileTarget(LogFilePath, Kernel.Configuration.EnableDebugLevelLogging); } /// /// Gets or sets a value indicating whether this instance can self update. /// /// true if this instance can self update; otherwise, false. public bool CanSelfUpdate { get { return ClickOnceHelper.IsNetworkDeployed; } } /// /// Checks for update. /// /// The cancellation token. /// The progress. /// Task{CheckForUpdateResult}. public Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) { return new ApplicationUpdateCheck().CheckForApplicationUpdate(cancellationToken, progress); } /// /// Updates the application. /// /// The cancellation token. /// The progress. /// Task. public Task UpdateApplication(CancellationToken cancellationToken, IProgress progress) { return new ApplicationUpdater().UpdateApplication(cancellationToken, progress); } /// /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. private IEnumerable GetComposablePartAssemblies() { // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed foreach (var pluginAssembly in Directory .EnumerateFiles(Kernel.ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) .Select(LoadAssembly).Where(a => a != null)) { yield return pluginAssembly; } var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); var corePluginDirectory = Path.Combine(runningDirectory, "CorePlugins"); // This will prevent the .dll file from getting locked, and allow us to replace it when needed foreach (var pluginAssembly in Directory .EnumerateFiles(corePluginDirectory, "*.dll", SearchOption.TopDirectoryOnly) .Select(LoadAssembly).Where(a => a != null)) { yield return pluginAssembly; } // Include composable parts in the Model assembly yield return typeof(SystemInfo).Assembly; // Include composable parts in the Common assembly yield return typeof(IKernel).Assembly; // Include composable parts in the Controller assembly yield return typeof(Kernel).Assembly; // Common implementations yield return typeof(TaskManager).Assembly; // Server implementations yield return typeof(ServerApplicationPaths).Assembly; // Include composable parts in the running assembly yield return GetType().Assembly; } /// /// Loads the assembly. /// /// The file. /// Assembly. private 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; } } /// /// Gets the exports. /// /// /// All types. /// if set to true [manage liftime]. /// IEnumerable{``0}. public IEnumerable GetExports(bool manageLiftime = true) { var currentType = typeof(T); Logger.Info("Composing instances of " + currentType.Name); var parts = AllConcreteTypes.Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast().ToArray(); if (manageLiftime) { _disposableParts.AddRange(parts.OfType()); } return parts; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { foreach (var part in _disposableParts) { part.Dispose(); } _disposableParts.Clear(); } } }