Moves the event handling logic to individual components, no more public "Install" methods.

This commit is contained in:
Shannon
2021-01-20 18:48:18 +11:00
parent 166b86a007
commit 17078b59c5
4 changed files with 93 additions and 72 deletions

View File

@@ -92,6 +92,8 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
// would automatically just register for all implemented INotificationHandler{T}?
builder.AddNotificationHandler<UmbracoApplicationStarting, ModelsBuilderNotificationHandler>();
builder.AddNotificationHandler<ServerVariablesParsing, ModelsBuilderNotificationHandler>();
builder.AddNotificationHandler<UmbracoApplicationStarting, LiveModelsProvider>();
builder.AddNotificationHandler<UmbracoApplicationStarting, OutOfDateModelsStatus>();
builder.Services.AddUnique<ModelsGenerator>();
builder.Services.AddUnique<LiveModelsProvider>();
builder.Services.AddUnique<OutOfDateModelsStatus>();

View File

@@ -1,19 +1,22 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Extensions;
using Umbraco.ModelsBuilder.Embedded.Building;
using Umbraco.Web.Cache;
using Umbraco.Web.Common.Lifetime;
namespace Umbraco.ModelsBuilder.Embedded
{
// supports LiveAppData - but not PureLive
public sealed class LiveModelsProvider
public sealed class LiveModelsProvider : INotificationHandler<UmbracoApplicationStarting>
{
private static Mutex s_mutex;
private static int s_req;
@@ -22,30 +25,49 @@ namespace Umbraco.ModelsBuilder.Embedded
private readonly ModelsGenerator _modelGenerator;
private readonly ModelsGenerationError _mbErrors;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IUmbracoRequestLifetime _umbracoRequestLifetime;
/// <summary>
/// Initializes a new instance of the <see cref="LiveModelsProvider"/> class.
/// </summary>
public LiveModelsProvider(ILogger<LiveModelsProvider> logger, IOptions<ModelsBuilderSettings> config, ModelsGenerator modelGenerator, ModelsGenerationError mbErrors, IHostingEnvironment hostingEnvironment)
public LiveModelsProvider(
ILogger<LiveModelsProvider> logger,
IOptions<ModelsBuilderSettings> config,
ModelsGenerator modelGenerator,
ModelsGenerationError mbErrors,
IHostingEnvironment hostingEnvironment,
IUmbracoRequestLifetime umbracoRequestLifetime)
{
_logger = logger;
_config = config.Value ?? throw new ArgumentNullException(nameof(config));
_modelGenerator = modelGenerator;
_mbErrors = mbErrors;
_hostingEnvironment = hostingEnvironment;
_umbracoRequestLifetime = umbracoRequestLifetime;
}
// we do not manage pure live here
internal bool IsEnabled => _config.ModelsMode.IsLiveNotPure();
internal void Install()
/// <summary>
/// Handles the <see cref="UmbracoApplicationStarting"/> notification
/// </summary>
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
{
// just be sure
Install();
return Task.CompletedTask;
}
private void Install()
{
// don't run if not enabled
if (!IsEnabled)
{
return;
}
_umbracoRequestLifetime.RequestEnd += (sender, context) => AppEndRequest(context);
// initialize mutex
// ApplicationId will look like "/LM/W3SVC/1/Root/AppName"
// name is system-wide and must be less than 260 chars
@@ -56,34 +78,27 @@ namespace Umbraco.ModelsBuilder.Embedded
// anything changes, and we want to re-generate models.
ContentTypeCacheRefresher.CacheUpdated += RequestModelsGeneration;
DataTypeCacheRefresher.CacheUpdated += RequestModelsGeneration;
// at the end of a request since we're restarting the pool
// NOTE - this does NOT trigger - see module below
//umbracoApplication.EndRequest += GenerateModelsIfRequested;
}
// NOTE
// Using HttpContext Items fails because CacheUpdated triggers within
// some asynchronous backend task where we seem to have no HttpContext.
// So we use a static (non request-bound) var to register that models
// CacheUpdated triggers within some asynchronous backend task where
// we have no HttpContext. So we use a static (non request-bound)
// var to register that models
// need to be generated. Could be by another request. Anyway. We could
// have collisions but... you know the risk.
private void RequestModelsGeneration(object sender, EventArgs args)
{
//HttpContext.Current.Items[this] = true;
_logger.LogDebug("Requested to generate models.");
Interlocked.Exchange(ref s_req, 1);
}
public void GenerateModelsIfRequested()
private void GenerateModelsIfRequested()
{
//if (HttpContext.Current.Items[this] == null) return;
if (Interlocked.Exchange(ref s_req, 0) == 0) return;
// cannot use a simple lock here because we don't want another AppDomain
// to generate while we do... and there could be 2 AppDomains if the app restarts.
if (Interlocked.Exchange(ref s_req, 0) == 0)
{
return;
}
try
{
@@ -91,7 +106,7 @@ namespace Umbraco.ModelsBuilder.Embedded
const int timeout = 2 * 60 * 1000; // 2 mins
s_mutex.WaitOne(timeout); // wait until it is safe, and acquire
_logger.LogInformation("Generate models now.");
GenerateModels();
_modelGenerator.GenerateModels();
_mbErrors.Clear();
_logger.LogInformation("Generated.");
}
@@ -110,13 +125,7 @@ namespace Umbraco.ModelsBuilder.Embedded
}
}
private void GenerateModels()
{
// EnableDllModels will recycle the app domain - but this request will end properly
_modelGenerator.GenerateModels();
}
public void AppEndRequest(HttpContext context)
private void AppEndRequest(HttpContext context)
{
if (context.Request.IsClientSideRequest())
{

View File

@@ -5,7 +5,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using Umbraco.Configuration;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Events;
@@ -16,7 +15,6 @@ using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Extensions;
using Umbraco.ModelsBuilder.Embedded.BackOffice;
using Umbraco.Web.Common.Lifetime;
using Umbraco.Web.Common.ModelBinders;
using Umbraco.Web.WebAssets;
@@ -30,28 +28,19 @@ namespace Umbraco.ModelsBuilder.Embedded
{
private readonly ModelsBuilderSettings _config;
private readonly IShortStringHelper _shortStringHelper;
private readonly LiveModelsProvider _liveModelsProvider;
private readonly OutOfDateModelsStatus _outOfDateModels;
private readonly LinkGenerator _linkGenerator;
private readonly IUmbracoRequestLifetime _umbracoRequestLifetime;
private readonly ContentModelBinder _modelBinder;
public ModelsBuilderNotificationHandler(
IOptions<ModelsBuilderSettings> config,
IShortStringHelper shortStringHelper,
LiveModelsProvider liveModelsProvider,
OutOfDateModelsStatus outOfDateModels,
LinkGenerator linkGenerator,
IUmbracoRequestLifetime umbracoRequestLifetime,
ContentModelBinder modelBinder)
{
_config = config.Value;
_shortStringHelper = shortStringHelper;
_liveModelsProvider = liveModelsProvider;
_outOfDateModels = outOfDateModels;
_shortStringHelper = shortStringHelper;
_linkGenerator = linkGenerator;
_umbracoRequestLifetime = umbracoRequestLifetime;
_modelBinder = modelBinder;
}
@@ -62,8 +51,6 @@ namespace Umbraco.ModelsBuilder.Embedded
{
// always setup the dashboard
// note: UmbracoApiController instances are automatically registered
_umbracoRequestLifetime.RequestEnd += (sender, context) => _liveModelsProvider.AppEndRequest(context);
_modelBinder.ModelBindingException += ContentModelBinder_ModelBindingException;
if (_config.ModelsMode != ModelsMode.Nothing)
@@ -71,16 +58,6 @@ namespace Umbraco.ModelsBuilder.Embedded
FileService.SavingTemplate += FileService_SavingTemplate;
}
if (_config.ModelsMode.IsLiveNotPure())
{
_liveModelsProvider.Install();
}
if (_config.FlagOutOfDateModels)
{
_outOfDateModels.Install();
}
return Task.CompletedTask;
}
@@ -89,7 +66,7 @@ namespace Umbraco.ModelsBuilder.Embedded
/// </summary>
public Task HandleAsync(ServerVariablesParsing notification, CancellationToken cancellationToken)
{
var serverVars = notification.ServerVariables;
IDictionary<string, object> serverVars = notification.ServerVariables;
if (!serverVars.ContainsKey("umbracoUrls"))
{

View File

@@ -1,13 +1,16 @@
using System.IO;
using System.IO;
using Microsoft.Extensions.Options;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Configuration.Models;
using Umbraco.Web.Cache;
using Umbraco.Core.Events;
using System.Threading.Tasks;
using System.Threading;
namespace Umbraco.ModelsBuilder.Embedded
{
public sealed class OutOfDateModelsStatus
public sealed class OutOfDateModelsStatus : INotificationHandler<UmbracoApplicationStarting>
{
private readonly ModelsBuilderSettings _config;
private readonly IHostingEnvironment _hostingEnvironment;
@@ -18,11 +21,38 @@ namespace Umbraco.ModelsBuilder.Embedded
_hostingEnvironment = hostingEnvironment;
}
internal void Install()
public bool IsEnabled => _config.FlagOutOfDateModels;
public bool IsOutOfDate
{
// just be sure
if (_config.FlagOutOfDateModels == false)
get
{
if (_config.FlagOutOfDateModels == false)
{
return false;
}
var path = GetFlagPath();
return path != null && File.Exists(path);
}
}
/// <summary>
/// Handles the <see cref="UmbracoApplicationStarting"/> notification
/// </summary>
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
{
Install();
return Task.CompletedTask;
}
private void Install()
{
// don't run if not configured
if (!IsEnabled)
{
return;
}
ContentTypeCacheRefresher.CacheUpdated += (sender, args) => Write();
DataTypeCacheRefresher.CacheUpdated += (sender, args) => Write();
@@ -32,35 +62,38 @@ namespace Umbraco.ModelsBuilder.Embedded
{
var modelsDirectory = _config.ModelsDirectoryAbsolute(_hostingEnvironment);
if (!Directory.Exists(modelsDirectory))
{
Directory.CreateDirectory(modelsDirectory);
}
return Path.Combine(modelsDirectory, "ood.flag");
}
private void Write()
{
var path = GetFlagPath();
if (path == null || File.Exists(path)) return;
if (path == null || File.Exists(path))
{
return;
}
File.WriteAllText(path, "THIS FILE INDICATES THAT MODELS ARE OUT-OF-DATE\n\n");
}
public void Clear()
{
if (_config.FlagOutOfDateModels == false) return;
var path = GetFlagPath();
if (path == null || !File.Exists(path)) return;
File.Delete(path);
}
public bool IsEnabled => _config.FlagOutOfDateModels;
public bool IsOutOfDate
{
get
if (_config.FlagOutOfDateModels == false)
{
if (_config.FlagOutOfDateModels == false) return false;
var path = GetFlagPath();
return path != null && File.Exists(path);
return;
}
}
var path = GetFlagPath();
if (path == null || !File.Exists(path))
{
return;
}
File.Delete(path);
}
}
}