starting point towards running as a service

pull/702/head
Luke Pulverenti 11 years ago
parent e50c29ffca
commit f3ce127a62

@ -133,8 +133,8 @@ namespace MediaBrowser.Api
{
Task.Run(async () =>
{
await Task.Delay(100);
_appHost.Restart();
await Task.Delay(100).ConfigureAwait(false);
await _appHost.Restart().ConfigureAwait(false);
});
}
@ -146,8 +146,8 @@ namespace MediaBrowser.Api
{
Task.Run(async () =>
{
await Task.Delay(100);
_appHost.Shutdown();
await Task.Delay(100).ConfigureAwait(false);
await _appHost.Shutdown().ConfigureAwait(false);
});
}

@ -552,7 +552,7 @@ namespace MediaBrowser.Common.Implementations
/// <summary>
/// Restarts this instance.
/// </summary>
public abstract void Restart();
public abstract Task Restart();
/// <summary>
/// Gets or sets a value indicating whether this instance can self update.
@ -582,7 +582,7 @@ namespace MediaBrowser.Common.Implementations
/// <summary>
/// Shuts down.
/// </summary>
public abstract void Shutdown();
public abstract Task Shutdown();
/// <summary>
/// Called when [application updated].

@ -37,7 +37,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Restarts this instance.
/// </summary>
void Restart();
Task Restart();
/// <summary>
/// Gets the application version.
@ -113,7 +113,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Shuts down.
/// </summary>
void Shutdown();
Task Shutdown();
/// <summary>
/// Gets the plugins.

@ -25,5 +25,11 @@ namespace MediaBrowser.Controller
/// </summary>
/// <value>The HTTP server URL prefix.</value>
string HttpServerUrlPrefix { get; }
/// <summary>
/// Gets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
bool IsBackgroundService { get; }
}
}

