BaseKernel.cs 30 KB

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