2
0

BaseKernel.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. using MediaBrowser.Common.Events;
  2. using MediaBrowser.Common.IO;
  3. using MediaBrowser.Common.Net;
  4. using MediaBrowser.Common.Plugins;
  5. using MediaBrowser.Common.ScheduledTasks;
  6. using MediaBrowser.Common.Serialization;
  7. using MediaBrowser.Model.Configuration;
  8. using MediaBrowser.Model.Logging;
  9. using MediaBrowser.Model.System;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.ComponentModel.Composition;
  13. using System.ComponentModel.Composition.Hosting;
  14. using System.ComponentModel.Composition.Primitives;
  15. using System.Diagnostics;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Reflection;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. namespace MediaBrowser.Common.Kernel
  22. {
  23. /// <summary>
  24. /// Represents a shared base kernel for both the Ui and server apps
  25. /// </summary>
  26. /// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
  27. /// <typeparam name="TApplicationPathsType">The type of the T application paths type.</typeparam>
  28. public abstract class BaseKernel<TConfigurationType, TApplicationPathsType> : IDisposable, IKernel
  29. where TConfigurationType : BaseApplicationConfiguration, new()
  30. where TApplicationPathsType : BaseApplicationPaths, new()
  31. {
  32. /// <summary>
  33. /// Occurs when [has pending restart changed].
  34. /// </summary>
  35. public event EventHandler HasPendingRestartChanged;
  36. #region ConfigurationUpdated Event
  37. /// <summary>
  38. /// Occurs when [configuration updated].
  39. /// </summary>
  40. public event EventHandler<EventArgs> ConfigurationUpdated;
  41. /// <summary>
  42. /// Called when [configuration updated].
  43. /// </summary>
  44. internal void OnConfigurationUpdated()
  45. {
  46. EventHelper.QueueEventIfNotNull(ConfigurationUpdated, this, EventArgs.Empty, Logger);
  47. // Notify connected clients
  48. TcpManager.SendWebSocketMessage("ConfigurationUpdated", Configuration);
  49. }
  50. #endregion
  51. #region LoggerLoaded Event
  52. /// <summary>
  53. /// Fires whenever the logger is loaded
  54. /// </summary>
  55. public event EventHandler LoggerLoaded;
  56. /// <summary>
  57. /// Called when [logger loaded].
  58. /// </summary>
  59. private void OnLoggerLoaded()
  60. {
  61. EventHelper.QueueEventIfNotNull(LoggerLoaded, this, EventArgs.Empty, Logger);
  62. }
  63. #endregion
  64. #region ReloadBeginning Event
  65. /// <summary>
  66. /// Fires whenever the kernel begins reloading
  67. /// </summary>
  68. public event EventHandler<EventArgs> ReloadBeginning;
  69. /// <summary>
  70. /// Called when [reload beginning].
  71. /// </summary>
  72. private void OnReloadBeginning()
  73. {
  74. EventHelper.QueueEventIfNotNull(ReloadBeginning, this, EventArgs.Empty, Logger);
  75. }
  76. #endregion
  77. #region ReloadCompleted Event
  78. /// <summary>
  79. /// Fires whenever the kernel completes reloading
  80. /// </summary>
  81. public event EventHandler<EventArgs> ReloadCompleted;
  82. /// <summary>
  83. /// Called when [reload completed].
  84. /// </summary>
  85. private void OnReloadCompleted()
  86. {
  87. EventHelper.QueueEventIfNotNull(ReloadCompleted, this, EventArgs.Empty, Logger);
  88. }
  89. #endregion
  90. #region ApplicationUpdated Event
  91. /// <summary>
  92. /// Occurs when [application updated].
  93. /// </summary>
  94. public event EventHandler<GenericEventArgs<Version>> ApplicationUpdated;
  95. /// <summary>
  96. /// Called when [application updated].
  97. /// </summary>
  98. /// <param name="newVersion">The new version.</param>
  99. public void OnApplicationUpdated(Version newVersion)
  100. {
  101. EventHelper.QueueEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<Version> { Argument = newVersion }, Logger);
  102. NotifyPendingRestart();
  103. }
  104. #endregion
  105. /// <summary>
  106. /// The _configuration loaded
  107. /// </summary>
  108. private bool _configurationLoaded;
  109. /// <summary>
  110. /// The _configuration sync lock
  111. /// </summary>
  112. private object _configurationSyncLock = new object();
  113. /// <summary>
  114. /// The _configuration
  115. /// </summary>
  116. private TConfigurationType _configuration;
  117. /// <summary>
  118. /// Gets the system configuration
  119. /// </summary>
  120. /// <value>The configuration.</value>
  121. public TConfigurationType Configuration
  122. {
  123. get
  124. {
  125. // Lazy load
  126. LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => XmlSerializer.GetXmlConfiguration<TConfigurationType>(ApplicationPaths.SystemConfigurationFilePath, Logger));
  127. return _configuration;
  128. }
  129. protected set
  130. {
  131. _configuration = value;
  132. if (value == null)
  133. {
  134. _configurationLoaded = false;
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// Gets a value indicating whether this instance is first run.
  140. /// </summary>
  141. /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
  142. public bool IsFirstRun { get; private set; }
  143. /// <summary>
  144. /// Gets or sets a value indicating whether this instance has changes that require the entire application to restart.
  145. /// </summary>
  146. /// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value>
  147. public bool HasPendingRestart { get; private set; }
  148. /// <summary>
  149. /// Gets the application paths.
  150. /// </summary>
  151. /// <value>The application paths.</value>
  152. public TApplicationPathsType ApplicationPaths { get; private set; }
  153. /// <summary>
  154. /// The _failed assembly loads
  155. /// </summary>
  156. private readonly List<string> _failedPluginAssemblies = new List<string>();
  157. /// <summary>
  158. /// Gets the plugin assemblies that failed to load.
  159. /// </summary>
  160. /// <value>The failed assembly loads.</value>
  161. public IEnumerable<string> FailedPluginAssemblies
  162. {
  163. get { return _failedPluginAssemblies; }
  164. }
  165. /// <summary>
  166. /// Gets the list of currently loaded plugins
  167. /// </summary>
  168. /// <value>The plugins.</value>
  169. [ImportMany(typeof(IPlugin))]
  170. public IEnumerable<IPlugin> Plugins { get; protected set; }
  171. /// <summary>
  172. /// Gets the list of Scheduled Tasks
  173. /// </summary>
  174. /// <value>The scheduled tasks.</value>
  175. [ImportMany(typeof(IScheduledTask))]
  176. public IEnumerable<IScheduledTask> ScheduledTasks { get; private set; }
  177. /// <summary>
  178. /// Gets the web socket listeners.
  179. /// </summary>
  180. /// <value>The web socket listeners.</value>
  181. public IEnumerable<IWebSocketListener> WebSocketListeners { get; private set; }
  182. /// <summary>
  183. /// Gets the MEF CompositionContainer
  184. /// </summary>
  185. /// <value>The composition container.</value>
  186. private CompositionContainer CompositionContainer { get; set; }
  187. /// <summary>
  188. /// The _HTTP manager
  189. /// </summary>
  190. /// <value>The HTTP manager.</value>
  191. public HttpManager HttpManager { get; private set; }
  192. /// <summary>
  193. /// Gets or sets the TCP manager.
  194. /// </summary>
  195. /// <value>The TCP manager.</value>
  196. public TcpManager TcpManager { get; private set; }
  197. /// <summary>
  198. /// Gets the task manager.
  199. /// </summary>
  200. /// <value>The task manager.</value>
  201. public TaskManager TaskManager { get; private set; }
  202. /// <summary>
  203. /// Gets the rest services.
  204. /// </summary>
  205. /// <value>The rest services.</value>
  206. public IEnumerable<IRestfulService> RestServices { get; private set; }
  207. /// <summary>
  208. /// The disposable parts
  209. /// </summary>
  210. private readonly List<IDisposable> _disposableParts = new List<IDisposable>();
  211. /// <summary>
  212. /// The _protobuf serializer initialized
  213. /// </summary>
  214. private bool _protobufSerializerInitialized;
  215. /// <summary>
  216. /// The _protobuf serializer sync lock
  217. /// </summary>
  218. private object _protobufSerializerSyncLock = new object();
  219. /// <summary>
  220. /// Gets a dynamically compiled generated serializer that can serialize protocontracts without reflection
  221. /// </summary>
  222. private DynamicProtobufSerializer _protobufSerializer;
  223. /// <summary>
  224. /// Gets the protobuf serializer.
  225. /// </summary>
  226. /// <value>The protobuf serializer.</value>
  227. public DynamicProtobufSerializer ProtobufSerializer
  228. {
  229. get
  230. {
  231. // Lazy load
  232. LazyInitializer.EnsureInitialized(ref _protobufSerializer, ref _protobufSerializerInitialized, ref _protobufSerializerSyncLock, () => DynamicProtobufSerializer.Create(AllTypes));
  233. return _protobufSerializer;
  234. }
  235. private set
  236. {
  237. _protobufSerializer = value;
  238. if (value == null)
  239. {
  240. _protobufSerializerInitialized = false;
  241. }
  242. }
  243. }
  244. /// <summary>
  245. /// Gets the UDP server port number.
  246. /// This can't be configurable because then the user would have to configure their client to discover the server.
  247. /// </summary>
  248. /// <value>The UDP server port number.</value>
  249. public abstract int UdpServerPortNumber { get; }
  250. /// <summary>
  251. /// Gets the name of the web application that can be used for url building.
  252. /// All api urls will be of the form {protocol}://{host}:{port}/{appname}/...
  253. /// </summary>
  254. /// <value>The name of the web application.</value>
  255. public string WebApplicationName
  256. {
  257. get { return "mediabrowser"; }
  258. }
  259. /// <summary>
  260. /// Gets the HTTP server URL prefix.
  261. /// </summary>
  262. /// <value>The HTTP server URL prefix.</value>
  263. public virtual string HttpServerUrlPrefix
  264. {
  265. get
  266. {
  267. return "http://+:" + Configuration.HttpServerPortNumber + "/" + WebApplicationName + "/";
  268. }
  269. }
  270. /// <summary>
  271. /// Gets the kernel context. Subclasses will have to override.
  272. /// </summary>
  273. /// <value>The kernel context.</value>
  274. public abstract KernelContext KernelContext { get; }
  275. /// <summary>
  276. /// Gets the log file path.
  277. /// </summary>
  278. /// <value>The log file path.</value>
  279. public string LogFilePath
  280. {
  281. get { return ApplicationHost.LogFilePath; }
  282. }
  283. /// <summary>
  284. /// Gets the logger.
  285. /// </summary>
  286. /// <value>The logger.</value>
  287. protected ILogger Logger { get; private set; }
  288. /// <summary>
  289. /// Gets or sets the application host.
  290. /// </summary>
  291. /// <value>The application host.</value>
  292. protected IApplicationHost ApplicationHost { get; private set; }
  293. /// <summary>
  294. /// Gets the assemblies.
  295. /// </summary>
  296. /// <value>The assemblies.</value>
  297. public Assembly[] Assemblies { get; private set; }
  298. /// <summary>
  299. /// Gets all types.
  300. /// </summary>
  301. /// <value>All types.</value>
  302. public Type[] AllTypes { get; private set; }
  303. /// <summary>
  304. /// Initializes a new instance of the <see cref="BaseKernel{TApplicationPathsType}" /> class.
  305. /// </summary>
  306. /// <param name="appHost">The app host.</param>
  307. /// <param name="logger">The logger.</param>
  308. /// <exception cref="System.ArgumentNullException">isoManager</exception>
  309. protected BaseKernel(IApplicationHost appHost, ILogger logger)
  310. {
  311. if (appHost == null)
  312. {
  313. throw new ArgumentNullException("appHost");
  314. }
  315. if (logger == null)
  316. {
  317. throw new ArgumentNullException("logger");
  318. }
  319. ApplicationHost = appHost;
  320. Logger = logger;
  321. }
  322. /// <summary>
  323. /// Initializes the Kernel
  324. /// </summary>
  325. /// <returns>Task.</returns>
  326. public async Task Init()
  327. {
  328. ApplicationPaths = new TApplicationPathsType();
  329. IsFirstRun = !File.Exists(ApplicationPaths.SystemConfigurationFilePath);
  330. // Performs initializations that can be reloaded at anytime
  331. await Reload().ConfigureAwait(false);
  332. }
  333. /// <summary>
  334. /// Performs initializations that can be reloaded at anytime
  335. /// </summary>
  336. /// <returns>Task.</returns>
  337. public async Task Reload()
  338. {
  339. OnReloadBeginning();
  340. await ReloadInternal().ConfigureAwait(false);
  341. OnReloadCompleted();
  342. Logger.Info("Kernel.Reload Complete");
  343. }
  344. /// <summary>
  345. /// Performs initializations that can be reloaded at anytime
  346. /// </summary>
  347. /// <returns>Task.</returns>
  348. protected virtual async Task ReloadInternal()
  349. {
  350. // Set these to null so that they can be lazy loaded again
  351. Configuration = null;
  352. ProtobufSerializer = null;
  353. ReloadLogger();
  354. Logger.Info("Version {0} initializing", ApplicationVersion);
  355. DisposeHttpManager();
  356. HttpManager = new HttpManager(this, Logger);
  357. await OnConfigurationLoaded().ConfigureAwait(false);
  358. DisposeTaskManager();
  359. TaskManager = new TaskManager(this, Logger);
  360. Logger.Info("Loading Plugins");
  361. await ReloadComposableParts().ConfigureAwait(false);
  362. DisposeTcpManager();
  363. TcpManager = new TcpManager(ApplicationHost, this, Logger);
  364. }
  365. /// <summary>
  366. /// Called when [configuration loaded].
  367. /// </summary>
  368. /// <returns>Task.</returns>
  369. protected virtual Task OnConfigurationLoaded()
  370. {
  371. return Task.FromResult<object>(null);
  372. }
  373. /// <summary>
  374. /// Disposes and reloads all loggers
  375. /// </summary>
  376. public void ReloadLogger()
  377. {
  378. ApplicationHost.ReloadLogger();
  379. OnLoggerLoaded();
  380. }
  381. /// <summary>
  382. /// Uses MEF to locate plugins
  383. /// Subclasses can use this to locate types within plugins
  384. /// </summary>
  385. /// <returns>Task.</returns>
  386. private async Task ReloadComposableParts()
  387. {
  388. _failedPluginAssemblies.Clear();
  389. DisposeComposableParts();
  390. Assemblies = GetComposablePartAssemblies().ToArray();
  391. AllTypes = Assemblies.SelectMany(GetTypes).ToArray();
  392. ComposeParts(AllTypes);
  393. await OnComposablePartsLoaded().ConfigureAwait(false);
  394. CompositionContainer.Catalog.Dispose();
  395. }
  396. /// <summary>
  397. /// Composes the parts.
  398. /// </summary>
  399. /// <param name="allTypes">All types.</param>
  400. private void ComposeParts(IEnumerable<Type> allTypes)
  401. {
  402. var concreteTypes = allTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray();
  403. CompositionContainer = GetSafeCompositionContainer(concreteTypes.Select(i => new TypeCatalog(i)));
  404. RegisterExportedValues(CompositionContainer);
  405. CompositionContainer.ComposeParts(this);
  406. FindParts(concreteTypes);
  407. }
  408. /// <summary>
  409. /// Composes the parts with ioc container.
  410. /// </summary>
  411. /// <param name="allTypes">All types.</param>
  412. protected virtual void FindParts(Type[] allTypes)
  413. {
  414. RestServices = GetExports<IRestfulService>(allTypes);
  415. WebSocketListeners = GetExports<IWebSocketListener>(allTypes);
  416. }
  417. /// <summary>
  418. /// Gets the exports.
  419. /// </summary>
  420. /// <typeparam name="T"></typeparam>
  421. /// <param name="allTypes">All types.</param>
  422. /// <returns>IEnumerable{``0}.</returns>
  423. protected IEnumerable<T> GetExports<T>(Type[] allTypes)
  424. {
  425. var currentType = typeof(T);
  426. Logger.Info("Composing instances of " + currentType.Name);
  427. var parts = allTypes.Where(currentType.IsAssignableFrom).Select(Instantiate).Cast<T>().ToArray();
  428. _disposableParts.AddRange(parts.OfType<IDisposable>());
  429. return parts;
  430. }
  431. /// <summary>
  432. /// Instantiates the specified type.
  433. /// </summary>
  434. /// <param name="type">The type.</param>
  435. /// <returns>System.Object.</returns>
  436. private object Instantiate(Type type)
  437. {
  438. return ApplicationHost.CreateInstance(type);
  439. }
  440. /// <summary>
  441. /// Composes the exported values.
  442. /// </summary>
  443. /// <param name="container">The container.</param>
  444. protected virtual void RegisterExportedValues(CompositionContainer container)
  445. {
  446. ApplicationHost.Register<IKernel>(this);
  447. container.ComposeExportedValue("logger", Logger);
  448. container.ComposeExportedValue("appHost", ApplicationHost);
  449. container.ComposeExportedValue("isoManager", ApplicationHost.Resolve<IIsoManager>());
  450. }
  451. /// <summary>
  452. /// Gets the composable part assemblies.
  453. /// </summary>
  454. /// <returns>IEnumerable{Assembly}.</returns>
  455. protected virtual IEnumerable<Assembly> GetComposablePartAssemblies()
  456. {
  457. // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
  458. // This will prevent the .dll file from getting locked, and allow us to replace it when needed
  459. var pluginAssemblies = Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
  460. .Select(file =>
  461. {
  462. try
  463. {
  464. return Assembly.Load(File.ReadAllBytes((file)));
  465. }
  466. catch (Exception ex)
  467. {
  468. _failedPluginAssemblies.Add(file);
  469. Logger.ErrorException("Error loading {0}", ex, file);
  470. return null;
  471. }
  472. }).Where(a => a != null);
  473. foreach (var pluginAssembly in pluginAssemblies)
  474. {
  475. yield return pluginAssembly;
  476. }
  477. var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
  478. var corePluginDirectory = Path.Combine(runningDirectory, "CorePlugins");
  479. // This will prevent the .dll file from getting locked, and allow us to replace it when needed
  480. pluginAssemblies = Directory.EnumerateFiles(corePluginDirectory, "*.dll", SearchOption.TopDirectoryOnly)
  481. .Select(file =>
  482. {
  483. try
  484. {
  485. return Assembly.Load(File.ReadAllBytes((file)));
  486. }
  487. catch (Exception ex)
  488. {
  489. _failedPluginAssemblies.Add(file);
  490. Logger.ErrorException("Error loading {0}", ex, file);
  491. return null;
  492. }
  493. }).Where(a => a != null);
  494. foreach (var pluginAssembly in pluginAssemblies)
  495. {
  496. yield return pluginAssembly;
  497. }
  498. // Include composable parts in the Model assembly
  499. yield return typeof(SystemInfo).Assembly;
  500. // Include composable parts in the Common assembly
  501. yield return Assembly.GetExecutingAssembly();
  502. // Include composable parts in the subclass assembly
  503. yield return GetType().Assembly;
  504. }
  505. /// <summary>
  506. /// Plugins that live on both the server and UI are going to have references to assemblies from both sides.
  507. /// But looks for Parts on one side, it will throw an exception when it seems Types from the other side that it doesn't have a reference to.
  508. /// For example, a plugin provides a Resolver. When MEF runs in the UI, it will throw an exception when it sees the resolver because there won't be a reference to the base class.
  509. /// This method will catch those exceptions while retining the list of Types that MEF is able to resolve.
  510. /// </summary>
  511. /// <param name="catalogs">The catalogs.</param>
  512. /// <returns>CompositionContainer.</returns>
  513. /// <exception cref="System.ArgumentNullException">catalogs</exception>
  514. private static CompositionContainer GetSafeCompositionContainer(IEnumerable<ComposablePartCatalog> catalogs)
  515. {
  516. if (catalogs == null)
  517. {
  518. throw new ArgumentNullException("catalogs");
  519. }
  520. var newList = new List<ComposablePartCatalog>();
  521. // Go through each Catalog
  522. foreach (var catalog in catalogs)
  523. {
  524. try
  525. {
  526. // Try to have MEF find Parts
  527. catalog.Parts.ToArray();
  528. // If it succeeds we can use the entire catalog
  529. newList.Add(catalog);
  530. }
  531. catch (ReflectionTypeLoadException ex)
  532. {
  533. // If it fails we can still get a list of the Types it was able to resolve and create TypeCatalogs
  534. var typeCatalogs = ex.Types.Where(t => t != null).Select(t => new TypeCatalog(t));
  535. newList.AddRange(typeCatalogs);
  536. }
  537. }
  538. return new CompositionContainer(new AggregateCatalog(newList));
  539. }
  540. /// <summary>
  541. /// Gets a list of types within an assembly
  542. /// 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
  543. /// </summary>
  544. /// <param name="assembly">The assembly.</param>
  545. /// <returns>IEnumerable{Type}.</returns>
  546. /// <exception cref="System.ArgumentNullException">assembly</exception>
  547. private static IEnumerable<Type> GetTypes(Assembly assembly)
  548. {
  549. if (assembly == null)
  550. {
  551. throw new ArgumentNullException("assembly");
  552. }
  553. try
  554. {
  555. return assembly.GetTypes();
  556. }
  557. catch (ReflectionTypeLoadException ex)
  558. {
  559. // If it fails we can still get a list of the Types it was able to resolve
  560. return ex.Types.Where(t => t != null);
  561. }
  562. }
  563. /// <summary>
  564. /// Fires after MEF finishes finding composable parts within plugin assemblies
  565. /// </summary>
  566. /// <returns>Task.</returns>
  567. protected virtual Task OnComposablePartsLoaded()
  568. {
  569. return Task.Run(() =>
  570. {
  571. foreach (var task in ScheduledTasks)
  572. {
  573. task.Initialize(this, Logger);
  574. }
  575. // Start-up each plugin
  576. Parallel.ForEach(Plugins, plugin =>
  577. {
  578. Logger.Info("Initializing {0} {1}", plugin.Name, plugin.Version);
  579. try
  580. {
  581. plugin.Initialize(this, Logger);
  582. Logger.Info("{0} {1} initialized.", plugin.Name, plugin.Version);
  583. }
  584. catch (Exception ex)
  585. {
  586. Logger.ErrorException("Error initializing {0}", ex, plugin.Name);
  587. }
  588. });
  589. });
  590. }
  591. /// <summary>
  592. /// Notifies that the kernel that a change has been made that requires a restart
  593. /// </summary>
  594. public void NotifyPendingRestart()
  595. {
  596. HasPendingRestart = true;
  597. TcpManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
  598. EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
  599. }
  600. /// <summary>
  601. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  602. /// </summary>
  603. public void Dispose()
  604. {
  605. Dispose(true);
  606. GC.SuppressFinalize(this);
  607. }
  608. /// <summary>
  609. /// Releases unmanaged and - optionally - managed resources.
  610. /// </summary>
  611. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  612. protected virtual void Dispose(bool dispose)
  613. {
  614. if (dispose)
  615. {
  616. DisposeTcpManager();
  617. DisposeTaskManager();
  618. DisposeHttpManager();
  619. DisposeComposableParts();
  620. foreach (var part in _disposableParts)
  621. {
  622. part.Dispose();
  623. }
  624. _disposableParts.Clear();
  625. }
  626. }
  627. /// <summary>
  628. /// Disposes the TCP manager.
  629. /// </summary>
  630. private void DisposeTcpManager()
  631. {
  632. if (TcpManager != null)
  633. {
  634. TcpManager.Dispose();
  635. TcpManager = null;
  636. }
  637. }
  638. /// <summary>
  639. /// Disposes the task manager.
  640. /// </summary>
  641. private void DisposeTaskManager()
  642. {
  643. if (TaskManager != null)
  644. {
  645. TaskManager.Dispose();
  646. TaskManager = null;
  647. }
  648. }
  649. /// <summary>
  650. /// Disposes the HTTP manager.
  651. /// </summary>
  652. private void DisposeHttpManager()
  653. {
  654. if (HttpManager != null)
  655. {
  656. HttpManager.Dispose();
  657. HttpManager = null;
  658. }
  659. }
  660. /// <summary>
  661. /// Disposes all objects gathered through MEF composable parts
  662. /// </summary>
  663. protected virtual void DisposeComposableParts()
  664. {
  665. if (CompositionContainer != null)
  666. {
  667. CompositionContainer.Dispose();
  668. }
  669. }
  670. /// <summary>
  671. /// Gets the current application version
  672. /// </summary>
  673. /// <value>The application version.</value>
  674. public Version ApplicationVersion
  675. {
  676. get
  677. {
  678. return GetType().Assembly.GetName().Version;
  679. }
  680. }
  681. /// <summary>
  682. /// Performs the pending restart.
  683. /// </summary>
  684. /// <returns>Task.</returns>
  685. public void PerformPendingRestart()
  686. {
  687. if (HasPendingRestart)
  688. {
  689. RestartApplication();
  690. }
  691. else
  692. {
  693. Logger.Info("PerformPendingRestart - not needed");
  694. }
  695. }
  696. /// <summary>
  697. /// Restarts the application.
  698. /// </summary>
  699. protected void RestartApplication()
  700. {
  701. Logger.Info("Restarting the application");
  702. ApplicationHost.Restart();
  703. }
  704. /// <summary>
  705. /// Gets the system status.
  706. /// </summary>
  707. /// <returns>SystemInfo.</returns>
  708. public virtual SystemInfo GetSystemInfo()
  709. {
  710. return new SystemInfo
  711. {
  712. HasPendingRestart = HasPendingRestart,
  713. Version = ApplicationVersion.ToString(),
  714. IsNetworkDeployed = ApplicationHost.CanSelfUpdate,
  715. WebSocketPortNumber = TcpManager.WebSocketPortNumber,
  716. SupportsNativeWebSocket = TcpManager.SupportsNativeWebSocket,
  717. FailedPluginAssemblies = FailedPluginAssemblies.ToArray()
  718. };
  719. }
  720. /// <summary>
  721. /// The _save lock
  722. /// </summary>
  723. private readonly object _configurationSaveLock = new object();
  724. /// <summary>
  725. /// Saves the current configuration
  726. /// </summary>
  727. public void SaveConfiguration()
  728. {
  729. lock (_configurationSaveLock)
  730. {
  731. XmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath);
  732. }
  733. OnConfigurationUpdated();
  734. }
  735. /// <summary>
  736. /// Gets the application paths.
  737. /// </summary>
  738. /// <value>The application paths.</value>
  739. BaseApplicationPaths IKernel.ApplicationPaths
  740. {
  741. get { return ApplicationPaths; }
  742. }
  743. /// <summary>
  744. /// Gets the configuration.
  745. /// </summary>
  746. /// <value>The configuration.</value>
  747. BaseApplicationConfiguration IKernel.Configuration
  748. {
  749. get { return Configuration; }
  750. }
  751. }
  752. }