@ -81,7 +81,13 @@ namespace MediaBrowser.Model.System
public int HttpServerPortNumber { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SystemInfo"/> class.
/// Gets or sets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
public bool IsBackgroundService { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SystemInfo" /> class.
/// </summary>
public SystemInfo()
{

@ -1,89 +1,19 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Constants;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations;
using MediaBrowser.ServerApplication.Splash;
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Cache;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MediaBrowser.ServerApplication
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
public partial class App : Application, IApplicationInterface
{
/// <summary>
/// The single instance mutex
/// </summary>
private static Mutex _singleInstanceMutex;
/// <summary>
/// Defines the entry point of the application.
/// </summary>
[STAThread]
public static void Main()
{
bool createdNew;
var runningPath = Process.GetCurrentProcess().MainModule.FileName.Replace(Path.DirectorySeparatorChar.ToString(), string.Empty);
_singleInstanceMutex = new Mutex(true, @"Local\" + runningPath, out createdNew);
if (!createdNew)
{
_singleInstanceMutex = null;
return;
}
// Look for the existence of an update archive
var appPaths = new ServerApplicationPaths();
var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
if (File.Exists(updateArchive))
{
// Update is there - execute update
try
{
new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
// And just let the app exit so it can update
return;
}
catch (Exception e)
{
MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
}
}
var application = new App();
application.Run();
}
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>The instance.</value>
public static App Instance
{
get
{
return Current as App;
}
}
/// <summary>
/// Gets or sets the logger.
/// </summary>
@ -95,7 +25,7 @@ namespace MediaBrowser.ServerApplication
/// </summary>
/// <value>The composition root.</value>
protected ApplicationHost CompositionRoot { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="App" /> class.
/// </summary>
@ -105,6 +35,11 @@ namespace MediaBrowser.ServerApplication
InitializeComponent();
}
public bool IsBackgroundService
{
get { return false; }
}
/// <summary>
/// Gets the name of the uninstaller file.
/// </summary>
@ -114,63 +49,35 @@ namespace MediaBrowser.ServerApplication
get { return "MediaBrowser.Server.Uninstall.exe"; }
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Application.Startup" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
public void OnUnhandledException(Exception ex)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
LoadKernel();
Logger.ErrorException("UnhandledException", ex);
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
MessageBox.Show("Unhandled exception: " + ex.Message);
}
/// <summary>
/// Handles the UnhandledException event of the CurrentDomain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="UnhandledExceptionEventArgs" /> instance containing the event data.</param>
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
protected override void OnStartup(StartupEventArgs e)
{
var exception = (Exception)e.ExceptionObject;
Logger.ErrorException("UnhandledException", exception);
base.OnStartup(e);
MessageBox.Show("Unhandled exception: " + exception.Message);
if (!Debugger.IsAttached)
{
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception));
}
}
/// <summary>
/// Handles the SessionEnding event of the SystemEvents control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="SessionEndingEventArgs" /> instance containing the event data.</param>
void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
// Try to shut down gracefully
Shutdown();
LoadApplication();
}
/// <summary>
/// Loads the kernel.
/// </summary>
protected async void LoadKernel()
protected async void LoadApplication()
{
try
{
CompositionRoot = new ApplicationHost();
CompositionRoot = new ApplicationHost(this);
Logger = CompositionRoot.LogManager.GetLogger("App");
var splash = new SplashWindow(CompositionRoot.ApplicationVersion);
splash.Show();
await CompositionRoot.Init();
splash.Hide();
@ -192,13 +99,18 @@ namespace MediaBrowser.ServerApplication
}
}
public void ShutdownApplication()
{
Dispatcher.Invoke(Shutdown);
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
/// </summary>
/// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param>
protected override void OnExit(ExitEventArgs e)
{
ReleaseMutex();
MainStartup.ReleaseMutex();
base.OnExit(e);
@ -208,22 +120,6 @@ namespace MediaBrowser.ServerApplication
}
}
/// <summary>
/// Releases the mutex.
/// </summary>
private void ReleaseMutex()
{
if (_singleInstanceMutex == null)
{
return;
}
_singleInstanceMutex.ReleaseMutex();
_singleInstanceMutex.Close();
_singleInstanceMutex.Dispose();
_singleInstanceMutex = null;
}
/// <summary>
/// Opens the dashboard page.
/// </summary>
@ -281,9 +177,9 @@ namespace MediaBrowser.ServerApplication
/// Restarts this instance.
/// </summary>
/// <exception cref="System.NotImplementedException"></exception>
public void Restart()
public void RestartApplication()
{
Dispatcher.Invoke(ReleaseMutex);
Dispatcher.Invoke(MainStartup.ReleaseMutex);
CompositionRoot.Dispose();

@ -1,5 +1,4 @@
using System.Windows.Forms;
using MediaBrowser.Api;
using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Constants;
@ -7,7 +6,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Implementations;
using MediaBrowser.Common.Implementations.IO;
using MediaBrowser.Common.Implementations.ScheduledTasks;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@ -184,6 +182,13 @@ namespace MediaBrowser.ServerApplication
private IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; }
public bool IsBackgroundService
{
get { return _appInterface != null && _appInterface.IsBackgroundService; }
}
private readonly IApplicationInterface _appInterface;
/// <summary>
/// The full path to our startmenu shortcut
/// </summary>
@ -194,6 +199,11 @@ namespace MediaBrowser.ServerApplication
private Task<IHttpServer> _httpServerCreationTask;
public ApplicationHost(IApplicationInterface appInterface)
{
_appInterface = appInterface;
}
/// <summary>
/// Runs the startup tasks.
/// </summary>
@ -505,7 +515,7 @@ namespace MediaBrowser.ServerApplication
base.OnConfigurationUpdated(sender, e);
HttpServer.EnableHttpRequestLogging = ServerConfigurationManager.Configuration.EnableHttpLevelLogging;
if (!string.Equals(HttpServer.UrlPrefix, HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
{
NotifyPendingRestart();
@ -521,19 +531,18 @@ namespace MediaBrowser.ServerApplication
/// <summary>
/// Restarts this instance.
/// </summary>
public override void Restart()
public override async Task Restart()
{
try
{
var task = ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None);
task.Wait();
await ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending server restart web socket message", ex);
}
App.Instance.Restart();
_appInterface.RestartApplication();
}
/// <summary>
@ -618,7 +627,8 @@ namespace MediaBrowser.ServerApplication
Id = _systemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath,
MacAddress = GetMacAddress(),
HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber
HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber,
IsBackgroundService = IsBackgroundService
};
}
@ -642,19 +652,18 @@ namespace MediaBrowser.ServerApplication
/// <summary>
/// Shuts down.
/// </summary>
public override void Shutdown()
public override async Task Shutdown()
{
try
{
var task = ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None);
task.Wait();
await ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending server shutdown web socket message", ex);
}
App.Instance.Dispatcher.Invoke(App.Instance.Shutdown);
_appInterface.ShutdownApplication();
}
/// <summary>

@ -0,0 +1,30 @@
using System.ServiceProcess;
namespace MediaBrowser.ServerApplication
{
public class BackgroundService : ServiceBase
{
public BackgroundService()
{
CanPauseAndContinue = false;
CanHandleSessionChangeEvent = true;
CanStop = false;
CanShutdown = true;
ServiceName = "Media Browser";
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
base.OnSessionChange(changeDescription);
}
protected override void OnStart(string[] args)
{
}
protected override void OnShutdown()
{
base.OnShutdown();
}
}
}

@ -64,7 +64,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints
{
_logger.ErrorException("Error launching startup wizard", ex);
MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
if (!_appHost.IsBackgroundService)
{
MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
}
}
}

