Browse Source

added a shutdown api method, font size fix and other decouplings

LukePulverenti 12 years ago
parent
commit
6efd22a3d2
30 changed files with 420 additions and 235 deletions
  1. 31 1
      MediaBrowser.Api/SystemService.cs
  2. 7 6
      MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs
  3. 19 24
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  4. 3 3
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  5. 11 3
      MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  6. 10 2
      MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
  7. 0 0
      MediaBrowser.Common.Implementations/Server/RegisterServer.bat
  8. 27 18
      MediaBrowser.Common.Implementations/Server/ServerManager.cs
  9. 5 34
      MediaBrowser.Common.Implementations/Server/WebSocketConnection.cs
  10. 6 48
      MediaBrowser.Common/Kernel/BaseKernel.cs
  11. 6 6
      MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs
  12. 5 0
      MediaBrowser.Common/Kernel/IApplicationHost.cs
  13. 2 9
      MediaBrowser.Common/Kernel/IKernel.cs
  14. 54 0
      MediaBrowser.Common/Kernel/IServerManager.cs
  15. 2 3
      MediaBrowser.Common/MediaBrowser.Common.csproj
  16. 85 0
      MediaBrowser.Common/Net/IWebSocketConnection.cs
  17. 6 0
      MediaBrowser.Common/Net/IWebSocketServer.cs
  18. 2 29
      MediaBrowser.Controller/Kernel.cs
  19. 48 4
      MediaBrowser.Controller/Library/LibraryManager.cs
  20. 2 2
      MediaBrowser.Controller/Library/UserManager.cs
  21. 5 5
      MediaBrowser.Controller/Updates/InstallationManager.cs
  22. 38 2
      MediaBrowser.Networking/Udp/UdpServer.cs
  23. 8 0
      MediaBrowser.Networking/WebSocket/AlchemyServer.cs
  24. 1 2
      MediaBrowser.ServerApplication/App.xaml.cs
  25. 31 24
      MediaBrowser.ServerApplication/ApplicationHost.cs
  26. 0 4
      MediaBrowser.WebDashboard/Html/css/site.css
  27. 1 1
      Nuget/MediaBrowser.ApiClient.nuspec
  28. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  29. 1 1
      Nuget/MediaBrowser.Common.nuspec
  30. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 31 - 1
MediaBrowser.Api/SystemService.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Kernel;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Serialization;
@@ -29,6 +30,11 @@ namespace MediaBrowser.Api
     {
     }
 
+    [Route("/System/Shutdown", "POST")]
+    public class ShutdownApplication
+    {
+    }
+    
     /// <summary>
     /// Class GetConfiguration
     /// </summary>
@@ -61,19 +67,30 @@ namespace MediaBrowser.Api
         /// </summary>
         private readonly IJsonSerializer _jsonSerializer;
 
+        /// <summary>
+        /// The _app host
+        /// </summary>
+        private readonly IApplicationHost _appHost;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="SystemService" /> class.
         /// </summary>
         /// <param name="jsonSerializer">The json serializer.</param>
+        /// <param name="appHost">The app host.</param>
         /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
-        public SystemService(IJsonSerializer jsonSerializer)
+        public SystemService(IJsonSerializer jsonSerializer, IApplicationHost appHost)
             : base()
         {
             if (jsonSerializer == null)
             {
                 throw new ArgumentNullException("jsonSerializer");
             }
+            if (appHost == null)
+            {
+                throw new ArgumentNullException("appHost");
+            }
 
+            _appHost = appHost;
             _jsonSerializer = jsonSerializer;
         }
 
@@ -118,6 +135,19 @@ namespace MediaBrowser.Api
             });
         }
 
+        /// <summary>
+        /// Posts the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        public void Post(ShutdownApplication request)
+        {
+            Task.Run(async () =>
+            {
+                await Task.Delay(100);
+                _appHost.Shutdown();
+            });
+        }
+
         /// <summary>
         /// Posts the specified configuraiton.
         /// </summary>

+ 7 - 6
MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Kernel;
-using MediaBrowser.Controller;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -27,18 +26,20 @@ namespace MediaBrowser.Api.WebSocket
         /// <summary>
         /// The _kernel
         /// </summary>
+        private readonly IApplicationHost _appHost;
         private readonly IKernel _kernel;
-        
+ 
         /// <summary>
         /// Initializes a new instance of the <see cref="LogFileWebSocketListener" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
         /// <param name="kernel">The kernel.</param>
-        public LogFileWebSocketListener(ILogger logger, Kernel kernel)
+        public LogFileWebSocketListener(ILogger logger, IApplicationHost host, IKernel kernel)
             : base(logger)
         {
+            _appHost = host;
             _kernel = kernel;
-            _kernel.LoggerLoaded += kernel_LoggerLoaded;
+            kernel.LoggerLoaded += kernel_LoggerLoaded;
         }
 
         /// <summary>
@@ -48,9 +49,9 @@ namespace MediaBrowser.Api.WebSocket
         /// <returns>IEnumerable{System.String}.</returns>
         protected override async Task<IEnumerable<string>> GetDataToSend(LogFileWebSocketState state)
         {
-            if (!string.Equals(_kernel.LogFilePath, state.LastLogFilePath))
+            if (!string.Equals(_appHost.LogFilePath, state.LastLogFilePath))
             {
-                state.LastLogFilePath = _kernel.LogFilePath;
+                state.LastLogFilePath = _appHost.LogFilePath;
                 state.StartLine = 0;
             }
 

+ 19 - 24
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -151,21 +151,22 @@ namespace MediaBrowser.Common.Implementations
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="obj">The obj.</param>
-        protected void RegisterSingleInstance<T>(T obj)
+        /// <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.RegisterSingle(obj);
-        }
 
