mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
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:
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
namespace Orchard.Tests.Environment {
|
namespace Orchard.Tests.Environment {
|
||||||
public class StubHostEnvironment : HostEnvironment {
|
public class StubHostEnvironment : HostEnvironment {
|
||||||
public override void ResetSiteCompilation() {
|
public override void RestartAppDomain() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ namespace Orchard.Commands {
|
|||||||
|
|
||||||
public Localizer T { get; set; }
|
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."));
|
throw new OrchardCommandHostRetryException(T("A change of configuration requires the session to be restarted."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,89 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.Logging;
|
||||||
using Orchard.Mvc;
|
using Orchard.Mvc;
|
||||||
using Orchard.Services;
|
using Orchard.Services;
|
||||||
using Orchard.Utility.Extensions;
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.Environment
|
namespace Orchard.Environment {
|
||||||
{
|
public class DefaultHostEnvironment : HostEnvironment {
|
||||||
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 IClock _clock;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public DefaultHostEnvironment(IClock clock, IHttpContextAccessor httpContextAccessor) {
|
public DefaultHostEnvironment(IClock clock, IHttpContextAccessor httpContextAccessor) {
|
||||||
_clock = clock;
|
_clock = clock;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ResetSiteCompilation()
|
public Localizer T { get; set; }
|
||||||
{
|
public ILogger Logger { get; set; }
|
||||||
// Touch web.config
|
|
||||||
File.SetLastWriteTimeUtc(MapPath("~/web.config"), _clock.UtcNow);
|
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
|
// 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
|
// 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.
|
// new request will come to the newly started AppDomain.
|
||||||
var httpContext = _httpContextAccessor.Current();
|
var httpContext = _httpContextAccessor.Current();
|
||||||
if (httpContext != null)
|
if (httpContext != null) {
|
||||||
{
|
|
||||||
// Don't redirect posts...
|
// Don't redirect posts...
|
||||||
if (httpContext.Request.RequestType == "GET")
|
if (httpContext.Request.RequestType == "GET") {
|
||||||
{
|
|
||||||
httpContext.Response.Redirect(HttpContext.Current.Request.ToUrlString(), true /*endResponse*/);
|
httpContext.Response.Redirect(HttpContext.Current.Request.ToUrlString(), true /*endResponse*/);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
httpContext.Response.WriteFile(RefreshHtmlPath);
|
||||||
httpContext.Response.WriteFile("~/Refresh.html");
|
|
||||||
httpContext.Response.End();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -74,11 +74,6 @@ namespace Orchard.Environment.Extensions {
|
|||||||
Logger.Information("Done loading extensions...");
|
Logger.Information("Done loading extensions...");
|
||||||
|
|
||||||
// Very last step: Notify the host environment to restart the AppDomain if needed
|
// 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) {
|
if (context.RestartAppDomain) {
|
||||||
Logger.Information("AppDomain restart required.");
|
Logger.Information("AppDomain restart required.");
|
||||||
_hostEnvironment.RestartAppDomain();
|
_hostEnvironment.RestartAppDomain();
|
||||||
|
@@ -24,7 +24,6 @@ namespace Orchard.Environment.Extensions {
|
|||||||
public IList<Action> CopyActions { get; private set; }
|
public IList<Action> CopyActions { get; private set; }
|
||||||
|
|
||||||
public bool RestartAppDomain { get; set; }
|
public bool RestartAppDomain { get; set; }
|
||||||
public bool ResetSiteCompilation { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of extensions (modules) present in the system
|
/// List of extensions (modules) present in the system
|
||||||
|
@@ -76,19 +76,9 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) {
|
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) {
|
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) {
|
public override void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) {
|
||||||
|
@@ -17,10 +17,6 @@ namespace Orchard.Environment {
|
|||||||
return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => new AssemblyName(assembly.FullName).Name == name);
|
return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => new AssemblyName(assembly.FullName).Name == name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestartAppDomain() {
|
public abstract void RestartAppDomain();
|
||||||
ResetSiteCompilation();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void ResetSiteCompilation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
namespace Orchard.Environment {
|
|
||||||
|
namespace Orchard.Environment {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abstraction of the running environment
|
/// Abstraction of the running environment
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -9,6 +10,5 @@
|
|||||||
bool IsAssemblyLoaded(string name);
|
bool IsAssemblyLoaded(string name);
|
||||||
|
|
||||||
void RestartAppDomain();
|
void RestartAppDomain();
|
||||||
void ResetSiteCompilation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user