@ -0,0 +1,32 @@
using System;
namespace MediaBrowser.ServerApplication
{
/// <summary>
/// Interface IApplicationInterface
/// </summary>
public interface IApplicationInterface
{
/// <summary>
/// Gets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
bool IsBackgroundService { get; }
/// <summary>
/// Shutdowns the application.
/// </summary>
void ShutdownApplication();
/// <summary>
/// Restarts the application.
/// </summary>
void RestartApplication();
/// <summary>
/// Called when [unhandled exception].
/// </summary>
/// <param name="ex">The ex.</param>
void OnUnhandledException(Exception ex);
}
}

@ -0,0 +1,134 @@
using MediaBrowser.Common.Constants;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Server.Implementations;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
using Microsoft.Win32;
namespace MediaBrowser.ServerApplication
{
public class MainStartup
{
/// <summary>
/// The single instance mutex
/// </summary>
private static Mutex _singleInstanceMutex;
private static IApplicationInterface _applicationInterface;
/// <summary>
/// Defines the entry point of the application.
/// </summary>
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
bool createdNew;
var runningPath = Process.GetCurrentProcess().MainModule.FileName.Replace(Path.DirectorySeparatorChar.ToString(), string.Empty);
_singleInstanceMutex = new Mutex(true, @"Local\" + runningPath, out createdNew);
if (!createdNew)
{
_singleInstanceMutex = null;
return;
}
// Look for the existence of an update archive
var appPaths = new ServerApplicationPaths();
var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
if (File.Exists(updateArchive))
{
// Update is there - execute update
try
{
new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
// And just let the app exit so it can update
return;
}
catch (Exception e)
{
MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
}
}
StartApplication();
}
private static void StartApplication()
{
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
var commandLineArgs = Environment.GetCommandLineArgs();
if (commandLineArgs.Length > 1 && commandLineArgs[1].Equals("-service"))
{
// Start application as a service
StartBackgroundService();
}
else
{
StartWpfApp();
}
}
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
// Try to shutdown gracefully
if (_applicationInterface != null)
{
_applicationInterface.ShutdownApplication();
}
}
private static void StartWpfApp()
{
var app = new App();
_applicationInterface = app;
app.Run();
}
private static void StartBackgroundService()
{
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = (Exception)e.ExceptionObject;
if (_applicationInterface != null)
{
_applicationInterface.OnUnhandledException(exception);
}
if (!Debugger.IsAttached)
{
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception));
}
}
/// <summary>
/// Releases the mutex.
/// </summary>
internal static void ReleaseMutex()
{
if (_singleInstanceMutex == null)
{
return;
}
_singleInstanceMutex.ReleaseMutex();
_singleInstanceMutex.Close();
_singleInstanceMutex.Dispose();
_singleInstanceMutex = null;
}
}
}

@ -119,7 +119,7 @@ namespace MediaBrowser.ServerApplication
Dispatcher.InvokeAsync(() =>
{
var logWindow = App.Instance.Windows.OfType<LogWindow>().FirstOrDefault();
var logWindow = App.Current.Windows.OfType<LogWindow>().FirstOrDefault();
if ((logWindow == null && _configurationManager.Configuration.ShowLogWindow) || (logWindow != null && !_configurationManager.Configuration.ShowLogWindow))
{
@ -204,7 +204,7 @@ namespace MediaBrowser.ServerApplication
{
App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
}
/// <summary>
/// Occurs when [property changed].
/// </summary>
@ -258,7 +258,7 @@ namespace MediaBrowser.ServerApplication
{
App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
}
/// <summary>
/// Handles the click event of the cmVisitCT control.
/// </summary>
@ -285,9 +285,9 @@ namespace MediaBrowser.ServerApplication
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
private void cmExit_click(object sender, RoutedEventArgs e)
private async void cmExit_click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
await _appHost.Shutdown().ConfigureAwait(false);
}
/// <summary>
@ -295,9 +295,9 @@ namespace MediaBrowser.ServerApplication
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
private void cmdReloadServer_click(object sender, RoutedEventArgs e)
private async void cmdReloadServer_click(object sender, RoutedEventArgs e)
{
App.Instance.Restart();
await _appHost.Restart().ConfigureAwait(false);
}
/// <summary>

@ -57,7 +57,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>MediaBrowser.ServerApplication.App</StartupObject>
<StartupObject>MediaBrowser.ServerApplication.MainStartup</StartupObject>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
@ -187,6 +187,7 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.0.11.0.17-ALPHA\lib\net45\System.Windows.Interactivity.dll</HintPath>
@ -203,8 +204,13 @@
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="BackgroundService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="EntryPoints\StartupWizard.cs" />
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
<Compile Include="IApplicationInterface.cs" />
<Compile Include="MainStartup.cs" />
<Compile Include="Splash\SplashWindow.xaml.cs">
<DependentUpon>SplashWindow.xaml</DependentUpon>
</Compile>

Loading…
Cancel
Save