Remove CoreInitialComponent

This commit is contained in:
Paul Johnson
2020-12-15 15:20:36 +00:00
parent 0151c435f1
commit a69739a7a2
13 changed files with 111 additions and 173 deletions

View File

@@ -0,0 +1,16 @@
namespace Umbraco.Core.Events
{
public class UmbracoApplicationStarting : INotification
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoApplicationStarting"/> class.
/// </summary>
/// <param name="runtimeLevel">The runtime level</param>
public UmbracoApplicationStarting(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel;
/// <summary>
/// Gets the runtime level of execution.
/// </summary>
public RuntimeLevel RuntimeLevel { get; }
}
}

View File

@@ -0,0 +1,4 @@
namespace Umbraco.Core.Events
{
public class UmbracoApplicationStopping : INotification { }
}

View File

@@ -1,22 +1,15 @@
using System;
using Microsoft.Extensions.Hosting;
namespace Umbraco.Core
{
/// <summary>
/// Defines the Umbraco runtime.
/// </summary>
public interface IRuntime
public interface IRuntime : IHostedService
{
/// <summary>
/// Gets the runtime state.
/// </summary>
IRuntimeState State { get; }
void Start();
/// <summary>
/// Terminates the runtime.
/// </summary>
void Terminate();
}
}

View File

@@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.8" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Examine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -38,6 +38,7 @@ using Umbraco.Core.Templates;
using Umbraco.Examine;
using Umbraco.Infrastructure.Examine;
using Umbraco.Infrastructure.Media;
using Umbraco.Infrastructure.Runtime;
using Umbraco.Web;
using Umbraco.Web.Actions;
using Umbraco.Web.Cache;
@@ -66,11 +67,12 @@ namespace Umbraco.Core.Runtime
{
// core's initial composer composes before all core composers
[ComposeBefore(typeof(ICoreComposer))]
public class CoreInitialComposer : ComponentComposer<CoreInitialComponent>
public class CoreInitialComposer : IComposer
{
public override void Compose(IUmbracoBuilder builder)
/// <inheritdoc/>
public void Compose(IUmbracoBuilder builder)
{
base.Compose(builder);
builder.AddNotificationHandler<UmbracoApplicationStarting, EssentialDirectoryCreator>();
// composers
builder

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
@@ -10,71 +13,102 @@ namespace Umbraco.Infrastructure.Runtime
{
public class CoreRuntime : IRuntime
{
public IRuntimeState State { get; }
private readonly ILogger<CoreRuntime> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly ComponentCollection _components;
private readonly IApplicationShutdownRegistry _applicationShutdownRegistry;
private readonly IProfilingLogger _profilingLogger;
private readonly IMainDom _mainDom;
private readonly IUmbracoDatabaseFactory _databaseFactory;
private readonly IEventAggregator _eventAggregator;
private readonly IHostingEnvironment _hostingEnvironment;
public CoreRuntime(
ILogger<CoreRuntime> logger,
ILoggerFactory loggerFactory,
IRuntimeState state,
ComponentCollection components,
IApplicationShutdownRegistry applicationShutdownRegistry,
IProfilingLogger profilingLogger,
IMainDom mainDom,
IUmbracoDatabaseFactory databaseFactory)
IUmbracoDatabaseFactory databaseFactory,
IEventAggregator eventAggregator,
IHostingEnvironment hostingEnvironment)
{
State = state;
_logger = logger;
_loggerFactory = loggerFactory;
_components = components;
_applicationShutdownRegistry = applicationShutdownRegistry;
_profilingLogger = profilingLogger;
_mainDom = mainDom;
_databaseFactory = databaseFactory;
}
_eventAggregator = eventAggregator;
_hostingEnvironment = hostingEnvironment;
public void Start()
_logger = _loggerFactory.CreateLogger<CoreRuntime>();
}
/// <summary>
/// Gets the state of the Umbraco runtime.
/// </summary>
public IRuntimeState State { get; }
/// <inheritdoc/>
public async Task StartAsync(CancellationToken cancellationToken)
{
StaticApplicationLogging.Initialize(_loggerFactory);
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
{
var exception = (Exception)args.ExceptionObject;
var isTerminating = args.IsTerminating; // always true?
var msg = "Unhandled exception in AppDomain";
if (isTerminating) msg += " (terminating)";
if (isTerminating)
{
msg += " (terminating)";
}
msg += ".";
_logger.LogError(exception, msg);
};
AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data));
DetermineRuntimeLevel();
if (State.Level <= RuntimeLevel.BootFailed)
{
throw new InvalidOperationException($"Cannot start the runtime if the runtime level is less than or equal to {RuntimeLevel.BootFailed}");
}
var hostingEnvironmentLifetime = _applicationShutdownRegistry;
IApplicationShutdownRegistry hostingEnvironmentLifetime = _applicationShutdownRegistry;
if (hostingEnvironmentLifetime == null)
throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(Start)}");
{
throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}");
}
// acquire the main domain - if this fails then anything that should be registered with MainDom will not operate
AcquireMainDom();
await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken);
// create & initialize the components
_components.Initialize();
}
public void Terminate()
public async Task StopAsync(CancellationToken cancellationToken)
{
_components.Terminate();
await _eventAggregator.PublishAsync(new UmbracoApplicationStopping(), cancellationToken);
StaticApplicationLogging.Initialize(null);
}
private void AcquireMainDom()
{
using (var timer = _profilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired."))
using (DisposableTimer timer = _profilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired."))
{
try
{
@@ -90,7 +124,7 @@ namespace Umbraco.Infrastructure.Runtime
private void DetermineRuntimeLevel()
{
using var timer = _profilingLogger.DebugDuration<CoreRuntime>("Determining runtime level.", "Determined.");
using DisposableTimer timer = _profilingLogger.DebugDuration<CoreRuntime>("Determining runtime level.", "Determined.");
try
{

View File

@@ -1,25 +1,28 @@
using Microsoft.Extensions.Options;
using Umbraco.Core.Composing;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
namespace Umbraco.Core.Runtime
namespace Umbraco.Infrastructure.Runtime
{
public class CoreInitialComponent : IComponent
public class EssentialDirectoryCreator : INotificationHandler<UmbracoApplicationStarting>
{
private readonly IIOHelper _ioHelper;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly GlobalSettings _globalSettings;
public CoreInitialComponent(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions<GlobalSettings> globalSettings)
public EssentialDirectoryCreator(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions<GlobalSettings> globalSettings)
{
_ioHelper = ioHelper;
_hostingEnvironment = hostingEnvironment;
_globalSettings = globalSettings.Value;
}
public void Initialize()
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
{
// ensure we have some essential directories
// every other component can then initialize safely
@@ -28,9 +31,8 @@ namespace Umbraco.Core.Runtime
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews));
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews));
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials));
}
public void Terminate()
{ }
return Task.CompletedTask;
}
}
}