-        /// <summary>
-        /// Registers the specified func.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="func">The func.</param>
-        protected void Register<T>(Func<T> func)
-            where T : class
-        {
-            Container.Register(func);
+            if (manageLifetime)
+            {
+                var disposable = obj as IDisposable;
+
+                if (disposable != null)
+                {
+                    Logger.Info("Registering " + disposable.GetType().Name);
+                    DisposableParts.Add(disposable);
+                }
+            }
         }
 
         /// <summary>
@@ -205,16 +206,6 @@ namespace MediaBrowser.Common.Implementations
             return (T)result.GetInstance();
         }
 
-        /// <summary>
-        /// Registers the specified service type.
-        /// </summary>
-        /// <param name="serviceType">Type of the service.</param>
-        /// <param name="implementation">Type of the concrete.</param>
-        protected void Register(Type serviceType, Type implementation)
-        {
-            Container.Register(serviceType, implementation);
-        }
-
         /// <summary>
         /// Loads the assembly.
         /// </summary>
@@ -282,13 +273,17 @@ namespace MediaBrowser.Common.Implementations
         /// <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)
         {
-            foreach (var part in DisposableParts)
+            var type = GetType();
+
+            Logger.Info("Disposing " + type.Name);
+
+            foreach (var part in DisposableParts.Distinct().Where(i => i.GetType() != type).ToList())
             {
+                Logger.Info("Disposing " + part.GetType().Name);
+
                 part.Dispose();
             }
 
-            var b = Container.GetCurrentRegistrations();
-
             DisposableParts.Clear();
         }
     }

+ 3 - 3
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -47,10 +47,7 @@
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
@@ -69,6 +66,8 @@
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\ProtobufSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
+    <Compile Include="Server\ServerManager.cs" />
+    <Compile Include="Server\WebSocketConnection.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -82,6 +81,7 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
+    <EmbeddedResource Include="Server\RegisterServer.bat" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(SolutionDir)\.nuget\nuget.targets" />

+ 11 - 3
MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -48,6 +48,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <value>The task manager.</value>
         private ITaskManager TaskManager { get; set; }
 
+        /// <summary>
+        /// Gets or sets the server manager.
+        /// </summary>
+        /// <value>The server manager.</value>
+        private IServerManager ServerManager { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
         /// </summary>
@@ -56,13 +62,15 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <param name="taskManager">The task manager.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
-        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
+        /// <param name="serverManager">The server manager.</param>
+        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IServerManager serverManager)
         {
             ScheduledTask = scheduledTask;
             ApplicationPaths = applicationPaths;
             TaskManager = taskManager;
             JsonSerializer = jsonSerializer;
             Logger = logger;
+            ServerManager = serverManager;
         }
 
         /// <summary>
@@ -302,7 +310,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             TaskCompletionStatus status;
             CurrentExecutionStartTime = DateTime.UtcNow;
 
-            //Kernel.TcpManager.SendWebSocketMessage("ScheduledTaskBeginExecute", Name);
+            ServerManager.SendWebSocketMessage("ScheduledTaskBeginExecute", Name);
 
             try
             {
@@ -324,7 +332,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             var startTime = CurrentExecutionStartTime;
             var endTime = DateTime.UtcNow;
 
-            //Kernel.TcpManager.SendWebSocketMessage("ScheduledTaskEndExecute", LastExecutionResult);
+            ServerManager.SendWebSocketMessage("ScheduledTaskEndExecute", LastExecutionResult);
 
             progress.ProgressChanged -= progress_ProgressChanged;
             CurrentCancellationTokenSource.Dispose();

+ 10 - 2
MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs

@@ -43,18 +43,26 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <value>The logger.</value>
         private ILogger Logger { get; set; }
 
+        /// <summary>
+        /// Gets or sets the server manager.
+        /// </summary>
+        /// <value>The server manager.</value>
+        private IServerManager ServerManager { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="TaskManager" /> class.
         /// </summary>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
+        /// <param name="serverManager">The server manager.</param>
         /// <exception cref="System.ArgumentException">kernel</exception>
-        public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger)
+        public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IServerManager serverManager)
         {
             ApplicationPaths = applicationPaths;
             JsonSerializer = jsonSerializer;
             Logger = logger;
+            ServerManager = serverManager;
 
             ScheduledTasks = new IScheduledTaskWorker[] { };
         }
@@ -155,7 +163,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         {
             var myTasks = ScheduledTasks.ToList();
 
-            myTasks.AddRange(tasks.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger)));
+            myTasks.AddRange(tasks.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, ServerManager)));
 
             ScheduledTasks = myTasks.ToArray();
         }

+ 0 - 0
MediaBrowser.Common/Kernel/RegisterServer.bat → MediaBrowser.Common.Implementations/Server/RegisterServer.bat


+ 27 - 18
MediaBrowser.Common/Kernel/TcpManager.cs → MediaBrowser.Common.Implementations/Server/ServerManager.cs

