Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <summary>
- /// This module implements a safer way of loading the XML cache that avoids http://issues.umbraco.org/issue/U4-6626
- /// using a file system exclusive lock instead of a named semaphore.
- ///
- /// This module must be registered BEFORE <see cref="UmbracoModule" /> in web.config.
- /// </summary>
- public class XmlCacheSafeLoaderModule : IHttpModule
- {
- const int c_RetryDelaySeconds = 120;
- const string c_LockFileVirtualPath = "~/App_Data/TEMP/XmlCacheSafeLoader.lock";
- static readonly string[] s_AspNetHandlerExtensions = { ".aspx", ".ashx", ".asmx", ".axd", ".svc" };
- static readonly CancellationTokenSource s_ModuleExecutionCancellation = new CancellationTokenSource();
- public void Init(HttpApplication app)
- {
- app.BeginRequest += (sender, args) =>
- {
- var httpContext = new HttpContextWrapper(((HttpApplication)sender).Context);
- UmbracoContext.EnsureContext(
- httpContext,
- ApplicationContext.Current,
- new WebSecurity(httpContext, ApplicationContext.Current),
- true);
- };
- app.PostResolveRequestCache += (sender, e) =>
- {
- var httpContext = new HttpContextWrapper(((HttpApplication)sender).Context);
- ProcessRequest(httpContext);
- };
- }
- static void ProcessRequest(HttpContextBase httpContext)
- {
- if (s_ModuleExecutionCancellation.IsCancellationRequested
- || httpContext.Request.HttpMethod.ToUpperInvariant() != "GET"
- || IsClientSideRequest(httpContext.Request.Url)
- || IsDefaultBackOfficeRequest(httpContext.Request.Url))
- {
- return;
- }
- if (UmbracoContext.Current == null)
- {
- throw new InvalidOperationException(
- "The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext");
- }
- var lockFilePath = httpContext.Server.MapPath(c_LockFileVirtualPath);
- IDisposable fileSystemLock;
- try
- {
- fileSystemLock = File.Open(lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
- }
- catch (Exception ex)
- {
- var response = httpContext.Response;
- response.Clear();
- response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
- response.TrySkipIisCustomErrors = true;
- response.ContentType = "text/plain";
- LogHelper.Error<XmlCacheSafeLoaderModule>(
- string.Format(
- "Failed to acquire XML cache file system lock due to error [{0}].",
- ex.Message),
- ex);
- response.Write(
- string.Format(
- "Please wait until website restart is complete. This page will automatically refresh every {0} seconds.",
- c_RetryDelaySeconds));
- response.Headers["Retry-After"] = c_RetryDelaySeconds.ToString();
- response.Headers["Refresh"] = c_RetryDelaySeconds + ";URL=" + httpContext.Request.Url;
- response.Flush();
- response.End();
- return;
- }
- var stopwatch = Stopwatch.StartNew();
- try
- {
- using (fileSystemLock)
- {
- LogHelper.Warn<XmlCacheSafeLoaderModule>("Acquired file system lock.");
- ClosePossiblyOpenStuckSemaphoreLeftByUmbraco725();
- LogHelper.Warn<XmlCacheSafeLoaderModule>("Loading XML cache...");
- var contentInstance = content.Instance;
- // This should always return false because the XML is loaded in the content constructor
- // which is called when you get the singleton "Instance" property. But to protect from
- // future code changes in that class, we check it anyway.
- while (contentInstance.isInitializing)
- {
- Thread.Sleep(1000);
- }
- }
- LogHelper.Warn<XmlCacheSafeLoaderModule>(
- "XML cache load complete. Released file system lock after {0}",
- () => stopwatch.Elapsed);
- s_ModuleExecutionCancellation.Cancel();
- LogHelper.Info<XmlCacheSafeLoaderModule>("Module deactivated.");
- }
- catch (Exception ex)
- {
- LogHelper.Error<XmlCacheSafeLoaderModule>(
- "Failed to load the XML file. Released file system lock after " + stopwatch.Elapsed,
- ex);
- throw;
- }
- }
- // The name of the semaphore was found by decompiling the umbraco.content.InitializeFileLock() method.
- // This works as of Umbraco 7.2.5 but may be useless in the future.
- static void ClosePossiblyOpenStuckSemaphoreLeftByUmbraco725()
- {
- Semaphore stuckSemaphore;
- var semaphoreName = HostingEnvironment.ApplicationID + "/XmlStore/XmlFile";
- if (Semaphore.TryOpenExisting(semaphoreName, out stuckSemaphore))
- {
- stuckSemaphore.Dispose();
- LogHelper.Warn<XmlCacheSafeLoaderModule>("Closed left-over semaphore {0}", () => semaphoreName);
- }
- else
- {
- LogHelper.Info<XmlCacheSafeLoaderModule>(
- "Semaphore {0} was not found. Continuing...",
- () => semaphoreName);
- }
- }
- public void Dispose()
- {
- }
- // Copy of internal Umbraco.Core.UriExtensions.IsClientSideRequest :-(
- static bool IsClientSideRequest(Uri url)
- {
- string extension = Path.GetExtension(url.LocalPath);
- return !extension.IsNullOrWhiteSpace() && !s_AspNetHandlerExtensions.Any(s => extension.InvariantEquals(s));
- }
- // Copy of internal Umbraco.Core.UriExtensions.IsDefaultBackOfficeRequest :-(
- static bool IsDefaultBackOfficeRequest(Uri url)
- {
- return url.AbsolutePath.InvariantEquals(GlobalSettings.Path.TrimEnd("/"))
- || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/'))
- || (url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default")
- || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default/"));
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement