JD2022-TU1/main/tools/framework/JD.TeaBoxAddinSystem/Bootstrap/ApplicationStartup.cs

434 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using JD.Kernel.Classes;
using JD.Kernel.StaticHelpers;
using JD.LogService;
using JD.TeaBoxAddinSystem.Addins;
using JD.TeaBoxAddinSystem.Addins.MEF;
using JD.TeaBoxAddinSystem.Addins.MEF.Integration;
using JD.Threading;
using Message = JD.LogService.Message;
namespace JD.TeaBoxAddinSystem.Bootstrap
{
/// <summary>
/// Base class for objects used to bootstrap applications.
/// </summary>
public class ApplicationStartup
{
#region Variables
internal const string CoreBootstrapPath = "Bootstrap/All";
private readonly Arguments m_arguments;
private readonly string m_applicationName;
private readonly RuntimeMode m_runtimeMode = RuntimeMode.Normal;
private readonly ApplicationConfiguration m_configuration;
private readonly LoggingContext m_bootstrapLoggingContext = new LoggingContext();
private static ApplicationStartup m_instance;
private IDisposable m_splashScreenHandle;
// Metrics
private StartupStatistic m_startupStatistic;
#endregion
#region Constructor
/// <summary>
/// Constructor.
/// </summary>
/// <param name="arguments">Application Arguments.</param>
/// <param name="applicationName">Application name.</param>
/// <param name="runtimeMode">Runtime mode.</param>
public ApplicationStartup(string[] arguments, string applicationName, RuntimeMode runtimeMode)
{
if (string.IsNullOrEmpty(applicationName))
{
applicationName = (Runtime.EntryAssembly != null ? Runtime.EntryAssembly.FullName : Runtime.ApplicationName);
}
m_arguments = new Arguments(arguments);
m_applicationName = applicationName;
m_configuration = new ApplicationConfiguration(ApplicationName);
m_runtimeMode = runtimeMode;
}
#endregion
#region Properties
/// <summary>
/// Unique instance of the started application startup. Not for external use. Back off.
/// </summary>
/// <remarks>
/// Consider using <see cref="Runtime"/> instead.
/// </remarks>
public static ApplicationStartup Instance
{
get
{
return m_instance;
}
}
/// <summary>
/// Arguments.
/// </summary>
public Arguments Arguments
{
get
{
return m_arguments;
}
}
/// <summary>
/// Application Name.
/// </summary>
public string ApplicationName
{
get
{
return m_applicationName;
}
}
/// <summary>
/// Whether the application was started in embedded mode.
/// </summary>
public RuntimeMode RuntimeMode
{
get
{
return m_runtimeMode;
}
}
internal ApplicationConfiguration Configuration
{
get
{
return m_configuration;
}
}
/// <summary>
/// Returns true if this application startup forces the application in command-line mode.
/// </summary>
protected virtual bool ForceCommandLineMode
{
get
{
return false;
}
}
/// <summary>
/// Addin Logging Context.
/// </summary>
public LoggingContext BootstrapLoggingContext
{
get { return m_bootstrapLoggingContext; }
}
#endregion
#region Methods
#region Main Methods
/// <summary>
/// Starts the application (must be in embedded mode).
/// </summary>
/// <returns>Dispose the result to quit the application.</returns>
public IDisposable StartEmbedded()
{
Throw.Assert(RuntimeMode == RuntimeMode.EmbeddedInProcess || RuntimeMode == RuntimeMode.SystemTests);
Start();
return new DisposableHelper(Quit);
}
/// <summary>
/// Starts the application.
/// </summary>
/// <returns>Exit code for the application.</returns>
public int Start()
{
if (Runtime.Initialized)
{
throw new InvalidOperationException("Cannot start an ApplicationStartup twice in a same AppDomain");
}
if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
{
Thread.CurrentThread.Name = "Main Thread";
}
Debug.Assert(m_instance == null);
m_instance = this;
m_startupStatistic = new StartupStatistic();
if (ForceCommandLineMode == false)
{
Idle.Event += Application_Idle;
}
#if DEBUG
if (Debugger.IsAttached)
{
return StartImpl();
}
#endif
return StartImpl();
}
private int StartImpl()
{
#if DEBUG
Control.CheckForIllegalCrossThreadCalls = true;
#endif
// To have invariant type conversion
ThreadHelper.SetDefaultThreadCulture(Thread.CurrentThread);
LogService.LogService.LogMessage("Starting {0}...", ApplicationName);
ConfigureInternal();
// Initialize the runtime.
LogService.LogService.LogMessage("Initializing runtime...");
Runtime.Initialize(this, m_bootstrapLoggingContext);
LogService.LogService.LogMessage("Starting core services...");
StartCoreServices();
LogService.LogService.LogMessage("Running...");
return Run();
}
private void ConfigureInternal()
{
Configure(m_configuration);
}
/// <summary>
/// Starts the main services
/// </summary>
protected virtual void StartCoreServices()
{
StringParserService.StringParserService.Properties["ApplicationName"] = ApplicationName;
}
/// <summary>
/// Configures the application, using the given <paramref name="configuration"/>.
/// </summary>
/// <param name="configuration"></param>
protected virtual void Configure(ApplicationConfiguration configuration)
{
}
/// <summary>
/// Initializes and processes the command-line arguments
/// </summary>
/// <exception cref="ArgumentException">Thrown when some arguments are invalid.</exception>
private void InitializeCommandLineArguments()
{
Runtime.CommandLineMode = ShouldStartInCommandLineMode(Runtime.CommandLineHandlerPaths);
Runtime.CommandLineMode |= ForceCommandLineMode;
}
private bool ShouldStartInCommandLineMode(IEnumerable<string> handlersPath)
{
bool commandLineMode = ForceCommandLineMode;
foreach (string path in handlersPath)
{
foreach (object obj in AddinManager.GetExtensionObjects<object>(path))
{
ICommandLineHandler handler = obj as ICommandLineHandler;
if (handler != null && handler.Initialize())
{
commandLineMode = true;
}
}
}
return commandLineMode;
}
private void BootstrapAddinEngine()
{
m_bootstrapLoggingContext.MessageAdded += delegate(object sender, ItemEventArgs<Message> e)
{
LogService.LogService.LogMessage(e.Item);
};
m_bootstrapLoggingContext.WarningAdded += delegate(object sender, ItemEventArgs<Error> e)
{
LogService.LogService.LogWarning(e.Item);
};
m_bootstrapLoggingContext.ErrorAdded += delegate(object sender, ItemEventArgs<Error> e)
{
LogService.LogService.LogError(e.Item);
};
AddinManager.Loggers.Add(m_bootstrapLoggingContext);
Runtime.AddinRepository.Update(m_bootstrapLoggingContext);
Runtime.AddinRepository.Load();
}
private void BootstrapMEFAddinEngine()
{
AddinRepository repository = new AddinRepository(Runtime.ApplicationRootPath, m_bootstrapLoggingContext);
repository.Load();
var service = new MEFAddinService(repository, m_bootstrapLoggingContext);
AddinService.Use(service);
}
private int Run()
{
LogService.LogService.LogMessage("Initializing Addin Tree subsystem");
var stopwatch = Stopwatch.StartNew();
BootstrapAddinEngine();
LogService.LogService.LogMessage($"Addin Tree subsystem initialized in {stopwatch.Elapsed.TotalSeconds}s");
LogService.LogService.LogMessage("Initializing MEF subsystem");
stopwatch.Restart();
BootstrapMEFAddinEngine();
LogService.LogService.LogMessage($"MEF subsystem initialized in {stopwatch.Elapsed.TotalSeconds}s");
try
{
LogService.LogService.LogMessage("Parsing command-line arguments...");
InitializeCommandLineArguments();
ShowSplashScreen();
int exitCode;
OnStarting(out exitCode);
if (exitCode != 0)
{
return exitCode;
}
// run bootstrap commands
LogService.LogService.LogMessage("Running Bootstrap commands...");
var coreBootstrap = new BootstrapCommandRunner(AddinManager.GetExtensionObjects<IBootstrapCommand>(CoreBootstrapPath));
coreBootstrap.Run();
var addinBootstrap = new BootstrapCommandRunner(AddinManager.GetExtensionObjects<IBootstrapCommand>(m_configuration.BootstrapAddinPath));
addinBootstrap.Run();
if (Runtime.RuntimeMode != RuntimeMode.Normal)
{
OnAfterApplicationStarted();
}
else
{
Quit();
}
return Runtime.ExitCode;
}
catch (CommandLineArgumentException e)
{
LogService.LogService.LogError("Error in arguments: {0}", e.Message);
Console.Error.WriteLine(e.Message);
return -1;
}
}
/// <summary>
/// Should not be called directly. To quit properly the application, use Application.Quit().
/// </summary>
/// <returns>Exit code</returns>
private void Quit()
{
LogService.LogService.LogMessage("Leaving application...");
OnQuitting();
OnQuitted(EventArgs.Empty);
}
private void ShowSplashScreen()
{
#if !DEBUG
bool noLogo = Arguments["nologo"].Count > 0;
if (!noLogo && !Runtime.CommandLineMode && Runtime.ShouldStartOwnInstance)
{
Idle.Event -= Application_Idle;
m_splashScreenHandle = OnShowSplashScreen();
Application.DoEvents();
Idle.Event += Application_Idle;
}
#endif
}
protected virtual IDisposable OnShowSplashScreen()
{
return null;
}
/// <summary>
/// Called just before the application starts.
/// </summary>
protected virtual void OnStarting(out int exitCode)
{
PropertyService.PropertyService.Load();
MessageService.MessageService.Initialize();
exitCode = 0;
}
/// <summary>
/// Called just before the application quits.
/// </summary>
protected virtual void OnQuitting()
{
PropertyService.PropertyService.Close();
MessageService.MessageService.Close();
DisposableHelper.SafeDispose(ref m_splashScreenHandle);
}
private void OnAfterApplicationStarted()
{
m_startupStatistic.MemoryUsedAfterStartUp = Process.GetCurrentProcess().PrivateMemorySize64;
m_startupStatistic.Dispose();
}
#endregion
#region Event Handlers
void Application_Idle(object sender, EventArgs e)
{
Idle.Event -= Application_Idle;
DisposableHelper.SafeDispose(ref m_splashScreenHandle);
OnAfterApplicationStarted();
}
#endregion
#endregion
#region Events
public event EventHandler Quitted;
private void OnQuitted(EventArgs e)
{
Quitted?.Invoke(this, e);
}
#endregion
}
}