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
This commit is contained in:
Renaud Paquay
2010-12-06 20:52:33 -08:00
parent 5f10d84ac2
commit 57ff31e8e5
8 changed files with 66 additions and 41 deletions

View File

@@ -2,8 +2,7 @@
namespace Orchard.Tests.Environment {
public class StubHostEnvironment : HostEnvironment {
public override void ResetSiteCompilation() {
public override void RestartAppDomain() {
}
}
}

View File

@@ -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."));
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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();

View File

@@ -24,7 +24,6 @@ namespace Orchard.Environment.Extensions {
public IList<Action> CopyActions { get; private set; }
public bool RestartAppDomain { get; set; }
public bool ResetSiteCompilation { get; set; }
/// <summary>
/// List of extensions (modules) present in the system

View File

@@ -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) {

View File

@@ -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();
}
}

View File

@@ -1,4 +1,5 @@
namespace Orchard.Environment {

namespace Orchard.Environment {
/// <summary>
/// Abstraction of the running environment
/// </summary>
@@ -9,6 +10,5 @@
bool IsAssemblyLoaded(string name);
void RestartAppDomain();
void ResetSiteCompilation();
}
}