View File

@@ -33,54 +33,6 @@ namespace Umbraco.Tests.Integration
MyComposer.Reset();
}
/// <summary>
/// Calling AddUmbracoCore to configure the container
/// </summary>
[Test]
public async Task AddUmbracoCore()
{
var testHelper = new TestHelper();
var hostBuilder = new HostBuilder()
.UseUmbraco()
.ConfigureServices((hostContext, services) =>
{
var webHostEnvironment = testHelper.GetWebHostEnvironment();
services.AddSingleton(testHelper.DbProviderFactoryCreator);
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
// Add it!
var typeLoader = services.AddTypeLoader(
GetType().Assembly,
webHostEnvironment,
testHelper.GetHostingEnvironment(),
testHelper.ConsoleLoggerFactory,
AppCaches.NoCache,
hostContext.Configuration,
testHelper.Profiler);
var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, testHelper.ConsoleLoggerFactory);
builder.Services.AddUnique<AppCaches>(AppCaches.NoCache);
builder.AddConfiguration();
builder.AddUmbracoCore();
});
var host = await hostBuilder.StartAsync();
var app = new ApplicationBuilder(host.Services);
// assert results
var runtimeState = app.ApplicationServices.GetRequiredService<IRuntimeState>();
var mainDom = app.ApplicationServices.GetRequiredService<IMainDom>();
Assert.IsFalse(mainDom.IsMainDom); // We haven't "Started" the runtime yet
Assert.IsNull(runtimeState.BootFailedException);
Assert.IsFalse(MyComponent.IsInit); // We haven't "Started" the runtime yet
await host.StopAsync();
Assert.IsFalse(MyComponent.IsTerminated); // we didn't "Start" the runtime so nothing was registered for shutdown
}
/// <summary>
/// Calling AddUmbracoCore to configure the container and UseUmbracoCore to start the runtime
/// </summary>

