diff --git a/project/console/AppRunner.cs b/project/console/AppRunner.cs index 8eae02346..e166e8063 100644 --- a/project/console/AppRunner.cs +++ b/project/console/AppRunner.cs @@ -27,54 +27,27 @@ public class AppRunner /// public int Run(string[] args, bool usesShadowCopying) { - ConsoleRunnerArguments consoleArgs = new ConsoleRunnerArguments(); - List extra = new List(); - - OptionSet opts = new OptionSet(); - opts.Add("h|?|help", "display this help screen", delegate(string v) { consoleArgs.ShowHelp = v != null; }) - .Add("c|config=", "the configuration file to use (defaults to ccnet.conf)", delegate(string v) { consoleArgs.ConfigFile = v; }) - .Add("r|remoting=", "turn remoting on/off (defaults to on)", delegate(string v) { consoleArgs.UseRemoting = v == "on"; }) - .Add("p|project=", "the project to integrate (???)", delegate(string v) { consoleArgs.Project = v; }) - .Add("v|validate", "validate the configuration file and exit", delegate(string v) { consoleArgs.ValidateConfigOnly = v != null; }) - .Add("l|logging=", "turn logging on/off (defaults to on)", delegate(string v) { consoleArgs.Logging = v == "on"; }) - .Add("e|errorpause=", "turn pause on error on/off (defaults to on)", delegate(string v) {consoleArgs.PauseOnError = v == "on"; }); - - try - { - extra = opts.Parse(args); - } - catch (OptionException e) - { - System.Console.WriteLine(e.Message); - System.Console.WriteLine(e.StackTrace); - return 1; - } - - if(consoleArgs.ShowHelp) - { - DisplayHelp(opts); - return 0; - } - - try + ConsoleRunnerArguments consoleArgs = new ConsoleRunnerArguments(); + + if (consoleArgs.ShowHelp) { - runner = new ConsoleRunner(consoleArgs, new CruiseServerFactory()); - if (!usesShadowCopying) - { - Log.Warning("Shadow-copying has been turned off - hot-swapping will not work!"); - } + DisplayHelp(GetPopulatedOptionSet(consoleArgs)); + return 0; + } - runner.Run(); + return SetUpConsoleRunner(usesShadowCopying, consoleArgs); + } + + private int SetUpConsoleRunner(bool usesShadowCopying, ConsoleRunnerArguments consoleArgs) + { + try + { + StartConsoleRunner(usesShadowCopying, consoleArgs); return 0; } catch (Exception ex) { - Log.Error(ex); - if (consoleArgs.PauseOnError) - { - System.Console.WriteLine("An unexpected error has caused the console to crash, please press any key to continue..."); - System.Console.ReadKey(); - } + HandleAppRunnerException(consoleArgs.PauseOnError, ex); return 1; } finally @@ -83,6 +56,40 @@ public int Run(string[] args, bool usesShadowCopying) } } + private void HandleAppRunnerException(bool pauseOnError, Exception ex) + { + Log.Error(ex); + if (pauseOnError) + { + System.Console.WriteLine("An unexpected error has caused the console to crash, please press any key to continue..."); + System.Console.ReadKey(); + } + } + + private void StartConsoleRunner(bool usesShadowCopying, ConsoleRunnerArguments consoleArgs) + { + runner = new ConsoleRunner(consoleArgs, new CruiseServerFactory()); + if (!usesShadowCopying) + { + Log.Warning("Shadow-copying has been turned off - hot-swapping will not work!"); + } + + runner.Run(); + } + + private OptionSet GetPopulatedOptionSet(ConsoleRunnerArguments consoleArgs) + { + OptionSet opts = new OptionSet(); + opts.Add("h|?|help", "display this help screen", delegate (string v) { consoleArgs.ShowHelp = v != null; }) + .Add("c|config=", "the configuration file to use (defaults to ccnet.conf)", delegate (string v) { consoleArgs.ConfigFile = v; }) + .Add("r|remoting=", "turn remoting on/off (defaults to on)", delegate (string v) { consoleArgs.UseRemoting = v == "on"; }) + .Add("p|project=", "the project to integrate (???)", delegate (string v) { consoleArgs.Project = v; }) + .Add("v|validate", "validate the configuration file and exit", delegate (string v) { consoleArgs.ValidateConfigOnly = v != null; }) + .Add("l|logging=", "turn logging on/off (defaults to on)", delegate (string v) { consoleArgs.Logging = v == "on"; }) + .Add("e|errorpause=", "turn pause on error on/off (defaults to on)", delegate (string v) { consoleArgs.PauseOnError = v == "on"; }); + return opts; + } + #region InitializeLifetimeService() /// /// Initialise the lifetime service. @@ -113,35 +120,43 @@ public void Stop(string reason) } if (stopRunner) { - // Perform the actual stop + StopAppRunner(reason); + } + } + private void StopAppRunner(string reason) + { + try + { Log.Info("Stopping console: " + reason); - try - { - runner.Stop(); - } - catch (RemotingException) - { - // Sometimes this exception gets thrown and not sure why. - } + runner.Stop(); + } + catch (RemotingException) + { + // Sometimes this exception gets thrown and not sure why. } } - - private static void DisplayHelp(OptionSet opts) + + private void DisplayHelp(OptionSet opts) { Assembly thisApp = Assembly.GetExecutingAssembly(); Stream helpStream = thisApp.GetManifestResourceStream("ThoughtWorks.CruiseControl.Console.Help.txt"); try { - StreamReader reader = new StreamReader(helpStream); - string data = reader.ReadToEnd(); - reader.Close(); - System.Console.Write(data); + WriteHelpToConsole(helpStream); } finally - { + { helpStream.Close(); } - opts.WriteOptionDescriptions (System.Console.Out); + opts.WriteOptionDescriptions(System.Console.Out); + } + + private void WriteHelpToConsole(Stream helpStream) + { + StreamReader reader = new StreamReader(helpStream); + string data = reader.ReadToEnd(); + reader.Close(); + System.Console.Write(data); } } } diff --git a/project/console/ConsoleMain.cs b/project/console/ConsoleMain.cs index 68f2f4c40..f8bbdb3cf 100644 --- a/project/console/ConsoleMain.cs +++ b/project/console/ConsoleMain.cs @@ -1,98 +1,17 @@ using System; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Runtime.Remoting; -using System.Configuration; +using ThoughtWorks.CruiseControl.Core; namespace ThoughtWorks.CruiseControl.Console { - public class ConsoleMain - { + public class ConsoleMain + { private static object lockObject = new object(); - [STAThread] - internal static int Main(string[] args) - { - bool restart = true; - int result = 0; - DateTime restartTime = DateTime.MinValue; - using (FileSystemWatcher watcher = new FileSystemWatcher(AppDomain.CurrentDomain.BaseDirectory, "*.dll")) - { - AppRunner runner = null; - - // Start monitoring file changes - watcher.Changed += delegate(object sender, FileSystemEventArgs e) - { - if (!restart) - { - lock (lockObject) - { - try - { - runner.Stop("One or more DLLs have changed"); - } - catch (RemotingException) - { - // Sometimes this exception occurs - the lock statement should catch it, but... - } - } - } - restart = true; - restartTime = DateTime.Now.AddSeconds(10); - }; - watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size; - watcher.EnableRaisingEvents = true; - - // Begin the main application loop - while (restart) - { - restart = false; - - // Load the domain and start the runner - // Allow the user to turn shadow-copying off - var setting = ConfigurationManager.AppSettings["ShadowCopy"] ?? string.Empty; - var useShadowCopying = !(string.Equals(setting, "off", StringComparison.OrdinalIgnoreCase) || - string.Equals(setting, "false", StringComparison.OrdinalIgnoreCase)); - AppDomain runnerDomain; - try - { - runnerDomain = CreateNewDomain(useShadowCopying); - } - catch (FileLoadException) - { - // Unable to use shadow-copying (no user profile?), therefore turn off shadow-copying - useShadowCopying = false; - runnerDomain = CreateNewDomain(useShadowCopying); - } - runner = runnerDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, - typeof(AppRunner).FullName) as AppRunner; - result = runner.Run(args, useShadowCopying); - AppDomain.Unload(runnerDomain); - - // Allow any change events to finish (i.e. if multiple files are copied) - while (DateTime.Now < restartTime) - { - Thread.Sleep(500); - } - } - } - return result; - } - - /// - /// Creates the new runner domain. - /// - /// If set to true shadow copying will be used. - /// The new . - private static AppDomain CreateNewDomain(bool useShadowCopying) + [STAThread] + internal static int Main(string[] args) { - return AppDomain.CreateDomain( - "CC.Net", - null, - AppDomain.CurrentDomain.BaseDirectory, - AppDomain.CurrentDomain.RelativeSearchPath, - useShadowCopying); + MainRunner runner = new MainRunner(args, lockObject); + return runner.Run(); } } } diff --git a/project/console/MainRunner.cs b/project/console/MainRunner.cs new file mode 100644 index 000000000..dc606584a --- /dev/null +++ b/project/console/MainRunner.cs @@ -0,0 +1,146 @@ +using System; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Runtime.Remoting; +using System.Configuration; + +namespace ThoughtWorks.CruiseControl.Console +{ + public class MainRunner + { + private AppRunner runner; + private DateTime restartTime = DateTime.MinValue; + private object lockObject; + private string[] args; + private bool restart = true; + + public MainRunner(string[] _args, object _lockObject) + { + lockObject = _lockObject; + args = _args; + } + + private FileSystemWatcher GetWatcher() + { + FileSystemWatcher watcher = new FileSystemWatcher(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); + watcher.Changed += delegate (object sender, FileSystemEventArgs e) + { + if (!restart) + { + lock (lockObject) + { + try + { + runner.Stop("One or more DLLs have changed"); + } + catch (RemotingException) + { + // Sometimes this exception occurs - the lock statement should catch it, but... + } + } + } + restart = true; + restartTime = DateTime.Now.AddSeconds(10); + }; + + watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size; + watcher.EnableRaisingEvents = true; + + return watcher; + } + + public int Run() + { + int result = 0; + using (FileSystemWatcher watcher = GetWatcher()) + { + // Begin the main application loop + while (restart) + { + restart = false; + result = GenerateResult(); + + } + } + return result; + } + + private int GenerateResult() + { + AppDomain runnerDomain = GetRunnerDomain(ShouldUseShadowCopying()); + int result = GetRunResult(runnerDomain, ShouldUseShadowCopying()); + AppDomain.Unload(runnerDomain); + AllowChangeEventsToFinish(); + return result; + } + + private void AllowChangeEventsToFinish() + { + // Allow any change events to finish (i.e. if multiple files are copied) + while (DateTime.Now < restartTime) + { + Thread.Sleep(500); + } + } + + private int GetRunResult(AppDomain runnerDomain, bool useShadowCopying) + { + var result = GetRunner(runnerDomain).Run(args, useShadowCopying); + return result; + } + + private AppRunner GetRunner(AppDomain runnerDomain) + { + if (runner != null) + { + return runner; + } + else + { + runner = runnerDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(AppRunner).FullName) as AppRunner; + return runner; + } + } + + private AppDomain GetRunnerDomain(bool useShadowCopying) + { + AppDomain runnerDomain; + try + { + runnerDomain = CreateNewDomain(useShadowCopying); + } + catch (FileLoadException) + { + // Unable to use shadow-copying (no user profile?), therefore turn off shadow-copying + useShadowCopying = false; + runnerDomain = CreateNewDomain(useShadowCopying); + } + + return runnerDomain; + } + + private bool ShouldUseShadowCopying() + { + string setting = ConfigurationManager.AppSettings["ShadowCopy"] ?? string.Empty; + + return !(string.Equals(setting, "off", StringComparison.OrdinalIgnoreCase) || + string.Equals(setting, "false", StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Creates the new runner domain. + /// + /// If set to true shadow copying will be used. + /// The new . + private AppDomain CreateNewDomain(bool useShadowCopying) + { + return AppDomain.CreateDomain( + "CC.Net", + null, + AppDomain.CurrentDomain.BaseDirectory, + AppDomain.CurrentDomain.RelativeSearchPath, + useShadowCopying); + } + } +} diff --git a/project/console/console.csproj b/project/console/console.csproj index a156f3194..48488b4ab 100644 --- a/project/console/console.csproj +++ b/project/console/console.csproj @@ -141,6 +141,7 @@ + ccnet.config PreserveNewest