From 14f32cd3094ca5250568a36e0b60a8192a29d175 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 23 Dec 2016 21:18:59 +0100 Subject: [PATCH] Decouple Default Settings to a separate publicly accessible class. Required for tests. --- .../Configuration/DefaultSettings.cs | 71 +++++++++++ .../Configuration/IWinSWConfiguration.cs | 57 +++++++++ src/Core/WinSWCore/ServiceDescriptor.cs | 118 +++++++++++------- src/Core/WinSWCore/WinSWCore.csproj | 2 + 4 files changed, 203 insertions(+), 45 deletions(-) create mode 100644 src/Core/WinSWCore/Configuration/DefaultSettings.cs create mode 100644 src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs diff --git a/src/Core/WinSWCore/Configuration/DefaultSettings.cs b/src/Core/WinSWCore/Configuration/DefaultSettings.cs new file mode 100644 index 0000000..c7e10c8 --- /dev/null +++ b/src/Core/WinSWCore/Configuration/DefaultSettings.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text; +using System.Xml; +using WMI; + +namespace winsw.Configuration +{ + /// + /// Default WinSW settings + /// + public sealed class DefaultWinSWSettings : IWinSWConfiguration + { + public string Id { get { return null; } } + public string Caption { get { return null; } } + public string Description { get { return null; } } + public string Executable { get { return null; } } + + public string ExecutablePath + { + get + { + // this returns the executable name as given by the calling process, so + // it needs to be absolutized. + string p = Environment.GetCommandLineArgs()[0]; + return Path.GetFullPath(p); + } + } + + // Installation + public bool AllowServiceAcountLogonRight { get { return false; } } + public string ServiceAccountPassword { get { return null; } } + public string ServiceAccountUser { get { return null; } } + public List FailureActions { get { return new List(); } } + public TimeSpan ResetFailureAfter { get { return TimeSpan.FromDays(1); } } + + // Executable management + public string Arguments { get { return ""; } } + public string Startarguments { get { return null; } } + public string StopExecutable { get { return null; } } + public string Stoparguments { get { return null; } } + public string WorkingDirectory { get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } } + public ProcessPriorityClass Priority { get { return ProcessPriorityClass.Normal; } } + public TimeSpan StopTimeout { get { return TimeSpan.FromSeconds(15); } } + public bool StopParentProcessFirst { get { return false; } } + + // Service management + public StartMode StartMode { get { return StartMode.Automatic; } } + public string[] ServiceDependencies { get { return new string[0]; } } + public TimeSpan WaitHint { get { return TimeSpan.FromSeconds(15); } } + public TimeSpan SleepTime { get { return TimeSpan.FromSeconds(1); } } + public bool Interactive { get { return false; } } + + // Logging + public string LogDirectory { get { return Path.GetDirectoryName(ExecutablePath); } } + public string LogMode { get { return "append"; } } + + // Environment + public List Downloads { get { return new List(); } } + public Dictionary EnvironmentVariables { get { return new Dictionary(); } } + + // Misc + public bool BeepOnShutdown { get { return false; } } + + // Extensions + public XmlNode ExtensionsConfiguration { get {return null; } } + } +} diff --git a/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs b/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs new file mode 100644 index 0000000..4a9f621 --- /dev/null +++ b/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml; +using WMI; +namespace winsw.Configuration +{ + interface IWinSWConfiguration + { + //TODO: Document the parameters && refactor + + string Id { get; } + string Caption { get; } + string Description { get; } + string Executable { get; } + string ExecutablePath { get; } + + // Installation + bool AllowServiceAcountLogonRight { get; } + string ServiceAccountPassword { get; } + string ServiceAccountUser { get; } + List FailureActions { get; } + TimeSpan ResetFailureAfter { get; } + + // Executable management + string Arguments { get; } + string Startarguments { get; } + string StopExecutable { get; } + string Stoparguments { get; } + string WorkingDirectory { get; } + ProcessPriorityClass Priority { get; } + TimeSpan StopTimeout { get; } + bool StopParentProcessFirst { get; } + + // Service management + StartMode StartMode { get; } + string[] ServiceDependencies { get; } + TimeSpan WaitHint { get; } + TimeSpan SleepTime { get; } + bool Interactive { get; } + + // Logging + string LogDirectory { get; } + //TODO: replace by enum + string LogMode { get; } + + // Environment + List Downloads { get; } + Dictionary EnvironmentVariables { get; } + + // Misc + bool BeepOnShutdown { get; } + + // Extensions + XmlNode ExtensionsConfiguration { get; } + } +} diff --git a/src/Core/WinSWCore/ServiceDescriptor.cs b/src/Core/WinSWCore/ServiceDescriptor.cs index 8dc3c40..fce0567 100755 --- a/src/Core/WinSWCore/ServiceDescriptor.cs +++ b/src/Core/WinSWCore/ServiceDescriptor.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; +using winsw.Configuration; using winsw.Native; using winsw.Util; using WMI; @@ -14,17 +15,21 @@ namespace winsw /// /// In-memory representation of the configuration file. /// - public class ServiceDescriptor + public class ServiceDescriptor : IWinSWConfiguration { // ReSharper disable once InconsistentNaming protected readonly XmlDocument dom = new XmlDocument(); + private static readonly DefaultWinSWSettings defaults = new DefaultWinSWSettings(); + public static DefaultWinSWSettings Defaults { get { return defaults; } } + /// /// Where did we find the configuration file? /// /// This string is "c:\abc\def\ghi" when the configuration XML is "c:\abc\def\ghi.xml" /// public string BasePath { get; set; } + /// /// The file name portion of the configuration file. /// @@ -36,10 +41,8 @@ namespace winsw { get { - // this returns the executable name as given by the calling process, so - // it needs to be absolutized. - string p = Environment.GetCommandLineArgs()[0]; - return Path.GetFullPath(p); + // Currently there is no opportunity to alter the executable path + return Defaults.ExecutablePath; } } @@ -191,7 +194,7 @@ namespace winsw { get { - string arguments = AppendTags("argument"); + string arguments = AppendTags("argument", Defaults.Arguments); if (arguments == null) { @@ -199,7 +202,7 @@ namespace winsw if (argumentsNode == null) { - return ""; + return Defaults.Arguments; } return Environment.ExpandEnvironmentVariables(argumentsNode.InnerText); @@ -218,7 +221,7 @@ namespace winsw { get { - return AppendTags("startargument"); + return AppendTags("startargument", Defaults.Startarguments); } } @@ -229,7 +232,7 @@ namespace winsw { get { - return AppendTags("stopargument"); + return AppendTags("stopargument", Defaults.Startarguments); } } @@ -237,7 +240,7 @@ namespace winsw public string WorkingDirectory { get { var wd = SingleElement("workingdirectory", true); - return String.IsNullOrEmpty(wd) ? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) : wd; + return String.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd; } } @@ -276,13 +279,13 @@ namespace winsw /// Combines the contents of all the elements of the given name, /// or return null if no element exists. Handles whitespace quotation. /// - private string AppendTags(string tagName) + private string AppendTags(string tagName, string defaultValue = null) { XmlNode argumentNode = dom.SelectSingleNode("//" + tagName); if (argumentNode == null) { - return null; + return defaultValue; } else { @@ -327,31 +330,51 @@ namespace winsw } else { - return Path.GetDirectoryName(ExecutablePath); + return Defaults.LogDirectory; } } } + public string LogMode + { + get + { + string mode = null; + + // first, backward compatibility with older configuration + XmlElement e = (XmlElement)dom.SelectSingleNode("//logmode"); + if (e != null) + { + mode = e.InnerText; + } + else + { + // this is more modern way, to support nested elements as configuration + e = (XmlElement)dom.SelectSingleNode("//log"); + if (e != null) + mode = e.GetAttribute("mode"); + } + + if (mode == null) + { + mode = Defaults.LogMode; + } + return mode; + } + } + public LogHandler LogHandler { + get { - string mode=null; - - // first, backward compatibility with older configuration XmlElement e = (XmlElement)dom.SelectSingleNode("//logmode"); - if (e!=null) { - mode = e.InnerText; - } else { + if (e == null) + { // this is more modern way, to support nested elements as configuration e = (XmlElement)dom.SelectSingleNode("//log"); - if (e!=null) - mode = e.GetAttribute("mode"); } - - if (mode == null) mode = "append"; - - switch (mode) + switch (LogMode) { case "rotate": return new SizeBasedRollingLogAppender(LogDirectory, BaseName); @@ -384,7 +407,7 @@ namespace winsw return new DefaultLogAppender(LogDirectory, BaseName); default: - throw new InvalidDataException("Undefined logging mode: " + mode); + throw new InvalidDataException("Undefined logging mode: " + LogMode); } } @@ -397,16 +420,17 @@ namespace winsw { get { - ArrayList serviceDependencies = new ArrayList(); - var xmlNodeList = dom.SelectNodes("//depend"); if (xmlNodeList != null) + { + ArrayList serviceDependencies = new ArrayList(); foreach (XmlNode depend in xmlNodeList) { serviceDependencies.Add(depend.InnerText); } - - return (string[])serviceDependencies.ToArray(typeof(string)); + return (string[])serviceDependencies.ToArray(typeof(string)); + } + return Defaults.ServiceDependencies; } } @@ -442,7 +466,7 @@ namespace winsw get { var p = SingleElement("startmode", true); - if (p == null) return StartMode.Automatic; // default value + if (p == null) return Defaults.StartMode; try { return (StartMode)Enum.Parse(typeof(StartMode), p, true); @@ -460,7 +484,7 @@ namespace winsw } /// - /// True if the service should when finished on shutdown. + /// True if the service should beep when finished on shutdown. /// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx /// public bool BeepOnShutdown @@ -481,7 +505,7 @@ namespace winsw { get { - return SingleTimeSpanElement(dom.FirstChild, "waithint", TimeSpan.FromSeconds(15)); + return SingleTimeSpanElement(dom.FirstChild, "waithint", Defaults.WaitHint); } } @@ -495,7 +519,7 @@ namespace winsw { get { - return SingleTimeSpanElement(dom.FirstChild, "sleeptime", TimeSpan.FromSeconds(1)); + return SingleTimeSpanElement(dom.FirstChild, "sleeptime", Defaults.SleepTime); } } @@ -538,13 +562,17 @@ namespace winsw { get { - List r = new List(); var xmlNodeList = dom.SelectNodes("//download"); - if (xmlNodeList != null) - foreach (XmlNode n in xmlNodeList) - { - r.Add(new Download(n)); - } + if (xmlNodeList == null) + { + return Defaults.Downloads; + } + + List r = new List(); + foreach (XmlNode n in xmlNodeList) + { + r.Add(new Download(n)); + } return r; } } @@ -587,7 +615,7 @@ namespace winsw { get { - return SingleTimeSpanElement(dom.FirstChild, "resetfailure", TimeSpan.FromDays(1)); + return SingleTimeSpanElement(dom.FirstChild, "resetfailure", Defaults.ResetFailureAfter); } } @@ -669,13 +697,13 @@ namespace winsw } /// - /// Time to wait for the service to gracefully shutdown before we forcibly kill it + /// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it /// public TimeSpan StopTimeout { get { - return SingleTimeSpanElement(dom.FirstChild, "stoptimeout", TimeSpan.FromSeconds(15)); + return SingleTimeSpanElement(dom.FirstChild, "stoptimeout", Defaults.StopTimeout); } } @@ -689,7 +717,7 @@ namespace winsw { return result; } - return false; + return Defaults.StopParentProcessFirst; } } @@ -701,7 +729,7 @@ namespace winsw get { var p = SingleElement("priority",true); - if (p == null) return ProcessPriorityClass.Normal; // default value + if (p == null) return Defaults.Priority; return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true); } diff --git a/src/Core/WinSWCore/WinSWCore.csproj b/src/Core/WinSWCore/WinSWCore.csproj index 219e5fe..72fbf20 100644 --- a/src/Core/WinSWCore/WinSWCore.csproj +++ b/src/Core/WinSWCore/WinSWCore.csproj @@ -45,6 +45,7 @@ + @@ -53,6 +54,7 @@ +