@@ -1,5 +1,5 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Common.Kernel;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
@@ -14,12 +14,12 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Common.Kernel
+namespace MediaBrowser.Common.Implementations.Server
 {
     /// <summary>
     /// Manages the Http Server, Udp Server and WebSocket connections
     /// </summary>
-    public class TcpManager : IDisposable
+    public class ServerManager : IServerManager, IDisposable
     {
         /// <summary>
         /// This is the udp server used for server discovery by clients
@@ -49,7 +49,7 @@ namespace MediaBrowser.Common.Kernel
         /// <summary>
         /// The web socket connections
         /// </summary>
-        private readonly List<WebSocketConnection> _webSocketConnections = new List<WebSocketConnection>();
+        private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
 
         /// <summary>
         /// Gets or sets the external web socket server.
@@ -81,7 +81,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets a value indicating whether [supports web socket].
         /// </summary>
         /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
-        internal bool SupportsNativeWebSocket
+        public bool SupportsNativeWebSocket
         {
             get { return HttpServer != null && HttpServer.SupportsWebSockets; }
         }
@@ -96,7 +96,7 @@ namespace MediaBrowser.Common.Kernel
         }
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="TcpManager" /> class.
+        /// Initializes a new instance of the <see cref="ServerManager" /> class.
         /// </summary>
         /// <param name="applicationHost">The application host.</param>
         /// <param name="kernel">The kernel.</param>
@@ -104,7 +104,7 @@ namespace MediaBrowser.Common.Kernel
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
         /// <exception cref="System.ArgumentNullException">applicationHost</exception>
-        public TcpManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger)
+        public ServerManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger)
         {
             if (applicationHost == null)
             {
@@ -132,8 +132,14 @@ namespace MediaBrowser.Common.Kernel
             _kernel = kernel;
             _applicationHost = applicationHost;
             _networkManager = networkManager;
+        }
 
-            if (applicationHost.IsFirstRun)
+        /// <summary>
+        /// Starts this instance.
+        /// </summary>
+        public void Start()
+        {
+            if (_applicationHost.IsFirstRun)
             {
                 RegisterServerWithAdministratorAccess();
             }
@@ -145,6 +151,8 @@ namespace MediaBrowser.Common.Kernel
             {
                 ReloadExternalWebSocketServer();
             }
+
+            _kernel.ConfigurationUpdated += _kernel_ConfigurationUpdated;
         }
 
         /// <summary>
@@ -170,7 +178,7 @@ namespace MediaBrowser.Common.Kernel
         /// Restarts the Http Server, or starts it if not currently running
         /// </summary>
         /// <param name="registerServerOnFailure">if set to <c>true</c> [register server on failure].</param>
-        public void ReloadHttpServer(bool registerServerOnFailure = true)
+        private void ReloadHttpServer(bool registerServerOnFailure = true)
         {
             // Only reload if the port has changed, so that we don't disconnect any active users
             if (HttpServer != null && HttpServer.UrlPrefix.Equals(_kernel.HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
@@ -426,7 +434,7 @@ namespace MediaBrowser.Common.Kernel
             var tmpFile = Path.Combine(_kernel.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat");
 
             // Extract the bat file
-            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Common.Kernel.RegisterServer.bat"))
+            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Common.Implementations.Server.bat"))
             {
                 using (var fileStream = File.Create(tmpFile))
                 {
@@ -489,20 +497,21 @@ namespace MediaBrowser.Common.Kernel
         }
 
         /// <summary>
-        /// Called when [application configuration changed].
+        /// Handles the ConfigurationUpdated event of the _kernel control.
         /// </summary>
-        /// <param name="oldConfig">The old config.</param>
-        /// <param name="newConfig">The new config.</param>
-        public void OnApplicationConfigurationChanged(BaseApplicationConfiguration oldConfig, BaseApplicationConfiguration newConfig)
+        /// <param name="sender">The source of the event.</param>
+        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
+        /// <exception cref="System.NotImplementedException"></exception>
+        void _kernel_ConfigurationUpdated(object sender, EventArgs e)
         {
-            HttpServer.EnableHttpRequestLogging = newConfig.EnableHttpLevelLogging;
+            HttpServer.EnableHttpRequestLogging = _kernel.Configuration.EnableHttpLevelLogging;
 
-            if (oldConfig.HttpServerPortNumber != newConfig.HttpServerPortNumber)
+            if (!string.Equals(HttpServer.UrlPrefix, _kernel.HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
             {
                 ReloadHttpServer();
             }
 
-            if (!SupportsNativeWebSocket && oldConfig.LegacyWebSocketPortNumber != newConfig.LegacyWebSocketPortNumber)
+            if (!SupportsNativeWebSocket && ExternalWebSocketServer != null && ExternalWebSocketServer.Port != _kernel.Configuration.LegacyWebSocketPortNumber)
             {
                 ReloadExternalWebSocketServer();
             }

+ 5 - 34
MediaBrowser.Common/Net/WebSocketConnection.cs → MediaBrowser.Common.Implementations/Server/WebSocketConnection.cs

@@ -1,16 +1,17 @@
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Common.Implementations.Server
 {
     /// <summary>
     /// Class WebSocketConnection
     /// </summary>
-    public class WebSocketConnection : IDisposable
+    public class WebSocketConnection : IWebSocketConnection
     {
         /// <summary>
         /// The _socket
@@ -20,7 +21,7 @@ namespace MediaBrowser.Common.Net
         /// <summary>
         /// The _remote end point
         /// </summary>
-        public readonly string RemoteEndPoint;
+        public string RemoteEndPoint { get; private set; }
 
         /// <summary>
         /// The _cancellation token source
@@ -221,34 +222,4 @@ namespace MediaBrowser.Common.Net
             }
         }
     }
-
-    /// <summary>
-    /// Class WebSocketMessage
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    public class WebSocketMessage<T>
-    {
-        /// <summary>
-        /// Gets or sets the type of the message.
-        /// </summary>
-        /// <value>The type of the message.</value>
-        public string MessageType { get; set; }
-        /// <summary>
-        /// Gets or sets the data.
-        /// </summary>
-        /// <value>The data.</value>
-        public T Data { get; set; }
-    }
-
-    /// <summary>
-    /// Class WebSocketMessageInfo
-    /// </summary>
-    public class WebSocketMessageInfo : WebSocketMessage<string>
-    {
-        /// <summary>
-        /// Gets or sets the connection.
-        /// </summary>
-        /// <value>The connection.</value>
-        public WebSocketConnection Connection { get; set; }
-    }
 }

+ 6 - 48
MediaBrowser.Common/Kernel/BaseKernel.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Logging;
@@ -165,7 +164,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets or sets the TCP manager.
         /// </summary>
         /// <value>The TCP manager.</value>
-        public TcpManager TcpManager { get; private set; }
+        public IServerManager ServerManager { get; private set; }
 
         /// <summary>
         /// Gets the UDP server port number.
@@ -202,15 +201,6 @@ namespace MediaBrowser.Common.Kernel
         /// <value>The kernel context.</value>
         public abstract KernelContext KernelContext { get; }
 
-        /// <summary>
-        /// Gets the log file path.
-        /// </summary>
-        /// <value>The log file path.</value>
-        public string LogFilePath
-        {
-            get { return ApplicationHost.LogFilePath; }
-        }
-
         /// <summary>
         /// Gets the logger.
         /// </summary>
@@ -238,23 +228,6 @@ namespace MediaBrowser.Common.Kernel
         /// <exception cref="System.ArgumentNullException">isoManager</exception>
         protected BaseKernel(IApplicationHost appHost, TApplicationPathsType appPaths, IXmlSerializer xmlSerializer, ILogger logger)
         {
-            if (appHost == null)
-            {
-                throw new ArgumentNullException("appHost");
-            }
-            if (appPaths == null)
-            {
-                throw new ArgumentNullException("appPaths");
-            }
-            if (xmlSerializer == null)
-            {
-                throw new ArgumentNullException("xmlSerializer");
-            }
-            if (logger == null)
-            {
-                throw new ArgumentNullException("logger");
-            }
-
             ApplicationPaths = appPaths;
             ApplicationHost = appHost;
             _xmlSerializer = xmlSerializer;
@@ -291,8 +264,8 @@ namespace MediaBrowser.Common.Kernel
 
             await OnComposablePartsLoaded().ConfigureAwait(false);
 
-            DisposeTcpManager();
-            TcpManager = (TcpManager)ApplicationHost.CreateInstance(typeof(TcpManager));
+            ServerManager = ApplicationHost.Resolve<IServerManager>();
+            ServerManager.Start();
         }
 
         /// <summary>
@@ -357,7 +330,7 @@ namespace MediaBrowser.Common.Kernel
         {
             HasPendingRestart = true;
 
-            TcpManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
+            ServerManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
 
             EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
         }
@@ -377,22 +350,7 @@ namespace MediaBrowser.Common.Kernel
         /// <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)
-            {
-                DisposeTcpManager();
-            }
-        }
 
-        /// <summary>
-        /// Disposes the TCP manager.
-        /// </summary>
-        private void DisposeTcpManager()
-        {
-            if (TcpManager != null)
-            {
-                TcpManager.Dispose();
-                TcpManager = null;
-            }
         }
 
         /// <summary>
@@ -424,8 +382,8 @@ namespace MediaBrowser.Common.Kernel
                 HasPendingRestart = HasPendingRestart,
                 Version = ApplicationHost.ApplicationVersion.ToString(),
                 IsNetworkDeployed = ApplicationHost.CanSelfUpdate,
-                WebSocketPortNumber = TcpManager.WebSocketPortNumber,
-                SupportsNativeWebSocket = TcpManager.SupportsNativeWebSocket,
+                WebSocketPortNumber = ServerManager.WebSocketPortNumber,
+                SupportsNativeWebSocket = ServerManager.SupportsNativeWebSocket,
                 FailedPluginAssemblies = ApplicationHost.FailedAssemblies.ToArray()
             };
         }

+ 6 - 6
MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs

@@ -19,8 +19,8 @@ namespace MediaBrowser.Common.Kernel
         /// <summary>
         /// The _active connections
         /// </summary>
-        protected readonly List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
-            new List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
+        protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
+            new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
 
         /// <summary>
         /// Gets the name.
@@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Kernel
 
             lock (ActiveConnections)
             {
-                ActiveConnections.Add(new Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
+                ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
             }
 
             timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
@@ -115,9 +115,9 @@ namespace MediaBrowser.Common.Kernel
         /// <param name="state">The state.</param>
         private async void TimerCallback(object state)
         {
-            var connection = (WebSocketConnection)state;
+            var connection = (IWebSocketConnection)state;
 
-            Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
+            Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
 
             lock (ActiveConnections)
             {
@@ -187,7 +187,7 @@ namespace MediaBrowser.Common.Kernel
         /// Disposes the connection.
         /// </summary>
         /// <param name="connection">The connection.</param>
-        private void DisposeConnection(Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
+        private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
         {
             Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
 

+ 5 - 0
MediaBrowser.Common/Kernel/IApplicationHost.cs

@@ -97,5 +97,10 @@ namespace MediaBrowser.Common.Kernel
         /// <typeparam name="T"></typeparam>
         /// <returns>``0.</returns>
         T TryResolve<T>();
+
+        /// <summary>
+        /// Shuts down.
+        /// </summary>
+        void Shutdown();
     }
 }

+ 2 - 9
MediaBrowser.Common/Kernel/IKernel.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Plugins;
+using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.System;
 using System;
@@ -71,12 +70,6 @@ namespace MediaBrowser.Common.Kernel
         /// <value>The name of the web application.</value>
         string WebApplicationName { get; }
 
-        /// <summary>
-        /// Gets the log file path.
-        /// </summary>
-        /// <value>The log file path.</value>
-        string LogFilePath { get; }
-
         /// <summary>
         /// Performs the pending restart.
         /// </summary>
@@ -104,7 +97,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets the TCP manager.
         /// </summary>
         /// <value>The TCP manager.</value>
-        TcpManager TcpManager { get; }
+        IServerManager ServerManager { get; }
 
         /// <summary>
         /// Gets the web socket listeners.

+ 54 - 0
MediaBrowser.Common/Kernel/IServerManager.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Kernel
+{
+    public interface IServerManager : IDisposable
+    {
+        /// <summary>
+        /// Gets a value indicating whether [supports web socket].
+        /// </summary>
+        /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
+        bool SupportsNativeWebSocket { get; }
+
+        /// <summary>
+        /// Gets the web socket port number.
+        /// </summary>
+        /// <value>The web socket port number.</value>
+        int WebSocketPortNumber { get; }
+
+        /// <summary>
+        /// Starts this instance.
+        /// </summary>
+        void Start();
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="data">The data.</param>
+        /// <returns>Task.</returns>
+        void SendWebSocketMessage<T>(string messageType, T data);
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+        void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction);
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">messageType</exception>
+        Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken);
+    }
+}

+ 2 - 3
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -91,10 +91,10 @@
     <Compile Include="IO\StreamDefaults.cs" />
     <Compile Include="Kernel\BasePeriodicWebSocketListener.cs" />
     <Compile Include="Kernel\IApplicationPaths.cs" />
+    <Compile Include="Kernel\IServerManager.cs" />
     <Compile Include="Kernel\IWebSocketListener.cs" />
     <Compile Include="Kernel\IApplicationHost.cs" />
     <Compile Include="Kernel\IKernel.cs" />
-    <Compile Include="Kernel\TcpManager.cs" />
     <Compile Include="Net\Handlers\IHttpServerHandler.cs" />
     <Compile Include="Net\Handlers\StaticFileHandler.cs" />
     <Compile Include="Net\IHttpClient.cs" />
@@ -103,12 +103,12 @@
     <Compile Include="Net\IRestfulService.cs" />
     <Compile Include="Net\IUdpServer.cs" />
     <Compile Include="Net\IWebSocket.cs" />
+    <Compile Include="Net\IWebSocketConnection.cs" />
     <Compile Include="Net\IWebSocketServer.cs" />
     <Compile Include="Net\MimeTypes.cs" />
     <Compile Include="Net\StreamWriter.cs" />
     <Compile Include="Net\UdpMessageReceivedEventArgs.cs" />
     <Compile Include="Net\WebSocketConnectEventArgs.cs" />
-    <Compile Include="Net\WebSocketConnection.cs" />
     <Compile Include="Net\WebSocketMessageType.cs" />
     <Compile Include="Net\WebSocketState.cs" />
     <Compile Include="Plugins\BaseUiPlugin.cs" />
@@ -138,7 +138,6 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
-    <EmbeddedResource Include="Kernel\RegisterServer.bat" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>

+ 85 - 0
MediaBrowser.Common/Net/IWebSocketConnection.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Net
+{
+    public interface IWebSocketConnection : IDisposable
+    {
+        /// <summary>
+        /// Gets or sets the receive action.
+        /// </summary>
+        /// <value>The receive action.</value>
+        Action<WebSocketMessageInfo> OnReceive { get; set; }
+
+        /// <summary>
+        /// Gets the state.
+        /// </summary>
+        /// <value>The state.</value>
+        WebSocketState State { get; }
+
+        /// <summary>
+        /// Gets the remote end point.
+        /// </summary>
+        /// <value>The remote end point.</value>
+        string RemoteEndPoint { get; }
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="message">The message.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">message</exception>
+        Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <param name="buffer">The buffer.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <param name="buffer">The buffer.</param>
+        /// <param name="type">The type.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">buffer</exception>
+        Task SendAsync(byte[] buffer, WebSocketMessageType type, CancellationToken cancellationToken);
+    }
+
+    /// <summary>
+    /// Class WebSocketMessage
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class WebSocketMessage<T>
+    {
+        /// <summary>
+        /// Gets or sets the type of the message.
+        /// </summary>
+        /// <value>The type of the message.</value>
+        public string MessageType { get; set; }
+        /// <summary>
+        /// Gets or sets the data.
+        /// </summary>
+        /// <value>The data.</value>
+        public T Data { get; set; }
+    }
+
+    /// <summary>
+    /// Class WebSocketMessageInfo
+    /// </summary>
+    public class WebSocketMessageInfo : WebSocketMessage<string>
+    {
+        /// <summary>
+        /// Gets or sets the connection.
+        /// </summary>
+        /// <value>The connection.</value>
+        public IWebSocketConnection Connection { get; set; }
+    }
+}

+ 6 - 0
MediaBrowser.Common/Net/IWebSocketServer.cs

@@ -22,5 +22,11 @@ namespace MediaBrowser.Common.Net
         /// Occurs when [web socket connected].
         /// </summary>
         event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
+
+        /// <summary>
+        /// Gets the port.
+        /// </summary>
+        /// <value>The port.</value>
+        int Port { get; }
     }
 }

+ 2 - 29
MediaBrowser.Controller/Kernel.cs

@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Playback;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Controller.ScheduledTasks;
 using MediaBrowser.Controller.Updates;
 using MediaBrowser.Controller.Weather;
 using MediaBrowser.Model.Configuration;
@@ -293,8 +292,6 @@ namespace MediaBrowser.Controller
             get { return 7359; }
         }
 
-        private readonly ITaskManager _taskManager;
-
         /// <summary>
         /// Creates a kernel based on a Data path, which is akin to our current programdata path
         /// </summary>
@@ -304,13 +301,11 @@ namespace MediaBrowser.Controller
         /// <param name="taskManager">The task manager.</param>
         /// <param name="logger">The logger.</param>
         /// <exception cref="System.ArgumentNullException">isoManager</exception>
-        public Kernel(IApplicationHost appHost, IServerApplicationPaths appPaths, IXmlSerializer xmlSerializer, ITaskManager taskManager, ILogger logger)
+        public Kernel(IApplicationHost appHost, IServerApplicationPaths appPaths, IXmlSerializer xmlSerializer, ILogger logger)
             : base(appHost, appPaths, xmlSerializer, logger)
         {
             Instance = this;
 
-            _taskManager = taskManager;
-
             // For now there's no real way to inject this properly
             BaseItem.Logger = logger;
             Ratings.Logger = logger;
@@ -469,7 +464,7 @@ namespace MediaBrowser.Controller
         {
             DisposeFileSystemManager();
 
-            FileSystemManager = new FileSystemManager(this, Logger, _taskManager);
+            FileSystemManager = new FileSystemManager(this, Logger, ApplicationHost.Resolve<ITaskManager>());
             FileSystemManager.StartWatchers();
         }
 
@@ -540,18 +535,6 @@ namespace MediaBrowser.Controller
 
             var reloadLogger = config.ShowLogWindow != oldConfiguration.ShowLogWindow;
 
-            // Figure out whether or not we should refresh people after the update is finished
-            var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders;
-
-            // This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes
-            if (!refreshPeopleAfterUpdate)
-            {
-                var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
-                var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
-
-                refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages;
-            }
-
             Configuration = config;
             SaveConfiguration();
 
@@ -560,20 +543,10 @@ namespace MediaBrowser.Controller
                 ReloadLogger();
             }
 
-            TcpManager.OnApplicationConfigurationChanged(oldConfiguration, config);
-
             // Validate currently executing providers, in the background
             Task.Run(() =>
             {
                 ProviderManager.ValidateCurrentlyRunningProviders();
-
-                // Any number of configuration settings could change the way the library is refreshed, so do that now
-                _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
-
-                if (refreshPeopleAfterUpdate)
-                {
-                    _taskManager.CancelIfRunningAndQueue<PeopleValidationTask>();
-                }
             });
         }
 

+ 48 - 4
MediaBrowser.Controller/Library/LibraryManager.cs

@@ -1,8 +1,10 @@
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Controller.ScheduledTasks;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MoreLinq;
@@ -20,7 +22,7 @@ namespace MediaBrowser.Controller.Library
     /// <summary>
     /// Class LibraryManager
     /// </summary>
-    public class LibraryManager : BaseManager<Kernel>
+    public class LibraryManager
     {
         #region LibraryChanged Event
         /// <summary>
@@ -48,7 +50,7 @@ namespace MediaBrowser.Controller.Library
         private void SendLibraryChangedWebSocketMessage(ChildrenChangedEventArgs args)
         {
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args));
+            Kernel.ServerManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args));
         }
         #endregion
 
