private bool reinitialize() { // If we are already initialized and the settings file hasn’t changed, we don’t need to do anything. var firstRunEver = _server == null; if (_server != null && File.GetLastWriteTimeUtc(_settingsPath) <= _settingsLastChangedTime) { return(false); } // This may load *and re-write* the settings file... var newSettings = PropellerUtil.LoadSettings(_settingsPath, firstRunEver ? new ConsoleLogger() : _log, firstRunEver); // ... so remember the file date/time stamp *after* the writing _settingsLastChangedTime = File.GetLastWriteTimeUtc(_settingsPath); _log = PropellerUtil.GetLogger(true, newSettings.LogFile, newSettings.LogVerbosity); _log.Info(firstRunEver ? "Initializing Propeller" : "Reinitializing Propeller"); // If either port number or the bind-to address have changed, stop and restart the server’s listener. var startListening = false; if (_server == null || CurrentSettings == null || !CurrentSettings.ServerOptions.Endpoints.Values.SequenceEqual(newSettings.ServerOptions.Endpoints.Values)) { var removed = CurrentSettings == null ? new HttpEndpoint[0] : CurrentSettings.ServerOptions.Endpoints.Values.Except(newSettings.ServerOptions.Endpoints.Values).ToArray(); var added = CurrentSettings == null?newSettings.ServerOptions.Endpoints.Values.ToArray() : newSettings.ServerOptions.Endpoints.Values.Except(CurrentSettings.ServerOptions.Endpoints.Values).ToArray(); if (_server == null || removed.Length > 0 || added.Length > 0) { if (removed.Length > 0) { _log.Info("Disabling {0}".Fmt(removed.Select(ep => "HTTP{0} on port {1}".Fmt(ep.Secure ? "S" : null, ep.Port)).JoinString(", ", lastSeparator: " and "))); } if (added.Length > 0) { _log.Info("Enabling {0}".Fmt(added.Select(ep => "HTTP{0} on port {1}".Fmt(ep.Secure ? "S" : null, ep.Port)).JoinString(", ", lastSeparator: " and "))); } if (_server == null) { _server = new HttpServer { Options = newSettings.ServerOptions, ErrorHandler = errorHandler, ResponseExceptionHandler = responseExceptionHandler } } ; else { _server.StopListening(); } startListening = true; } } CurrentSettings = newSettings; // Create a new instance of all the modules var newAppDomains = new HashSet <AppDomainInfo>(); foreach (var module in newSettings.Modules) { _log.Info("Initializing module: " + module.ModuleName); try { var inf = new AppDomainInfo(_log, newSettings, module, new SettingsSaver(s => { module.Settings = s; _settingsSavedByModule = true; })); newAppDomains.Add(inf); } catch (Exception e) { _log.Error("Failed to initialize module {0}:".Fmt(module.ModuleName)); _log.Exception(e); } } // Switcheroo! lock (_lockObject) { _log.Info("AppDomain Switcheroo"); _inactiveAppDomains.AddRange(_activeAppDomains); _activeAppDomains = newAppDomains; _server.Options = newSettings.ServerOptions; _server.Handler = createResolver().Handle; _server.Log = PropellerUtil.GetLogger(newSettings.HttpAccessLogToConsole, newSettings.HttpAccessLogFile, newSettings.HttpAccessLogVerbosity); if (startListening) { _server.StartListening(); } } // Delete any remaining temp folders no longer in use HashSet <string> tempFoldersInUse; lock (_lockObject) tempFoldersInUse = _activeAppDomains.Concat(_inactiveAppDomains).Select(ad => ad.TempPathUsed).ToHashSet(); foreach (var tempFolder in Directory.EnumerateDirectories(CurrentSettings.TempFolder ?? Path.GetTempPath(), "propeller-tmp-*")) { if (tempFoldersInUse.Contains(tempFolder)) { continue; } try { Directory.Delete(tempFolder, recursive: true); } catch { } } return(true); }
private static int _appDomainCount = 0; // only used to give each AppDomain a unique name public AppDomainInfo(LoggerBase log, PropellerSettings settings, PropellerModuleSettings moduleSettings, ISettingsSaver saver) { ModuleSettings = moduleSettings; Saver = saver; _log = log; _activeConnections = 0; // Determine the temporary folder that DLLs will be copied to var tempFolder = settings.TempFolder ?? Path.GetTempPath(); Directory.CreateDirectory(tempFolder); // Find a new folder to put the DLL/EXE files into int j = 1; do { TempPathUsed = Path.Combine(tempFolder, "propeller-tmp-" + (j++)); }while (Directory.Exists(TempPathUsed)); Directory.CreateDirectory(TempPathUsed); // Copy all the DLLs/EXEs to the temporary folder foreach (var sourceFile in new[] { typeof(PropellerEngine), typeof(IPropellerModule), typeof(HttpServer), typeof(Ut) }.Select(type => type.Assembly.Location).Concat( new[] { "*.exe", "*.dll", "*.pdb" }.SelectMany(ext => Directory.EnumerateFiles(Path.GetDirectoryName(moduleSettings.ModuleDll), ext)))) { var destFile = Path.Combine(TempPathUsed, Path.GetFileName(sourceFile)); if (File.Exists(destFile)) { _log.Warn(2, "Skipping file {0} because destination file {1} already exists.".Fmt(sourceFile, destFile)); } else { _log.Info(2, "Copying file {0} to {1}".Fmt(sourceFile, destFile)); File.Copy(sourceFile, destFile); } } // Create an AppDomain var setup = new AppDomainSetup { ApplicationBase = TempPathUsed, PrivateBinPath = TempPathUsed }; AppDomain = AppDomain.CreateDomain("Propeller AppDomain #{0}, module {1}".Fmt(_appDomainCount++, moduleSettings.ModuleName), null, setup); RunnerProxy = (AppDomainRunner)AppDomain.CreateInstanceAndUnwrap("Propeller", "RT.Propeller.AppDomainRunner"); RunnerProxy.Init( Path.Combine(TempPathUsed, Path.GetFileName(moduleSettings.ModuleDll)), moduleSettings.ModuleType, moduleSettings.ModuleName, moduleSettings.Settings, _log, saver); IEnumerable <string> filters = moduleSettings.MonitorFilters ?? Enumerable.Empty <string>(); if (RunnerProxy.FileFiltersToBeMonitoredForChanges != null) { filters = filters.Concat(RunnerProxy.FileFiltersToBeMonitoredForChanges); } foreach (var filter in filters.Concat(moduleSettings.ModuleDll)) { addFileSystemWatcher(Path.GetDirectoryName(filter), Path.GetFileName(filter)); } UrlMappings = moduleSettings.Hooks.Select(hook => new UrlMapping(hook, Handle, true)).ToArray(); _log.Info("Module {0} URLs: {1}".Fmt(moduleSettings.ModuleName, moduleSettings.Hooks.JoinString("; "))); }