View File

@@ -1,4 +1,4 @@

using System;
using System.Linq.Expressions;
using System.Net.Http;
@@ -119,15 +119,6 @@ namespace Umbraco.Tests.Integration.TestServerTest
protected LinkGenerator LinkGenerator { get; private set; }
protected WebApplicationFactory<UmbracoTestServerTestBase> Factory { get; private set; }
[TearDown]
public override void TearDown()
{
base.TearDown();
base.TerminateCoreRuntime();
Factory.Dispose();
}
#region IStartup
public override void ConfigureServices(IServiceCollection services)
@@ -162,7 +153,5 @@ namespace Umbraco.Tests.Integration.TestServerTest
}
#endregion
}
}

View File

@@ -71,9 +71,14 @@ namespace Umbraco.Tests.Integration.Testing
foreach (var a in _testTeardown)
a();
}
_testTeardown = null;
FirstTestInFixture = false;
FirstTestInSession = false;
// Ensure CoreRuntime stopped (now it's a HostedService)
IHost host = Services.GetRequiredService<IHost>();
host.StopAsync().GetAwaiter().GetResult();
}
[TearDown]
@@ -100,8 +105,6 @@ namespace Umbraco.Tests.Integration.Testing
var app = new ApplicationBuilder(host.Services);
Configure(app);
OnFixtureTearDown(() => host.Dispose());
}
#region Generic Host Builder and Runtime
@@ -135,6 +138,8 @@ namespace Umbraco.Tests.Integration.Testing
/// <returns></returns>
public virtual IHostBuilder CreateHostBuilder()
{
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
var hostBuilder = Host.CreateDefaultBuilder()
// IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work
// if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will
@@ -154,6 +159,12 @@ namespace Umbraco.Tests.Integration.Testing
{
services.AddTransient(_ => CreateLoggerFactory());
ConfigureServices(services);
if (!testOptions.Boot)
{
// If boot is false, we don't want the CoreRuntime hosted service to start
services.AddUnique(Mock.Of<IRuntime>());
}
});
return hostBuilder;
}
@@ -215,27 +226,9 @@ namespace Umbraco.Tests.Integration.Testing
{
UseTestLocalDb(app.ApplicationServices);
//get the currently set options
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
if (testOptions.Boot)
{
Services.GetRequiredService<IBackOfficeSecurityFactory>().EnsureBackOfficeSecurity();
Services.GetRequiredService<IUmbracoContextFactory>().EnsureUmbracoContext();
app.UseUmbracoCore(); // Takes 200 ms
OnTestTearDown(TerminateCoreRuntime);
}
}
/// <remarks>
/// Some IComponents hook onto static events (e.g. Published in ContentService)
/// If these fire after the components host has been shutdown, errors can occur.
/// If CoreRuntime.Start() is called We also need to de-register the events.
/// </remarks>
protected void TerminateCoreRuntime()
{
Services.GetRequiredService<IRuntime>().Terminate();
StaticApplicationLogging.Initialize(null);
Services.GetRequiredService<IBackOfficeSecurityFactory>().EnsureBackOfficeSecurity();
Services.GetRequiredService<IUmbracoContextFactory>().EnsureUmbracoContext();
app.UseUmbracoCore();
}
#endregion

View File

@@ -143,11 +143,13 @@ namespace Umbraco.Core.DependencyInjection
builder.Services.AddUnique<IUmbracoDatabase>(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().CreateDatabase());
builder.Services.AddUnique<ISqlContext>(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().SqlContext);
builder.Services.AddUnique<IUmbracoVersion, UmbracoVersion>();
builder.Services.AddUnique<IRuntime, CoreRuntime>();
builder.Services.AddUnique<IRuntimeState, RuntimeState>();
builder.Services.AddUnique<IHostingEnvironment, AspNetCoreHostingEnvironment>();
builder.Services.AddUnique<IMainDom, MainDom>();
builder.Services.AddUnique<IRuntime, CoreRuntime>();
builder.Services.AddHostedService(factory => factory.GetRequiredService<IRuntime>());
builder.AddComposers();
return builder;