@@ -57,15 +59,57 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         private readonly ILogger _logger;
 
+        /// <summary>
+        /// The _task manager
+        /// </summary>
+        private readonly ITaskManager _taskManager;
+
+        /// <summary>
+        /// Gets or sets the kernel.
+        /// </summary>
+        /// <value>The kernel.</value>
+        private Kernel Kernel { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// </summary>
         /// <param name="kernel">The kernel.</param>
         /// <param name="logger">The logger.</param>
-        public LibraryManager(Kernel kernel, ILogger logger)
-            : base(kernel)
+        /// <param name="taskManager">The task manager.</param>
+        public LibraryManager(Kernel kernel, ILogger logger, ITaskManager taskManager)
         {
+            Kernel = kernel;
             _logger = logger;
+            _taskManager = taskManager;
+
+            kernel.ConfigurationUpdated += kernel_ConfigurationUpdated;
+        }
+
+        /// <summary>
+        /// Handles the ConfigurationUpdated event of the kernel control.
+        /// </summary>
+        /// <param name="sender">The source of the event.</param>
+        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
+        void kernel_ConfigurationUpdated(object sender, EventArgs e)
+        {
+            //// Figure out whether or not we should refresh people after the update is finished
+            //var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders;
+
+            //// This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes
+            //if (!refreshPeopleAfterUpdate)
+            //{
+            //    var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
+            //    var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
+
+            //    refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages;
+            //}
+
+            Task.Run(() =>
+            {
+                // Any number of configuration settings could change the way the library is refreshed, so do that now
+                _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
+                _taskManager.CancelIfRunningAndQueue<PeopleValidationTask>();
+            });
         }
 
         /// <summary>

+ 2 - 2
MediaBrowser.Controller/Library/UserManager.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Library
             EventHelper.QueueEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("UserUpdated", new DtoBuilder(_logger).GetDtoUser(user));
+            Kernel.ServerManager.SendWebSocketMessage("UserUpdated", new DtoBuilder(_logger).GetDtoUser(user));
         }
         #endregion
 
