mirror of https://github.com/winsw/winsw
191 lines
6.4 KiB
C#
191 lines
6.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Xml;
|
|
using log4net;
|
|
|
|
namespace WinSW.Extensions
|
|
{
|
|
public class WinSWExtensionManager
|
|
{
|
|
public Dictionary<string, IWinSWExtension> Extensions { get; }
|
|
|
|
public XmlServiceConfig ServiceConfig { get; }
|
|
|
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
|
|
|
public WinSWExtensionManager(XmlServiceConfig serviceConfig)
|
|
{
|
|
this.ServiceConfig = serviceConfig;
|
|
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
|
}
|
|
|
|
private static IWinSWExtension CreateExtensionInstance(string id, string className)
|
|
{
|
|
object created;
|
|
|
|
try
|
|
{
|
|
Type? t = Type.GetType(className);
|
|
if (t is null)
|
|
{
|
|
throw new ExtensionException(id, "Class " + className + " does not exist");
|
|
}
|
|
|
|
created = Activator.CreateInstance(t)!;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new ExtensionException(id, "Cannot load the class by name: " + className, ex);
|
|
}
|
|
|
|
if (!(created is IWinSWExtension extension))
|
|
{
|
|
throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType());
|
|
}
|
|
|
|
return extension;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Notifies all extensions that the wrapper is being started.
|
|
/// They are supposed to run the initialization logic.
|
|
/// If any extensions fails, WinSW startup should be interrupted.
|
|
/// </summary>
|
|
/// <exception cref="Exception">Start failure</exception>
|
|
public void FireOnWrapperStarted()
|
|
{
|
|
foreach (var ext in this.Extensions)
|
|
{
|
|
try
|
|
{
|
|
ext.Value.OnWrapperStarted();
|
|
}
|
|
catch (ExtensionException ex)
|
|
{
|
|
Log.Fatal("onWrapperStarted() handler failed for " + ext.Value.DisplayName, ex);
|
|
throw; // Propagate error to stop the startup
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Notifies all extensions that the wrapper is being stopped.
|
|
/// If an error happens, further extensions will be tried
|
|
/// </summary>
|
|
public void FireBeforeWrapperStopped()
|
|
{
|
|
foreach (var ext in this.Extensions)
|
|
{
|
|
try
|
|
{
|
|
ext.Value.BeforeWrapperStopped();
|
|
}
|
|
catch (ExtensionException ex)
|
|
{
|
|
Log.Error("beforeWrapperStopped() handler failed for " + ext.Value.DisplayName, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handler, which is being invoked once the child process is started.
|
|
/// </summary>
|
|
/// <param name="process">Process</param>
|
|
public void FireOnProcessStarted(System.Diagnostics.Process process)
|
|
{
|
|
foreach (var ext in this.Extensions)
|
|
{
|
|
try
|
|
{
|
|
ext.Value.OnProcessStarted(process);
|
|
}
|
|
catch (ExtensionException ex)
|
|
{
|
|
Log.Error("onProcessStarted() handler failed for " + ext.Value.DisplayName, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handler, which is being invoked once the child process is terminated.
|
|
/// </summary>
|
|
/// <param name="process">Process</param>
|
|
public void FireOnProcessTerminated(System.Diagnostics.Process process)
|
|
{
|
|
foreach (var ext in this.Extensions)
|
|
{
|
|
try
|
|
{
|
|
ext.Value.OnProcessTerminated(process);
|
|
}
|
|
catch (ExtensionException ex)
|
|
{
|
|
Log.Error("onProcessTerminated() handler failed for " + ext.Value.DisplayName, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Implement loading of external extensions. Current version supports internal hack
|
|
#region Extension load management
|
|
|
|
/// Loads extensions according to the configuration file.
|
|
/// </summary>
|
|
/// <param name="logger">Logger</param>
|
|
/// <exception cref="Exception">Loading failure</exception>
|
|
public void LoadExtensions()
|
|
{
|
|
var extensionIds = this.ServiceConfig.ExtensionIds;
|
|
foreach (string extensionId in extensionIds)
|
|
{
|
|
this.LoadExtension(extensionId);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads extensions from the configuration file
|
|
/// </summary>
|
|
/// <param name="id">Extension ID</param>
|
|
/// <param name="logger">Logger</param>
|
|
/// <exception cref="Exception">Loading failure</exception>
|
|
private void LoadExtension(string id)
|
|
{
|
|
if (this.Extensions.ContainsKey(id))
|
|
{
|
|
throw new ExtensionException(id, "Extension has been already loaded");
|
|
}
|
|
|
|
XmlNode? extensionsConfig = this.ServiceConfig.ExtensionsConfiguration;
|
|
XmlElement? configNode = extensionsConfig is null ? null : extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement;
|
|
if (configNode is null)
|
|
{
|
|
throw new ExtensionException(id, "Cannot get the configuration entry");
|
|
}
|
|
|
|
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
|
|
if (descriptor.Enabled)
|
|
{
|
|
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
|
extension.Descriptor = descriptor;
|
|
try
|
|
{
|
|
extension.Configure(this.ServiceConfig, configNode);
|
|
}
|
|
catch (Exception ex)
|
|
{ // Consider any unexpected exception as fatal
|
|
Log.Fatal("Failed to configure the extension " + id, ex);
|
|
throw;
|
|
}
|
|
|
|
this.Extensions.Add(id, extension);
|
|
Log.Info("Extension loaded: " + id);
|
|
}
|
|
else
|
|
{
|
|
Log.Warn("Extension is disabled: " + id);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|