View File

@@ -1,13 +1,11 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using Smidge;
using Smidge.Nuglify;
using StackExchange.Profiling;
using Umbraco.Core;
using Umbraco.Core.Hosting;
using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
using Umbraco.Web.Common.Middleware;
@@ -23,9 +21,9 @@ namespace Umbraco.Extensions
/// <returns></returns>
public static bool UmbracoCanBoot(this IApplicationBuilder app)
{
var runtime = app.ApplicationServices.GetRequiredService<IRuntime>();
var state = app.ApplicationServices.GetRequiredService<IRuntimeState>();
// can't continue if boot failed
return runtime.State.Level > RuntimeLevel.BootFailed;
return state.Level > RuntimeLevel.BootFailed;
}
/// <summary>
@@ -39,25 +37,10 @@ namespace Umbraco.Extensions
if (!app.UmbracoCanBoot()) return app;
var hostingEnvironment = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
AppDomain.CurrentDomain.SetData("DataDirectory", hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data));
var runtime = app.ApplicationServices.GetRequiredService<IRuntime>();
// Register a listener for application shutdown in order to terminate the runtime
var hostLifetime = app.ApplicationServices.GetRequiredService<IApplicationShutdownRegistry>();
var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime);
hostLifetime.RegisterObject(runtimeShutdown);
// Register our global threadabort enricher for logging
var threadAbortEnricher = app.ApplicationServices.GetRequiredService<ThreadAbortExceptionEnricher>();
LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context
StaticApplicationLogging.Initialize(app.ApplicationServices.GetRequiredService<ILoggerFactory>());
// Start the runtime!
runtime.Start();
return app;
}
@@ -121,33 +104,6 @@ namespace Umbraco.Extensions
return app;
}
/// <summary>
/// Ensures the runtime is shutdown when the application is shutting down
/// </summary>
private class CoreRuntimeShutdown : IRegisteredObject
{
public CoreRuntimeShutdown(IRuntime runtime, IApplicationShutdownRegistry hostLifetime)
{
_runtime = runtime;
_hostLifetime = hostLifetime;
}
private bool _completed = false;
private readonly IRuntime _runtime;
private readonly IApplicationShutdownRegistry _hostLifetime;
public void Stop(bool immediate)
{
if (!_completed)
{
_completed = true;
_runtime.Terminate();
_hostLifetime.UnregisterObject(this);
}
}
}
}
}

View File

@@ -46,7 +46,6 @@ namespace Umbraco.Web
protected UmbracoApplicationBase()
{
HostingSettings hostingSettings = null;
GlobalSettings globalSettings = null;
SecuritySettings securitySettings = null;
@@ -60,8 +59,6 @@ namespace Umbraco.Web
var backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, ioHelper, _loggerFactory.CreateLogger<AspNetBackOfficeInfo>(), Options.Create(webRoutingSettings));
var profiler = GetWebProfiler(hostingEnvironment);
StaticApplicationLogging.Initialize(_loggerFactory);
Logger = NullLogger<UmbracoApplicationBase>.Instance;
}
private IProfiler GetWebProfiler(IHostingEnvironment hostingEnvironment)
@@ -87,7 +84,6 @@ namespace Umbraco.Web
_loggerFactory = loggerFactory;
Logger = logger;
StaticApplicationLogging.Initialize(_loggerFactory);
}
protected ILogger<UmbracoApplicationBase> Logger { get; }
@@ -189,7 +185,6 @@ namespace Umbraco.Web
LogContext.Push(new HttpRequestIdEnricher(_factory.GetRequiredService<IRequestCache>()));
_runtime = _factory.GetRequiredService<IRuntime>();
_runtime.Start();
}
// called by ASP.NET (auto event wireup) once per app domain
@@ -237,7 +232,6 @@ namespace Umbraco.Web
{
if (_runtime != null)
{
_runtime.Terminate();
_runtime.DisposeIfDisposable();
_runtime = null;