@@ -91,7 +91,7 @@ namespace MediaBrowser.Controller.Library
             EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("UserDeleted", user.Id.ToString());
+            Kernel.ServerManager.SendWebSocketMessage("UserDeleted", user.Id.ToString());
         }
         #endregion
 

+ 5 - 5
MediaBrowser.Controller/Updates/InstallationManager.cs

@@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Updates
             EventHelper.QueueEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("PluginUninstalled", plugin.GetPluginInfo());
+            Kernel.ServerManager.SendWebSocketMessage("PluginUninstalled", plugin.GetPluginInfo());
         }
         #endregion
 
@@ -371,7 +371,7 @@ namespace MediaBrowser.Controller.Updates
 
             var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
 
-            Kernel.TcpManager.SendWebSocketMessage("PackageInstalling", installationInfo);
+            Kernel.ServerManager.SendWebSocketMessage("PackageInstalling", installationInfo);
 
             try
             {
@@ -384,7 +384,7 @@ namespace MediaBrowser.Controller.Updates
 
                 CompletedInstallations.Add(installationInfo);
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationCompleted", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationCompleted", installationInfo);
             }
             catch (OperationCanceledException)
             {
@@ -395,7 +395,7 @@ namespace MediaBrowser.Controller.Updates
 
                 _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr);
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationCancelled", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationCancelled", installationInfo);
 
                 throw;
             }
