using System.Threading.Tasks; using MediaBrowser.Common.Implementations.Udp; using MediaBrowser.Common.Implementations.Updates; using MediaBrowser.Common.Implementations.WebSocket; using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using SimpleInjector; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading; namespace MediaBrowser.Common.Implementations { public abstract class BaseApplicationHost { /// /// Gets or sets the logger. /// /// The logger. public ILogger Logger { get; protected set; } /// /// Gets or sets the plugins. /// /// The plugins. public IEnumerable Plugins { get; protected set; } /// /// Gets or sets the log manager. /// /// The log manager. public ILogManager LogManager { get; protected set; } /// /// Gets the application paths. /// /// The application paths. protected IApplicationPaths ApplicationPaths { get; private set; } /// /// The container /// protected readonly Container Container = new Container(); /// /// Gets assemblies that failed to load /// public List FailedAssemblies { get; protected set; } /// /// Gets all types within all running assemblies /// /// All types. public Type[] AllTypes { get; protected set; } /// /// Gets all concrete types. /// /// All concrete types. public Type[] AllConcreteTypes { get; protected set; } /// /// The disposable parts /// protected readonly List DisposableParts = new List(); /// /// Gets a value indicating whether this instance is first run. /// /// true if this instance is first run; otherwise, false. public bool IsFirstRun { get; private set; } /// /// 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 IProtobufSerializer _protobufSerializer; /// /// Gets the protobuf serializer. /// /// The protobuf serializer. protected IProtobufSerializer ProtobufSerializer { get { // Lazy load LazyInitializer.EnsureInitialized(ref _protobufSerializer, ref _protobufSerializerInitialized, ref _protobufSerializerSyncLock, () => Serialization.ProtobufSerializer.Create(AllTypes)); return _protobufSerializer; } private set { _protobufSerializer = value; _protobufSerializerInitialized = value != null; } } /// /// Initializes a new instance of the class. /// protected BaseApplicationHost() { FailedAssemblies = new List(); } /// /// Inits this instance. /// /// Task. public virtual Task Init() { return Task.Run(() => { ApplicationPaths = GetApplicationPaths(); LogManager = GetLogManager(); Logger = LogManager.GetLogger("App"); IsFirstRun = !File.Exists(ApplicationPaths.SystemConfigurationFilePath); DiscoverTypes(); }); } /// /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. protected abstract IEnumerable GetComposablePartAssemblies(); /// /// Gets the log manager. /// /// ILogManager. protected abstract ILogManager GetLogManager(); /// /// Gets the application paths. /// /// IApplicationPaths. protected abstract IApplicationPaths GetApplicationPaths(); /// /// Finds the parts. /// protected virtual void FindParts() { Resolve().Init(GetExports(false)); Resolve().AddWebSocketListeners(GetExports(false)); Resolve().Start(); Resolve().AddTasks(GetExports(false)); Plugins = GetExports(); } /// /// Discovers the types. /// protected void DiscoverTypes() { FailedAssemblies.Clear(); AllTypes = GetComposablePartAssemblies().SelectMany(GetTypes).ToArray(); AllConcreteTypes = AllTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); } /// /// Registers resources that classes will depend on /// protected virtual void RegisterResources(ITaskManager taskManager, INetworkManager networkManager, IServerManager serverManager) { RegisterSingleInstance(LogManager); RegisterSingleInstance(Logger); RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(taskManager); RegisterSingleInstance(() => new AlchemyServer(Logger)); RegisterSingleInstance(ProtobufSerializer); RegisterSingleInstance(new UdpServer(Logger), false); RegisterSingleInstance(new PackageManager()); RegisterSingleInstance(new HttpClientManager.HttpClientManager(ApplicationPaths, Logger)); RegisterSingleInstance(networkManager); RegisterSingleInstance(serverManager); } /// /// 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 protected 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); } } /// /// 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. /// if set to true [manage lifetime]. protected void RegisterSingleInstance(T obj, bool manageLifetime = true) where T : class { Container.RegisterSingle(obj); if (manageLifetime) { var disposable = obj as IDisposable; if (disposable != null) { Logger.Info("Registering " + disposable.GetType().Name); DisposableParts.Add(disposable); } } } /// /// Registers the single instance. /// /// /// The func. protected 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(); } /// /// Loads the assembly. /// /// The file. /// Assembly. 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; } } /// /// Gets the exports. /// /// /// 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; } /// /// Gets the current application version /// /// The application version. public Version ApplicationVersion { get { return GetType().Assembly.GetName().Version; } } /// /// Configures the auto run at startup. /// /// if set to true [autorun]. public void ConfigureAutoRunAtStartup(bool autorun) { } /// /// Removes the plugin. /// /// The plugin. public void RemovePlugin(IPlugin plugin) { var list = Plugins.ToList(); list.Remove(plugin); Plugins = list; } /// /// 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) { 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); part.Dispose(); } } } } }