From 57ff31e8e52d1849ac29f05db4a23b9b2e961e6a Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Mon, 6 Dec 2010 20:52:33 -0800 Subject: [PATCH] Revising how Orchard restarts the AppDomain Orchard used to write to the "~/web.config" file to force an AppDomain restart needed in some case of dynamic compilation. Revised the policy to be like this: 1. If full trust, use HttpRuntime.UnloadAppDomain() 2. If Medium Trust, write to "~/bin/HostRestart/marker.txt" 3. If Medium Trust and 2. failed, write to "~/web.config" 4. If Medium Trust and 2.+3. failed, give an error message to the user with appropriate measures to take on the web server. Also removed a now unused "ResetSiteCompilation" method --HG-- branch : dev --- .../Environment/StubHostEnvironment.cs | 3 +- .../Commands/CommandHostEnvironment.cs | 2 +- .../Environment/DefaultHostEnvironment.cs | 76 +++++++++++++++---- .../Extensions/ExtensionLoaderCoordinator.cs | 5 -- .../Extensions/ExtensionLoadingContext.cs | 1 - .../Loaders/DynamicExtensionLoader.cs | 10 --- src/Orchard/Environment/HostEnvironment.cs | 6 +- src/Orchard/Environment/IHostEnvironment.cs | 4 +- 8 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/Orchard.Tests/Environment/StubHostEnvironment.cs b/src/Orchard.Tests/Environment/StubHostEnvironment.cs index bfac75427..b5f530518 100644 --- a/src/Orchard.Tests/Environment/StubHostEnvironment.cs +++ b/src/Orchard.Tests/Environment/StubHostEnvironment.cs @@ -2,8 +2,7 @@ namespace Orchard.Tests.Environment { public class StubHostEnvironment : HostEnvironment { - public override void ResetSiteCompilation() { - + public override void RestartAppDomain() { } } } diff --git a/src/Orchard/Commands/CommandHostEnvironment.cs b/src/Orchard/Commands/CommandHostEnvironment.cs index 0fc4c4ae8..b7981deeb 100644 --- a/src/Orchard/Commands/CommandHostEnvironment.cs +++ b/src/Orchard/Commands/CommandHostEnvironment.cs @@ -9,7 +9,7 @@ namespace Orchard.Commands { public Localizer T { get; set; } - public override void ResetSiteCompilation() { + public override void RestartAppDomain() { throw new OrchardCommandHostRetryException(T("A change of configuration requires the session to be restarted.")); } } diff --git a/src/Orchard/Environment/DefaultHostEnvironment.cs b/src/Orchard/Environment/DefaultHostEnvironment.cs index 2412341a9..2f198405a 100644 --- a/src/Orchard/Environment/DefaultHostEnvironment.cs +++ b/src/Orchard/Environment/DefaultHostEnvironment.cs @@ -1,43 +1,89 @@ using System.IO; using System.Web; +using Orchard.Localization; +using Orchard.Logging; using Orchard.Mvc; using Orchard.Services; using Orchard.Utility.Extensions; -namespace Orchard.Environment -{ - public class DefaultHostEnvironment : HostEnvironment - { +namespace Orchard.Environment { + public class DefaultHostEnvironment : HostEnvironment { + private const string WebConfigPath = "~/web.config"; + private const string RefreshHtmlPath = "~/refresh.html"; + private const string HostRestartPath = "~/bin/HostRestart"; private readonly IClock _clock; private readonly IHttpContextAccessor _httpContextAccessor; public DefaultHostEnvironment(IClock clock, IHttpContextAccessor httpContextAccessor) { _clock = clock; _httpContextAccessor = httpContextAccessor; + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; } - public override void ResetSiteCompilation() - { - // Touch web.config - File.SetLastWriteTimeUtc(MapPath("~/web.config"), _clock.UtcNow); + public Localizer T { get; set; } + public ILogger Logger { get; set; } + + public override void RestartAppDomain() { + if (IsFullTrust) { + HttpRuntime.UnloadAppDomain(); + } + else { + bool success = TryWriteBinFolder() || TryWriteWebConfig(); + + if (!success) { + throw new OrchardException( + T("Orchard needs to be restarted due to a configuration change, but was unable to do so.\r\n" + + "To prevent this issue in the future, a change to the web server configuration is required:\r\n" + + "- run the application in a full trust environment, or\r\n" + + "- give the application write access to the '{0}' folder, or\r\n" + + "- give the application write access to the '{1}' file.", + HostRestartPath, WebConfigPath)); + } + } // If setting up extensions/modules requires an AppDomain restart, it's very unlikely the // current request can be processed correctly. So, we redirect to the same URL, so that the // new request will come to the newly started AppDomain. var httpContext = _httpContextAccessor.Current(); - if (httpContext != null) - { + if (httpContext != null) { // Don't redirect posts... - if (httpContext.Request.RequestType == "GET") - { + if (httpContext.Request.RequestType == "GET") { httpContext.Response.Redirect(HttpContext.Current.Request.ToUrlString(), true /*endResponse*/); } - else - { - httpContext.Response.WriteFile("~/Refresh.html"); + else { + httpContext.Response.WriteFile(RefreshHtmlPath); httpContext.Response.End(); } } } + + private bool TryWriteWebConfig() { + try { + // In medium trust, "UnloadAppDomain" is not supported. Touch web.config + // to force an AppDomain restart. + File.SetLastWriteTimeUtc(MapPath(WebConfigPath), _clock.UtcNow); + return true; + } + catch { + return false; + } + } + + private bool TryWriteBinFolder() { + try { + var binMarker = MapPath(HostRestartPath); + Directory.CreateDirectory(binMarker); + + using (var stream = File.CreateText(Path.Combine(binMarker, "marker.txt"))) { + stream.WriteLine("Restart on '{0}'", _clock.UtcNow); + stream.Flush(); + } + return true; + } + catch { + return false; + } + } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs b/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs index 391985c5f..2d70ac83a 100644 --- a/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs +++ b/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs @@ -74,11 +74,6 @@ namespace Orchard.Environment.Extensions { Logger.Information("Done loading extensions..."); // Very last step: Notify the host environment to restart the AppDomain if needed - if (context.ResetSiteCompilation) { - Logger.Information("Reset site compilation state required."); - _hostEnvironment.ResetSiteCompilation(); - } - if (context.RestartAppDomain) { Logger.Information("AppDomain restart required."); _hostEnvironment.RestartAppDomain(); diff --git a/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs b/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs index bb9a11af5..06a086018 100644 --- a/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs +++ b/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs @@ -24,7 +24,6 @@ namespace Orchard.Environment.Extensions { public IList CopyActions { get; private set; } public bool RestartAppDomain { get; set; } - public bool ResetSiteCompilation { get; set; } /// /// List of extensions (modules) present in the system diff --git a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs index c5fc9eea5..2dd572fc5 100644 --- a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs @@ -76,19 +76,9 @@ namespace Orchard.Environment.Extensions.Loaders { } public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) { - // Since a dynamic assembly is not active anymore, we need to notify ASP.NET - // that a new site compilation is needed (since ascx files may be referencing - // this now removed extension). - Logger.Information("ExtensionRemoved: Module \"{0}\" has been removed, forcing site recompilation", dependency.Name); - ctx.ResetSiteCompilation = true; } public override void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { - // Since a dynamic assembly is not active anymore, we need to notify ASP.NET - // that a new site compilation is needed (since ascx files may be referencing - // this now removed extension). - Logger.Information("ExtensionDeactivated: Module \"{0}\" has been de-activated, forcing site recompilation", extension.Id); - ctx.ResetSiteCompilation = true; } public override void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { diff --git a/src/Orchard/Environment/HostEnvironment.cs b/src/Orchard/Environment/HostEnvironment.cs index 7cc96ecc9..e185944fa 100644 --- a/src/Orchard/Environment/HostEnvironment.cs +++ b/src/Orchard/Environment/HostEnvironment.cs @@ -17,10 +17,6 @@ namespace Orchard.Environment { return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => new AssemblyName(assembly.FullName).Name == name); } - public void RestartAppDomain() { - ResetSiteCompilation(); - } - - public abstract void ResetSiteCompilation(); + public abstract void RestartAppDomain(); } } \ No newline at end of file diff --git a/src/Orchard/Environment/IHostEnvironment.cs b/src/Orchard/Environment/IHostEnvironment.cs index 8b6d668bf..5f61bfa73 100644 --- a/src/Orchard/Environment/IHostEnvironment.cs +++ b/src/Orchard/Environment/IHostEnvironment.cs @@ -1,4 +1,5 @@ -namespace Orchard.Environment { + +namespace Orchard.Environment { /// /// Abstraction of the running environment /// @@ -9,6 +10,5 @@ bool IsAssemblyLoaded(string name); void RestartAppDomain(); - void ResetSiteCompilation(); } } \ No newline at end of file