@@ -406,7 +406,7 @@ namespace MediaBrowser.Controller.Updates
                     CurrentInstallations.Remove(tuple);
                 }
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationFailed", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationFailed", installationInfo);
 
                 throw;
             }

+ 38 - 2
MediaBrowser.Networking/Udp/UdpServer.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
 using MediaBrowser.Networking.Management;
 using System;
 using System.Net;
@@ -19,6 +20,21 @@ namespace MediaBrowser.Networking.Udp
         /// </summary>
         public event EventHandler<UdpMessageReceivedEventArgs> MessageReceived;
 
+        /// <summary>
+        /// Gets or sets the logger.
+        /// </summary>
+        /// <value>The logger.</value>
+        private ILogger Logger { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UdpServer" /> class.
+        /// </summary>
+        /// <param name="logger">The logger.</param>
+        public UdpServer(ILogger logger)
+        {
+            Logger = logger;
+        }
+
         /// <summary>
         /// Raises the <see cref="E:MessageReceived" /> event.
         /// </summary>
@@ -54,8 +70,24 @@ namespace MediaBrowser.Networking.Udp
         private IObservable<UdpReceiveResult> CreateObservable()
         {
             return Observable.Create<UdpReceiveResult>(obs =>
-                                Observable.FromAsync(() => _udpClient.ReceiveAsync())
-                                          .Subscribe(obs))
+                                Observable.FromAsync(() =>
+                                {
+                                    try
+                                    {
+                                        return _udpClient.ReceiveAsync();
+                                    }
+                                    catch (ObjectDisposedException)
+                                    {
+                                        return Task.FromResult(new UdpReceiveResult(new byte[]{}, new IPEndPoint(IPAddress.Any, 0)));
+                                    }
+                                    catch (Exception ex)
+                                    {
+                                        Logger.ErrorException("Error receiving udp message", ex);
+                                        return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
+                                    }
+                                })
+
+                             .Subscribe(obs))
                              .Repeat()
                              .Retry()
                              .Publish()
@@ -68,6 +100,10 @@ namespace MediaBrowser.Networking.Udp
         /// <param name="message">The message.</param>
         private void OnMessageReceived(UdpReceiveResult message)
         {
+            if (message.RemoteEndPoint.Port == 0)
+            {
+                return;
+            }
             var bytes = message.Buffer;
 
             OnMessageReceived(new UdpMessageReceivedEventArgs

+ 8 - 0
MediaBrowser.Networking/WebSocket/AlchemyServer.cs

@@ -42,6 +42,12 @@ namespace MediaBrowser.Networking.WebSocket
             _logger = logger;
         }
 
+        /// <summary>
+        /// Gets the port.
+        /// </summary>
+        /// <value>The port.</value>
+        public int Port { get; private set; }
+
         /// <summary>
         /// Starts the specified port number.
         /// </summary>
@@ -56,6 +62,8 @@ namespace MediaBrowser.Networking.WebSocket
 
             WebSocketServer.Start();
 
+            Port = portNumber;
+
             _logger.Info("Alchemy Web Socket Server started");
         }
 

+ 1 - 2
MediaBrowser.ServerApplication/App.xaml.cs

@@ -255,7 +255,6 @@ namespace MediaBrowser.ServerApplication
 
             base.OnExit(e);
 
-            Kernel.Dispose();
             CompositionRoot.Dispose();
         }
 
@@ -360,7 +359,7 @@ namespace MediaBrowser.ServerApplication
         {
             Dispatcher.Invoke(ReleaseMutex);
 
-            Kernel.Dispose();
+            CompositionRoot.Dispose();
 
             System.Windows.Forms.Application.Restart();
 

+ 31 - 24
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Common.Implementations;
 using MediaBrowser.Common.Implementations.ScheduledTasks;
 using MediaBrowser.Common.Implementations.Serialization;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Implementations.Server;
 using MediaBrowser.Common.Kernel;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.ScheduledTasks;
@@ -65,16 +66,6 @@ namespace MediaBrowser.ServerApplication
         /// </summary>
         private readonly IServerApplicationPaths _applicationPaths = new ServerApplicationPaths();
 
-        /// <summary>
-        /// The _task manager
-        /// </summary>
-        private readonly ITaskManager _taskManager;
-
-        /// <summary>
-        /// The _task manager
-        /// </summary>
-        private readonly IHttpServer _httpServer;
-
         /// <summary>
         /// Gets a value indicating whether this instance is first run.
         /// </summary>
@@ -89,29 +80,34 @@ namespace MediaBrowser.ServerApplication
             : base()
         {
             IsFirstRun = !File.Exists(_applicationPaths.SystemConfigurationFilePath);
-            
+
             Logger = new NLogger("App");
 
             DiscoverTypes();
 
-            _taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger);
+            Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, Logger);
+            
+            var networkManager = new NetworkManager();
+
+            var serverManager = new ServerManager(this, Kernel, networkManager, _jsonSerializer, Logger);
+
+            var taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger, serverManager);
 
-            Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, _taskManager, Logger);
             ReloadLogger();
 
             Logger.Info("Version {0} initializing", ApplicationVersion);
 
-            _httpServer = ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html");
+            var httpServer = ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html");
 
-            RegisterResources();
+            RegisterResources(taskManager, httpServer, networkManager, serverManager);
 
-            FindParts();
+            FindParts(taskManager, httpServer);
         }
 
         /// <summary>
         /// Registers resources that classes will depend on
         /// </summary>
-        private void RegisterResources()
+        private void RegisterResources(ITaskManager taskManager, IHttpServer httpServer, INetworkManager networkManager, IServerManager serverManager)
         {
             RegisterSingleInstance<IKernel>(Kernel);
             RegisterSingleInstance(Kernel);
@@ -121,28 +117,31 @@ namespace MediaBrowser.ServerApplication
 
             RegisterSingleInstance(_applicationPaths);
             RegisterSingleInstance<IApplicationPaths>(_applicationPaths);
-            RegisterSingleInstance(_taskManager);
+            RegisterSingleInstance(taskManager);
             RegisterSingleInstance<IIsoManager>(new PismoIsoManager(Logger));
             RegisterSingleInstance<IBlurayExaminer>(new BdInfoExaminer());
             RegisterSingleInstance<IHttpClient>(new HttpManager(_applicationPaths, Logger));
-            RegisterSingleInstance<INetworkManager>(new NetworkManager());
             RegisterSingleInstance<IZipClient>(new DotNetZipClient());
             RegisterSingleInstance<IWebSocketServer>(() => new AlchemyServer(Logger));
             RegisterSingleInstance(_jsonSerializer);
             RegisterSingleInstance(_xmlSerializer);
             RegisterSingleInstance(ProtobufSerializer);
-            RegisterSingleInstance<IUdpServer>(new UdpServer());
-            RegisterSingleInstance(_httpServer);
+            RegisterSingleInstance<IUdpServer>(new UdpServer(Logger), false);
+            RegisterSingleInstance(httpServer, false);
+
+            RegisterSingleInstance(networkManager);
+
+            RegisterSingleInstance(serverManager);
         }
 
         /// <summary>
         /// Finds the parts.
         /// </summary>
-        private void FindParts()
+        private void FindParts(ITaskManager taskManager, IHttpServer httpServer)
         {
-            _taskManager.AddTasks(GetExports<IScheduledTask>(false));
+            taskManager.AddTasks(GetExports<IScheduledTask>(false));
 
-            _httpServer.Init(GetExports<IRestfulService>(false));
+            httpServer.Init(GetExports<IRestfulService>(false));
         }
 
         /// <summary>
@@ -240,5 +239,13 @@ namespace MediaBrowser.ServerApplication
             // Include composable parts in the running assembly
             yield return GetType().Assembly;
         }
+
+        /// <summary>
+        /// Shuts down.
+        /// </summary>
+        public void Shutdown()
+        {
+            App.Instance.Shutdown();
+        }
     }
 }

+ 0 - 4
MediaBrowser.WebDashboard/Html/css/site.css

@@ -293,10 +293,6 @@ form, .readOnlyContent {
         right: 30px;
     }
 
-    .localnav .ui-btn-inner {
-        font-size: 16px;
-    }
-
     .libraryPage .ui-content {
         padding-right: 50px;
         padding-left: 50px;

+ 1 - 1
Nuget/MediaBrowser.ApiClient.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.ApiClient</id>
-        <version>3.0.0.6-beta</version>
+        <version>3.0.0.7-beta</version>
         <title>MediaBrowser.ApiClient</title>
         <authors>Media Browser Team</authors>
         <owners>scottisafool,Luke</owners>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title />
         <authors>Luke</authors>
         <owners>Media Browser Team</owners>
@@ -10,7 +10,7 @@
         <requireLicenseAcceptance>false</requireLicenseAcceptance>
         <description>Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption.</description>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.6" />
+            <dependency id="MediaBrowser.Common" version="3.0.7" />
             <dependency id="NLog" version="2.0.0.2000" />
             <dependency id="ServiceStack" version="3.9.37" />
             <dependency id="ServiceStack.Api.Swagger" version="3.9.35" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners />

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners />
@@ -10,7 +10,7 @@
         <requireLicenseAcceptance>false</requireLicenseAcceptance>
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.6" />
+            <dependency id="MediaBrowser.Common" version="3.0.7" />
         </dependencies>
     </metadata>
     <files>