diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 61d7cff240..f7c81d6d7e 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -18,6 +18,7 @@ namespace Umbraco.Core.Composing { "Umbraco.Core", "Umbraco.Web", + "Umbraco.Web.BackOffice", "Umbraco.Infrastructure", "Umbraco.PublishedCache.NuCache", "Umbraco.ModelsBuilder.Embedded", diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index 5b97d8e4f3..b12ffbc138 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -19,8 +19,5 @@ namespace Umbraco.Core.Hosting Version IISVersion { get; } string MapPath(string path); string ToAbsolute(string virtualPath, string root); - - void RegisterObject(IRegisteredObject registeredObject); - void UnregisterObject(IRegisteredObject registeredObject); } } diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironmentLifetime.cs b/src/Umbraco.Core/Hosting/IHostingEnvironmentLifetime.cs new file mode 100644 index 0000000000..891cedad20 --- /dev/null +++ b/src/Umbraco.Core/Hosting/IHostingEnvironmentLifetime.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Hosting +{ + public interface IHostingEnvironmentLifetime + { + void RegisterObject(IRegisteredObject registeredObject); + void UnregisterObject(IRegisteredObject registeredObject); + } +} diff --git a/src/Umbraco.Core/Runtime/IMainDom.cs b/src/Umbraco.Core/Runtime/IMainDom.cs index 444fc1c7d0..1b7f09bc89 100644 --- a/src/Umbraco.Core/Runtime/IMainDom.cs +++ b/src/Umbraco.Core/Runtime/IMainDom.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Core.Hosting; // TODO: Can't change namespace due to breaking changes, change in netcore namespace Umbraco.Core @@ -16,10 +17,17 @@ namespace Umbraco.Core /// Gets a value indicating whether the current domain is the main domain. /// /// - /// When the first call is made to this there will generally be some logic executed to acquire a distributed lock lease. + /// Acquire must be called first else this will always return false /// bool IsMainDom { get; } + /// + /// Tries to acquire the MainDom, returns true if successful else false + /// + /// + /// + bool Acquire(IHostingEnvironmentLifetime hostingEnvironment); + /// /// Registers a resource that requires the current AppDomain to be the main domain to function. /// diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index 2c56852095..0a4a250aa6 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Runtime #region Vars private readonly ILogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private IHostingEnvironmentLifetime _hostingEnvironment; private readonly IMainDomLock _mainDomLock; // our own lock for local consistency @@ -42,17 +42,24 @@ namespace Umbraco.Core.Runtime #region Ctor // initializes a new instance of MainDom - public MainDom(ILogger logger, IHostingEnvironment hostingEnvironment, IMainDomLock systemLock) + public MainDom(ILogger logger, IMainDomLock systemLock) { - hostingEnvironment.RegisterObject(this); - _logger = logger; - _hostingEnvironment = hostingEnvironment; _mainDomLock = systemLock; } #endregion + public bool Acquire(IHostingEnvironmentLifetime hostingEnvironment) + { + _hostingEnvironment = hostingEnvironment; + return LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => + { + hostingEnvironment.RegisterObject(this); + return Acquire(); + }); + } + /// /// Registers a resource that requires the current AppDomain to be the main domain to function. /// @@ -180,10 +187,9 @@ namespace Umbraco.Core.Runtime /// Gets a value indicating whether the current domain is the main domain. /// /// - /// The lazy initializer call will only call the Acquire callback when it's not been initialized, else it will just return - /// the value from _isMainDom which means when we set _isMainDom to false again after being signaled, this will return false; + /// Acquire must be called first else this will always return false /// - public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire()); + public bool IsMainDom => _isMainDom; // IRegisteredObject void IRegisteredObject.Stop(bool immediate) diff --git a/src/Umbraco.Core/Scheduling/KeepAlive.cs b/src/Umbraco.Core/Scheduling/KeepAlive.cs index 515251b105..1c4ef075ae 100644 --- a/src/Umbraco.Core/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Core/Scheduling/KeepAlive.cs @@ -11,18 +11,22 @@ namespace Umbraco.Web.Scheduling { public class KeepAlive : RecurringTaskBase { - private readonly IRuntimeState _runtime; + private readonly IRuntimeState _runtimeState; + private readonly IMainDom _mainDom; private readonly IKeepAliveSettings _keepAliveSettings; private readonly IProfilingLogger _logger; + private readonly IServerRegistrar _serverRegistrar; private static HttpClient _httpClient; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IKeepAliveSettings keepAliveSettings, IProfilingLogger logger) + IRuntimeState runtimeState, IMainDom mainDom, IKeepAliveSettings keepAliveSettings, IProfilingLogger logger, IServerRegistrar serverRegistrar) : base(runner, delayMilliseconds, periodMilliseconds) { - _runtime = runtime; + _runtimeState = runtimeState; + _mainDom = mainDom; _keepAliveSettings = keepAliveSettings; _logger = logger; + _serverRegistrar = serverRegistrar; if (_httpClient == null) _httpClient = new HttpClient(); } @@ -30,7 +34,7 @@ namespace Umbraco.Web.Scheduling public override async Task PerformRunAsync(CancellationToken token) { // not on replicas nor unknown role servers - switch (_runtime.ServerRole) + switch (_serverRegistrar.GetCurrentServerRole()) { case ServerRole.Replica: _logger.Debug("Does not run on replica servers."); @@ -41,7 +45,7 @@ namespace Umbraco.Web.Scheduling } // ensure we do not run if not main domain, but do NOT lock it - if (_runtime.IsMainDom == false) + if (_mainDom.IsMainDom == false) { _logger.Debug("Does not run if not MainDom."); return false; // do NOT repeat, going down @@ -54,7 +58,7 @@ namespace Umbraco.Web.Scheduling { if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}")) { - var umbracoAppUrl = _runtime.ApplicationUrl.ToString(); + var umbracoAppUrl = _runtimeState.ApplicationUrl.ToString(); if (umbracoAppUrl.IsNullOrWhiteSpace()) { _logger.Warn("No umbracoApplicationUrl for service (yet), skip."); diff --git a/src/Umbraco.Core/Scheduling/TempFileCleanup.cs b/src/Umbraco.Core/Scheduling/TempFileCleanup.cs index aefaf605db..90bf4ee9eb 100644 --- a/src/Umbraco.Core/Scheduling/TempFileCleanup.cs +++ b/src/Umbraco.Core/Scheduling/TempFileCleanup.cs @@ -14,26 +14,26 @@ namespace Umbraco.Web.Scheduling { private readonly DirectoryInfo[] _tempFolders; private readonly TimeSpan _age; - private readonly IRuntimeState _runtime; + private readonly IMainDom _mainDom; private readonly IProfilingLogger _logger; public TempFileCleanup(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, IEnumerable tempFolders, TimeSpan age, - IRuntimeState runtime, IProfilingLogger logger) + IMainDom mainDom, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { //SystemDirectories.TempFileUploads _tempFolders = tempFolders.ToArray(); _age = age; - _runtime = runtime; + _mainDom = mainDom; _logger = logger; } public override bool PerformRun() { // ensure we do not run if not main domain - if (_runtime.IsMainDom == false) + if (_mainDom.IsMainDom == false) { _logger.Debug("Does not run if not MainDom."); return false; // do NOT repeat, going down diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index e846342dbc..4715068073 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -13,13 +13,15 @@ namespace Umbraco.Core /// /// The application register. /// The application factory. - IFactory Boot(IRegister register); + IFactory Configure(IRegister register); /// /// Gets the runtime state. /// IRuntimeState State { get; } + void Start(); + /// /// Terminates the runtime. /// diff --git a/src/Umbraco.Core/Services/IRuntimeState.cs b/src/Umbraco.Core/Services/IRuntimeState.cs index 565b2563e6..4b5e26651b 100644 --- a/src/Umbraco.Core/Services/IRuntimeState.cs +++ b/src/Umbraco.Core/Services/IRuntimeState.cs @@ -25,21 +25,6 @@ namespace Umbraco.Core /// SemVersion SemanticVersion { get; } - /// - /// Gets a value indicating whether the application is running in debug mode. - /// - bool Debug { get; } - - /// - /// Gets a value indicating whether the runtime is the current main domain. - /// - bool IsMainDom { get; } - - /// - /// Get the server's current role. - /// - ServerRole ServerRole { get; } - /// /// Gets the Umbraco application url. /// @@ -71,6 +56,5 @@ namespace Umbraco.Core /// BootFailedException BootFailedException { get; } - IMainDom MainDom { get; } } } diff --git a/src/Umbraco.Core/SimpleMainDom.cs b/src/Umbraco.Core/SimpleMainDom.cs index 87cc7bcff1..52ed6c1d91 100644 --- a/src/Umbraco.Core/SimpleMainDom.cs +++ b/src/Umbraco.Core/SimpleMainDom.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Hosting; namespace Umbraco.Core { @@ -16,6 +17,9 @@ namespace Umbraco.Core /// public bool IsMainDom { get; private set; } = true; + // always acquire + public bool Acquire(IHostingEnvironmentLifetime hostingEnvironment) => true; + /// public bool Register(Action release, int weight = 100) => Register(null, release, weight); diff --git a/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs index 78b9589a2e..70b79fbeb0 100644 --- a/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs @@ -28,17 +28,18 @@ namespace Umbraco.Web private readonly IRequestAccessor _requestAccessor; public BatchedDatabaseServerMessenger( - IRuntimeState runtime, + IMainDom mainDom, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, + IServerRegistrar serverRegistrar, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers, IRequestCache requestCache, IRequestAccessor requestAccessor) - : base(runtime, scopeProvider, sqlContext, proflog, true, options, hostingEnvironment, cacheRefreshers) + : base(mainDom, scopeProvider, sqlContext, proflog, serverRegistrar, true, options, hostingEnvironment, cacheRefreshers) { _databaseFactory = databaseFactory; _requestCache = requestCache; diff --git a/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 2a24e6f318..4e183b9211 100644 --- a/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -91,7 +91,6 @@ namespace Umbraco.Web.Compose private readonly BackgroundTaskRunner _processTaskRunner; private bool _started; private IBackgroundTask[] _tasks; - private IndexRebuilder _indexRebuilder; private readonly IRequestAccessor _requestAccessor; public DatabaseServerRegistrarAndMessengerComponent( @@ -100,14 +99,12 @@ namespace Umbraco.Web.Compose IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, - IHostingEnvironment hostingEnvironment, - IndexRebuilder indexRebuilder, + IHostingEnvironmentLifetime hostingEnvironment, IRequestAccessor requestAccessor) { _runtime = runtime; _logger = logger; _registrationService = registrationService; - _indexRebuilder = indexRebuilder; _requestAccessor = requestAccessor; // create task runner for DatabaseServerRegistrar diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs index d4e22bbfde..06b3cc0cc9 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs @@ -4,13 +4,13 @@ using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; -using Umbraco.Net; +using Umbraco.Core.Net; namespace Umbraco.Core.Compose { public sealed class ManifestWatcherComponent : IComponent { - private readonly IRuntimeState _runtimeState; + private readonly IHostingEnvironment _hosting; private readonly ILogger _logger; private readonly IIOHelper _ioHelper; private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; @@ -19,9 +19,9 @@ namespace Umbraco.Core.Compose // package.manifest chances and restarts the application on any change private ManifestWatcher _mw; - public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger, IIOHelper ioHelper, IUmbracoApplicationLifetime umbracoApplicationLifetime) + public ManifestWatcherComponent(IHostingEnvironment hosting, ILogger logger, IIOHelper ioHelper, IUmbracoApplicationLifetime umbracoApplicationLifetime) { - _runtimeState = runtimeState; + _hosting = hosting; _logger = logger; _ioHelper = ioHelper; _umbracoApplicationLifetime = umbracoApplicationLifetime; @@ -29,7 +29,7 @@ namespace Umbraco.Core.Compose public void Initialize() { - if (_runtimeState.Debug == false) return; + if (_hosting.IsDebugMode == false) return; //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) // return; diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index 67cfcded92..1bf954e4c1 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Runtime public override void Compose(Composition composition) { base.Compose(composition); - + // composers composition .ComposeRepositories() @@ -120,10 +120,11 @@ namespace Umbraco.Core.Runtime // project composition.RegisterUnique(factory => new DatabaseServerMessenger( - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), true, new DatabaseServerMessengerOptions(), factory.GetInstance(), factory.GetInstance() diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 5cd51a174a..89ec036607 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Reflection; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; @@ -12,7 +11,6 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Sync; namespace Umbraco.Core.Runtime { @@ -25,12 +23,26 @@ namespace Umbraco.Core.Runtime { private ComponentCollection _components; private IFactory _factory; - private RuntimeState _state; + private readonly RuntimeState _state; private readonly IUmbracoBootPermissionChecker _umbracoBootPermissionChecker; private readonly IGlobalSettings _globalSettings; private readonly IConnectionStrings _connectionStrings; + /// + /// Constructor + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public CoreRuntime( Configs configs, IUmbracoVersion umbracoVersion, @@ -65,11 +77,7 @@ namespace Umbraco.Core.Runtime // runtime state // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' // as the second one captures the current value (null) and therefore fails - _state = new RuntimeState(Logger, - Configs.Global(), - new Lazy(() => mainDom), - new Lazy(() => _factory.GetInstance()), - UmbracoVersion, HostingEnvironment, BackOfficeInfo) + _state = new RuntimeState(Logger, Configs.Global(), UmbracoVersion, BackOfficeInfo) { Level = RuntimeLevel.Boot }; @@ -81,12 +89,13 @@ namespace Umbraco.Core.Runtime protected ILogger Logger { get; } protected IBackOfficeInfo BackOfficeInfo { get; } + public IDbProviderFactoryCreator DbProviderFactoryCreator { get; } /// /// Gets the profiler. /// - protected IProfiler Profiler { get; set; } + protected IProfiler Profiler { get; } /// /// Gets the profiling logger. @@ -109,10 +118,10 @@ namespace Umbraco.Core.Runtime /// public IRuntimeState State => _state; - public IMainDom MainDom { get; private set; } + public IMainDom MainDom { get; } /// - public virtual IFactory Boot(IRegister register) + public virtual IFactory Configure(IRegister register) { if (register is null) throw new ArgumentNullException(nameof(register)); @@ -140,28 +149,24 @@ namespace Umbraco.Core.Runtime // application environment ConfigureUnhandledException(); - ConfigureApplicationRootPath(); - - Boot(register, timer); + return _factory = Configure(register, timer); } - - return _factory; } /// - /// Boots the runtime within a timer. + /// Configure the runtime within a timer. /// - protected virtual IFactory Boot(IRegister register, DisposableTimer timer) + private IFactory Configure(IRegister register, DisposableTimer timer) { if (register is null) throw new ArgumentNullException(nameof(register)); if (timer is null) throw new ArgumentNullException(nameof(timer)); Composition composition = null; + IFactory factory = null; try { - // throws if not full-trust - _umbracoBootPermissionChecker.ThrowIfNotPermissions(); + // run handlers RuntimeOptions.DoRuntimeBoot(ProfilingLogger); @@ -179,15 +184,8 @@ namespace Umbraco.Core.Runtime composition = new Composition(register, typeLoader, ProfilingLogger, _state, Configs, IOHelper, appCaches); composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, appCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper, UmbracoVersion, DbProviderFactoryCreator, HostingEnvironment, BackOfficeInfo); - // run handlers - RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory); - - // register runtime-level services - // there should be none, really - this is here "just in case" - Compose(composition); - - // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - AcquireMainDom(MainDom); + // register ourselves (TODO: Should we put this in RegisterEssentials?) + composition.Register(_ => this, Lifetime.Singleton); // determine our runtime level DetermineRuntimeLevel(databaseFactory, ProfilingLogger); @@ -205,13 +203,7 @@ namespace Umbraco.Core.Runtime composers.Compose(); // create the factory - _factory = composition.CreateFactory(); - - // create & initialize the components - _components = _factory.GetInstance(); - _components.Initialize(); - - + factory = composition.CreateFactory(); } catch (Exception e) { @@ -228,11 +220,11 @@ namespace Umbraco.Core.Runtime // if something goes wrong above, we may end up with no factory // meaning nothing can get the runtime state, etc - so let's try // to make sure we have a factory - if (_factory == null) + if (factory == null) { try { - _factory = composition?.CreateFactory(); + factory = composition?.CreateFactory(); } catch { /* yea */ } } @@ -246,7 +238,29 @@ namespace Umbraco.Core.Runtime // throw a BootFailedException for every requests. } - return _factory; + return factory; + } + + public void Start() + { + // throws if not full-trust + _umbracoBootPermissionChecker.ThrowIfNotPermissions(); + + ConfigureApplicationRootPath(); + + // run handlers + RuntimeOptions.DoRuntimeEssentials(_factory); + + var hostingEnvironmentLifetime = _factory.TryGetInstance(); + if (hostingEnvironmentLifetime == null) + throw new InvalidOperationException($"An instance of {typeof(IHostingEnvironmentLifetime)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(Start)}"); + + // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate + AcquireMainDom(MainDom, _factory.GetInstance()); + + // create & initialize the components + _components = _factory.GetInstance(); + _components.Initialize(); } protected virtual void ConfigureUnhandledException() @@ -273,13 +287,13 @@ namespace Umbraco.Core.Runtime IOHelper.SetRootDirectory(path); } - private bool AcquireMainDom(IMainDom mainDom) + private bool AcquireMainDom(IMainDom mainDom, IHostingEnvironmentLifetime hostingEnvironmentLifetime) { using (var timer = ProfilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) { try { - return mainDom.IsMainDom; + return mainDom.Acquire(hostingEnvironmentLifetime); } catch { @@ -338,12 +352,6 @@ namespace Umbraco.Core.Runtime _components?.Terminate(); } - /// - /// Composes the runtime. - /// - public virtual void Compose(Composition composition) - { - } #region Getters @@ -384,5 +392,6 @@ namespace Umbraco.Core.Runtime #endregion + } } diff --git a/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs b/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs index 2f45a3e437..fc2a019023 100644 --- a/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.Runtime } /// - public override IFactory Boot(IRegister register) + public override IFactory Configure(IRegister register) { var profilingLogger = new ProfilingLogger(Logger, Profiler); @@ -57,7 +57,7 @@ namespace Umbraco.Web.Runtime NetworkHelper.MachineName); Logger.Debug("Runtime: {Runtime}", GetType().FullName); - var factory = base.Boot(register); + var factory = base.Configure(register); // now (and only now) is the time to switch over to perWebRequest scopes. // up until that point we may not have a request, and scoped services would diff --git a/src/Umbraco.Infrastructure/RuntimeOptions.cs b/src/Umbraco.Infrastructure/RuntimeOptions.cs index 23abd474a4..562a7e3c5c 100644 --- a/src/Umbraco.Infrastructure/RuntimeOptions.cs +++ b/src/Umbraco.Infrastructure/RuntimeOptions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core public static class RuntimeOptions { private static List> _onBoot; - private static List> _onEssentials; + private static List> _onEssentials; /// /// Executes the RuntimeBoot handlers. @@ -33,13 +33,13 @@ namespace Umbraco.Core /// /// Executes the RuntimeEssentials handlers. /// - internal static void DoRuntimeEssentials(Composition composition, AppCaches appCaches, TypeLoader typeLoader, IUmbracoDatabaseFactory databaseFactory) + internal static void DoRuntimeEssentials(IFactory factory) { if (_onEssentials== null) return; foreach (var action in _onEssentials) - action(composition, appCaches, typeLoader, databaseFactory); + action(factory); } /// @@ -64,10 +64,10 @@ namespace Umbraco.Core /// essential things (AppCaches, a TypeLoader, and a database factory) but /// before anything else. /// - public static void OnRuntimeEssentials(Action action) + public static void OnRuntimeEssentials(Action action) { if (_onEssentials == null) - _onEssentials = new List>(); + _onEssentials = new List>(); _onEssentials.Add(action); } } diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs index 420fba7ffc..e0099c5e7e 100644 --- a/src/Umbraco.Infrastructure/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/RuntimeState.cs @@ -22,44 +22,22 @@ namespace Umbraco.Core private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; private readonly ConcurrentHashSet _applicationUrls = new ConcurrentHashSet(); - private readonly Lazy _mainDom; - private readonly Lazy _serverRegistrar; private readonly IUmbracoVersion _umbracoVersion; - private readonly IHostingEnvironment _hostingEnvironment; private readonly IBackOfficeInfo _backOfficeInfo; /// /// Initializes a new instance of the class. /// public RuntimeState(ILogger logger, IGlobalSettings globalSettings, - Lazy mainDom, Lazy serverRegistrar, IUmbracoVersion umbracoVersion, - IHostingEnvironment hostingEnvironment, + IUmbracoVersion umbracoVersion, IBackOfficeInfo backOfficeInfo) { _logger = logger; _globalSettings = globalSettings; - _mainDom = mainDom; - _serverRegistrar = serverRegistrar; _umbracoVersion = umbracoVersion; - _hostingEnvironment = hostingEnvironment; _backOfficeInfo = backOfficeInfo; } - /// - /// Gets the server registrar. - /// - /// - /// This is NOT exposed in the interface. - /// - private IServerRegistrar ServerRegistrar => _serverRegistrar.Value; - - /// - /// Gets the application MainDom. - /// - /// - /// This is NOT exposed in the interface as MainDom is internal. - /// - public IMainDom MainDom => _mainDom.Value; /// public Version Version => _umbracoVersion.Current; @@ -70,23 +48,14 @@ namespace Umbraco.Core /// public SemVersion SemanticVersion => _umbracoVersion.SemanticVersion; - /// - public bool Debug => _hostingEnvironment.IsDebugMode; - - /// - public bool IsMainDom => MainDom.IsMainDom; - - /// - public ServerRole ServerRole => ServerRegistrar.GetCurrentServerRole(); - /// public Uri ApplicationUrl { get; private set; } /// - public string CurrentMigrationState { get; internal set; } + public string CurrentMigrationState { get; private set; } /// - public string FinalMigrationState { get; internal set; } + public string FinalMigrationState { get; private set; } /// public RuntimeLevel Level { get; internal set; } = RuntimeLevel.Unknown; @@ -248,7 +217,7 @@ namespace Umbraco.Core Reason = RuntimeLevelReason.UpgradeMigrations; } - protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) + private bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) { var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion, _globalSettings)); var stateValueKey = upgrader.StateValueKey; diff --git a/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs index 0b5b81319f..31884992f7 100644 --- a/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Infrastructure/Scheduling/BackgroundTaskRunner.cs @@ -81,7 +81,7 @@ namespace Umbraco.Web.Scheduling private readonly string _logPrefix; private readonly BackgroundTaskRunnerOptions _options; private readonly ILogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingEnvironment; private readonly object _locker = new object(); private readonly BufferBlock _tasks = new BufferBlock(new DataflowBlockOptions()); @@ -105,7 +105,7 @@ namespace Umbraco.Web.Scheduling /// A logger. /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + public BackgroundTaskRunner(ILogger logger, IHostingEnvironmentLifetime hostingEnvironment, MainDomHook hook = null) : this(typeof(T).FullName, new BackgroundTaskRunnerOptions(), logger, hostingEnvironment, hook) { } @@ -116,7 +116,7 @@ namespace Umbraco.Web.Scheduling /// A logger. /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(string name, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + public BackgroundTaskRunner(string name, ILogger logger, IHostingEnvironmentLifetime hostingEnvironment, MainDomHook hook = null) : this(name, new BackgroundTaskRunnerOptions(), logger, hostingEnvironment, hook) { } @@ -127,7 +127,7 @@ namespace Umbraco.Web.Scheduling /// A logger. /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + public BackgroundTaskRunner(BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironmentLifetime hostingEnvironment, MainDomHook hook = null) : this(typeof(T).FullName, options, logger, hostingEnvironment, hook) { } @@ -139,7 +139,7 @@ namespace Umbraco.Web.Scheduling /// A logger. /// The hosting environment /// An optional main domain hook. - public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironment hostingEnvironment, MainDomHook hook = null) + public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger logger, IHostingEnvironmentLifetime hostingEnvironment, MainDomHook hook = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs index 04c1571b3b..e7692b851a 100644 --- a/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Infrastructure/Scheduling/HealthCheckNotifier.cs @@ -11,22 +11,27 @@ namespace Umbraco.Web.Scheduling { public class HealthCheckNotifier : RecurringTaskBase { - private readonly IRuntimeState _runtimeState; + private readonly IMainDom _mainDom; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; private readonly IProfilingLogger _logger; private readonly IHealthChecksSettings _healthChecksSettingsConfig; + private readonly IServerRegistrar _serverRegistrar; + private readonly IRuntimeState _runtimeState; public HealthCheckNotifier(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IRuntimeState runtimeState, IProfilingLogger logger, IHealthChecksSettings healthChecksSettingsConfig) + IMainDom mainDom, IProfilingLogger logger, IHealthChecksSettings healthChecksSettingsConfig, IServerRegistrar serverRegistrar, + IRuntimeState runtimeState) : base(runner, delayMilliseconds, periodMilliseconds) { _healthChecks = healthChecks; _notifications = notifications; - _runtimeState = runtimeState; + _mainDom = mainDom; _logger = logger; _healthChecksSettingsConfig = healthChecksSettingsConfig; + _serverRegistrar = serverRegistrar; + _runtimeState = runtimeState; } public override async Task PerformRunAsync(CancellationToken token) @@ -34,7 +39,7 @@ namespace Umbraco.Web.Scheduling if (_runtimeState.Level != RuntimeLevel.Run) return true; // repeat... - switch (_runtimeState.ServerRole) + switch (_serverRegistrar.GetCurrentServerRole()) { case ServerRole.Replica: _logger.Debug("Does not run on replica servers."); @@ -45,7 +50,7 @@ namespace Umbraco.Web.Scheduling } // ensure we do not run if not main domain, but do NOT lock it - if (_runtimeState.IsMainDom == false) + if (_mainDom.IsMainDom == false) { _logger.Debug("Does not run if not MainDom."); return false; // do NOT repeat, going down diff --git a/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs index 563d79a193..aaf09dbe8f 100644 --- a/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs @@ -11,17 +11,19 @@ namespace Umbraco.Web.Scheduling public class LogScrubber : RecurringTaskBase { - private readonly IRuntimeState _runtime; + private readonly IMainDom _mainDom; + private readonly IServerRegistrar _serverRegistrar; private readonly IAuditService _auditService; private readonly ILoggingSettings _settings; private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; public LogScrubber(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IAuditService auditService, ILoggingSettings settings, IScopeProvider scopeProvider, IProfilingLogger logger) + IMainDom mainDom, IServerRegistrar serverRegistrar, IAuditService auditService, ILoggingSettings settings, IScopeProvider scopeProvider, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { - _runtime = runtime; + _mainDom = mainDom; + _serverRegistrar = serverRegistrar; _auditService = auditService; _settings = settings; _scopeProvider = scopeProvider; @@ -53,7 +55,7 @@ namespace Umbraco.Web.Scheduling public override bool PerformRun() { - switch (_runtime.ServerRole) + switch (_serverRegistrar.GetCurrentServerRole()) { case ServerRole.Replica: _logger.Debug("Does not run on replica servers."); @@ -64,7 +66,7 @@ namespace Umbraco.Web.Scheduling } // ensure we do not run if not main domain, but do NOT lock it - if (_runtime.IsMainDom == false) + if (_mainDom.IsMainDom == false) { _logger.Debug("Does not run if not MainDom."); return false; // do NOT repeat, going down diff --git a/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs index b074704033..fea16999fd 100644 --- a/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/Scheduling/ScheduledPublishing.cs @@ -10,16 +10,20 @@ namespace Umbraco.Web.Scheduling public class ScheduledPublishing : RecurringTaskBase { private readonly IRuntimeState _runtime; + private readonly IMainDom _mainDom; + private readonly IServerRegistrar _serverRegistrar; private readonly IContentService _contentService; private readonly IUmbracoContextFactory _umbracoContextFactory; private readonly ILogger _logger; private readonly IServerMessenger _serverMessenger; public ScheduledPublishing(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILogger logger, IServerMessenger serverMessenger) + IRuntimeState runtime, IMainDom mainDom, IServerRegistrar serverRegistrar, IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILogger logger, IServerMessenger serverMessenger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; + _mainDom = mainDom; + _serverRegistrar = serverRegistrar; _contentService = contentService; _umbracoContextFactory = umbracoContextFactory; _logger = logger; @@ -31,7 +35,7 @@ namespace Umbraco.Web.Scheduling if (Suspendable.ScheduledPublishing.CanRun == false) return true; // repeat, later - switch (_runtime.ServerRole) + switch (_serverRegistrar.GetCurrentServerRole()) { case ServerRole.Replica: _logger.Debug("Does not run on replica servers."); @@ -42,7 +46,7 @@ namespace Umbraco.Web.Scheduling } // ensure we do not run if not main domain, but do NOT lock it - if (_runtime.IsMainDom == false) + if (_mainDom.IsMainDom == false) { _logger.Debug("Does not run if not MainDom."); return false; // do NOT repeat, going down diff --git a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index c8ff67579a..c867d81d69 100644 --- a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -26,10 +26,12 @@ namespace Umbraco.Web.Scheduling private const int OneHourMilliseconds = 3600000; private readonly IRuntimeState _runtime; + private readonly IMainDom _mainDom; + private readonly IServerRegistrar _serverRegistrar; private readonly IContentService _contentService; private readonly IAuditService _auditService; private readonly IProfilingLogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingEnvironment; private readonly IScopeProvider _scopeProvider; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; @@ -43,7 +45,6 @@ namespace Umbraco.Web.Scheduling private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; - private BackgroundTaskRunner _tasksRunner; private BackgroundTaskRunner _scrubberRunner; private BackgroundTaskRunner _fileCleanupRunner; private BackgroundTaskRunner _healthCheckRunner; @@ -52,15 +53,17 @@ namespace Umbraco.Web.Scheduling private object _locker = new object(); private IBackgroundTask[] _tasks; - public SchedulerComponent(IRuntimeState runtime, + public SchedulerComponent(IRuntimeState runtime, IMainDom mainDom, IServerRegistrar serverRegistrar, IContentService contentService, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, - IHostingEnvironment hostingEnvironment, IHealthChecksSettings healthChecksSettingsConfig, + IHostingEnvironmentLifetime hostingEnvironment, IHealthChecksSettings healthChecksSettingsConfig, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor, ILoggingSettings loggingSettings, IKeepAliveSettings keepAliveSettings) { _runtime = runtime; + _mainDom = mainDom; + _serverRegistrar = serverRegistrar; _contentService = contentService; _auditService = auditService; _scopeProvider = scopeProvider; @@ -83,7 +86,6 @@ namespace Umbraco.Web.Scheduling // backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", _logger, _hostingEnvironment); _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", _logger, _hostingEnvironment); - _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", _logger, _hostingEnvironment); _scrubberRunner = new BackgroundTaskRunner("LogScrubber", _logger, _hostingEnvironment); _fileCleanupRunner = new BackgroundTaskRunner("TempFileCleanup", _logger, _hostingEnvironment); _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger, _hostingEnvironment); @@ -138,7 +140,7 @@ namespace Umbraco.Web.Scheduling { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSettings, _logger); + var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, _mainDom, keepAliveSettings, _logger, _serverRegistrar); _keepAliveRunner.TryAdd(task); return task; } @@ -147,7 +149,7 @@ namespace Umbraco.Web.Scheduling { // scheduled publishing/unpublishing // install on all, will only run on non-replica servers - var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _contentService, _umbracoContextFactory, _logger, _serverMessenger); + var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _mainDom, _serverRegistrar, _contentService, _umbracoContextFactory, _logger, _serverMessenger); _publishingRunner.TryAdd(task); return task; } @@ -173,7 +175,7 @@ namespace Umbraco.Web.Scheduling } var periodInMilliseconds = healthCheckSettingsConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000; - var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, _healthChecksSettingsConfig); + var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _mainDom, logger, _healthChecksSettingsConfig, _serverRegistrar, _runtime); _healthCheckRunner.TryAdd(task); return task; } @@ -182,7 +184,7 @@ namespace Umbraco.Web.Scheduling { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(), _runtime, _auditService, settings, _scopeProvider, _logger); + var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(), _mainDom, _serverRegistrar, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } @@ -194,7 +196,7 @@ namespace Umbraco.Web.Scheduling var task = new TempFileCleanup(_fileCleanupRunner, DefaultDelayMilliseconds, OneHourMilliseconds, new[] { new DirectoryInfo(_ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads)) }, TimeSpan.FromDays(1), //files that are over a day old - _runtime, _logger); + _mainDom, _logger); _scrubberRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs index 2c964a2723..fa697a4743 100644 --- a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs @@ -18,10 +18,10 @@ namespace Umbraco.Web.Search private readonly IndexRebuilder _indexRebuilder; private readonly IMainDom _mainDom; private readonly IProfilingLogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingEnvironment; private static BackgroundTaskRunner _rebuildOnStartupRunner; - public BackgroundIndexRebuilder(IMainDom mainDom, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IndexRebuilder indexRebuilder) + public BackgroundIndexRebuilder(IMainDom mainDom, IProfilingLogger logger, IHostingEnvironmentLifetime hostingEnvironment, IndexRebuilder indexRebuilder) { _mainDom = mainDom; _logger = logger; @@ -32,8 +32,6 @@ namespace Umbraco.Web.Search /// /// Called to rebuild empty indexes on startup /// - /// - /// /// /// public void RebuildIndexes(bool onlyEmptyIndexes, int waitMilliseconds = 0) diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 5a46a37d43..c915013162 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -28,10 +28,11 @@ namespace Umbraco.Core.Sync // public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger { - private readonly IRuntimeState _runtime; + private readonly IMainDom _mainDom; private readonly ManualResetEvent _syncIdle; private readonly object _locko = new object(); private readonly IProfilingLogger _profilingLogger; + private readonly IServerRegistrar _serverRegistrar; private readonly IHostingEnvironment _hostingEnvironment; private readonly CacheRefresherCollection _cacheRefreshers; private readonly ISqlContext _sqlContext; @@ -46,14 +47,15 @@ namespace Umbraco.Core.Sync public DatabaseServerMessengerOptions Options { get; } public DatabaseServerMessenger( - IRuntimeState runtime, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, + IMainDom mainDom, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IServerRegistrar serverRegistrar, bool distributedEnabled, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers) : base(distributedEnabled) { ScopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider)); _sqlContext = sqlContext; - _runtime = runtime; + _mainDom = mainDom; _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); + _serverRegistrar = serverRegistrar; _hostingEnvironment = hostingEnvironment; _cacheRefreshers = cacheRefreshers; Logger = proflog; @@ -126,7 +128,7 @@ namespace Umbraco.Core.Sync const int weight = 10; - var registered = _runtime.MainDom.Register( + var registered = _mainDom.Register( () => { lock (_locko) @@ -262,7 +264,7 @@ namespace Umbraco.Core.Sync _lastPruned = _lastSync; - switch (_runtime.ServerRole) + switch (_serverRegistrar.GetCurrentServerRole()) { case ServerRole.Single: case ServerRole.Master: diff --git a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs index 5db79de977..3c189cc045 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs @@ -42,7 +42,7 @@ namespace Umbraco.ModelsBuilder.Embedded private static readonly string[] OurFiles = { "models.hash", "models.generated.cs", "all.generated.cs", "all.dll.path", "models.err" }; private readonly IModelsBuilderConfig _config; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingLifetime; private readonly IIOHelper _ioHelper; private readonly ModelsGenerationError _errors; @@ -51,12 +51,13 @@ namespace Umbraco.ModelsBuilder.Embedded IProfilingLogger logger, IModelsBuilderConfig config, IHostingEnvironment hostingEnvironment, + IHostingEnvironmentLifetime hostingLifetime, IIOHelper ioHelper) { _umbracoServices = umbracoServices; _logger = logger; _config = config; - _hostingEnvironment = hostingEnvironment; + _hostingLifetime = hostingLifetime; _ioHelper = ioHelper; _errors = new ModelsGenerationError(config, ioHelper); _ver = 1; // zero is for when we had no version @@ -64,7 +65,7 @@ namespace Umbraco.ModelsBuilder.Embedded RazorBuildProvider.CodeGenerationStarted += RazorBuildProvider_CodeGenerationStarted; - if (!_hostingEnvironment.IsHosted) return; + if (!hostingEnvironment.IsHosted) return; var modelsDirectory = _config.ModelsDirectoryAbsolute(_ioHelper); if (!Directory.Exists(modelsDirectory)) @@ -73,7 +74,7 @@ namespace Umbraco.ModelsBuilder.Embedded // BEWARE! if the watcher is not properly released then for some reason the // BuildManager will start confusing types - using a 'registered object' here // though we should probably plug into Umbraco's MainDom - which is internal - _hostingEnvironment.RegisterObject(this); + _hostingLifetime.RegisterObject(this); _watcher = new FileSystemWatcher(modelsDirectory); _watcher.Changed += WatcherOnChanged; _watcher.EnableRaisingEvents = true; @@ -677,7 +678,7 @@ namespace Umbraco.ModelsBuilder.Embedded { _watcher.EnableRaisingEvents = false; _watcher.Dispose(); - _hostingEnvironment.UnregisterObject(this); + _hostingLifetime.UnregisterObject(this); } #endregion diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 536bebee56..76aa38e246 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -12,11 +12,11 @@ using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Net; using Umbraco.Core.Persistence; using Umbraco.Core.Serialization; using Umbraco.Core.Strings; using Umbraco.Core.Sync; -using Umbraco.Net; using Umbraco.Web; using Umbraco.Web.Routing; @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Common private UriUtility _uriUtility; private IIOHelper _ioHelper; - public TestHelperBase(Assembly entryAssembly) + protected TestHelperBase(Assembly entryAssembly) { SettingsForTests = new SettingsForTests(); MainDom = new SimpleMainDom(); @@ -52,12 +52,8 @@ namespace Umbraco.Tests.Common return new RuntimeState( Mock.Of(), Mock.Of(), - new Lazy(), - new Lazy(), GetUmbracoVersion(), - GetHostingEnvironment(), - GetBackOfficeInfo() - ); + GetBackOfficeInfo()); } public abstract IBackOfficeInfo GetBackOfficeInfo(); @@ -132,6 +128,7 @@ namespace Umbraco.Tests.Common } public abstract IHostingEnvironment GetHostingEnvironment(); + public abstract IHostingEnvironmentLifetime GetHostingEnvironmentLifetime(); public abstract IIpResolver GetIpResolver(); diff --git a/src/Umbraco.Tests.Integration/Implementations/HostBuilderExtensions.cs b/src/Umbraco.Tests.Integration/Implementations/HostBuilderExtensions.cs index 2d86122770..b0b664484b 100644 --- a/src/Umbraco.Tests.Integration/Implementations/HostBuilderExtensions.cs +++ b/src/Umbraco.Tests.Integration/Implementations/HostBuilderExtensions.cs @@ -1,31 +1,17 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using System; +using System; using System.Collections.Generic; using System.Data.Common; using System.Data.SqlClient; using System.IO; -using Umbraco.Core.Persistence; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; using Umbraco.Tests.Integration.Testing; -namespace Umbraco.Tests.Integration +namespace Umbraco.Tests.Integration.Implementations { public static class HostBuilderExtensions { - - /// - /// Ensures the lifetime of the host ends as soon as code executes - /// - /// - /// - public static IHostBuilder UseTestLifetime(this IHostBuilder hostBuilder) - { - hostBuilder.ConfigureServices((context, collection) => collection.AddSingleton()); - return hostBuilder; - } - public static IHostBuilder UseLocalDb(this IHostBuilder hostBuilder, string dbFilePath) { // Need to register SqlClient manually diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 4c37903b0c..f1aa53694c 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -8,10 +8,11 @@ using System.Net; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Diagnostics; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Core.Net; using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; -using Umbraco.Net; using Umbraco.Tests.Common; using Umbraco.Web.BackOffice; using Umbraco.Web.BackOffice.AspNetCore; @@ -19,11 +20,11 @@ using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment; namespace Umbraco.Tests.Integration.Implementations { - public class TestHelper : TestHelperBase { private IBackOfficeInfo _backOfficeInfo; private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingLifetime; private readonly IIpResolver _ipResolver; private readonly IWebHostEnvironment _hostEnvironment; private readonly IHttpContextAccessor _httpContextAccessor; @@ -40,11 +41,12 @@ namespace Umbraco.Tests.Integration.Implementations && x.ContentRootPath == CurrentAssemblyDirectory && x.WebRootPath == CurrentAssemblyDirectory); // same folder for now? - _hostingEnvironment = new AspNetCoreHostingEnvironment( + _hostingEnvironment = new TestHostingEnvironment( SettingsForTests.GetDefaultHostingSettings(), _hostEnvironment, - _httpContextAccessor, - Mock.Of()); + _httpContextAccessor); + + _hostingLifetime = new AspNetCoreHostingEnvironmentLifetime(Mock.Of()); Logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), Profiler); } @@ -75,7 +77,9 @@ namespace Umbraco.Tests.Integration.Implementations } public override IHostingEnvironment GetHostingEnvironment() => _hostingEnvironment; + public override IHostingEnvironmentLifetime GetHostingEnvironmentLifetime() => _hostingLifetime; public override IIpResolver GetIpResolver() => _ipResolver; + } } diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs new file mode 100644 index 0000000000..491b7e5480 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Umbraco.Core.Configuration; +using Umbraco.Web.BackOffice.AspNetCore; + +namespace Umbraco.Tests.Integration.Implementations +{ + + public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Umbraco.Core.Hosting.IHostingEnvironment + { + public TestHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) : base(hostingSettings, webHostEnvironment, httpContextAccessor) + { + } + + /// + /// Override for tests since we are not hosted + /// + /// + /// This is specifically used by IOHelper and we want this to return false so that the root path is manually calcualted which is what we want for tests. + /// + bool Umbraco.Core.Hosting.IHostingEnvironment.IsHosted { get; } = false; + } +} diff --git a/src/Umbraco.Tests.Integration/Implementations/TestLifetime.cs b/src/Umbraco.Tests.Integration/Implementations/TestLifetime.cs index 063644490b..a18360eff9 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestLifetime.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestLifetime.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.Hosting; -using System.Threading; +using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; -namespace Umbraco.Tests.Integration +namespace Umbraco.Tests.Integration.Implementations { /// /// Ensures the host lifetime ends as soon as code execution is done diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index dae4d0cbb8..c9e0e94481 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Composing.LightInject; @@ -46,27 +47,36 @@ namespace Umbraco.Tests.Integration /// Manually configure the containers/dependencies and call Boot on Core runtime /// [Test] - public void BootCoreRuntime() + public void Boot_Core_Runtime() { // LightInject / Umbraco var container = UmbracoServiceProviderFactory.CreateServiceContainer(); var serviceProviderFactory = new UmbracoServiceProviderFactory(container); var umbracoContainer = serviceProviderFactory.GetContainer(); - // Create the core runtime + // Special case since we are not using the Generic Host, we need to manually add an AspNetCore service to the container + umbracoContainer.Register(x => Mock.Of()); + var testHelper = new TestHelper(); + + // Create the core runtime var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(), testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker, testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator, testHelper.MainDom, testHelper.GetTypeFinder()); - // boot it! - var factory = coreRuntime.Boot(umbracoContainer); + // boot it! + var factory = coreRuntime.Configure(umbracoContainer); Assert.IsTrue(coreRuntime.MainDom.IsMainDom); Assert.IsNull(coreRuntime.State.BootFailedException); Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); Assert.IsTrue(MyComposer.IsComposed); + Assert.IsFalse(MyComponent.IsInit); + Assert.IsFalse(MyComponent.IsTerminated); + + coreRuntime.Start(); + Assert.IsTrue(MyComponent.IsInit); Assert.IsFalse(MyComponent.IsTerminated); @@ -78,7 +88,7 @@ namespace Umbraco.Tests.Integration } /// - /// Calling AddUmbracoCore to configure the container and boot the core runtime within a generic host + /// Calling AddUmbracoCore to configure the container /// [Test] public async Task AddUmbracoCore() @@ -99,22 +109,106 @@ namespace Umbraco.Tests.Integration }); var host = await hostBuilder.StartAsync(); + var app = new ApplicationBuilder(host.Services); // assert results - var runtimeState = umbracoContainer.GetInstance(); - var mainDom = umbracoContainer.GetInstance(); + var runtimeState = app.ApplicationServices.GetRequiredService(); + var mainDom = app.ApplicationServices.GetRequiredService(); + + Assert.IsFalse(mainDom.IsMainDom); // We haven't "Started" the runtime yet + Assert.IsNull(runtimeState.BootFailedException); + Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); + 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 + } + + /// + /// Calling AddUmbracoCore to configure the container and UseUmbracoCore to start the runtime + /// + /// + [Test] + public async Task UseUmbracoCore() + { + var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory); + var testHelper = new TestHelper(); + + var hostBuilder = new HostBuilder() + .UseUmbraco(serviceProviderFactory) + .ConfigureServices((hostContext, services) => + { + AddRequiredNetCoreServices(services, testHelper); + + // Add it! + services.AddUmbracoConfiguration(); + services.AddUmbracoCore(umbracoContainer, GetType().Assembly); + }); + + var host = await hostBuilder.StartAsync(); + var app = new ApplicationBuilder(host.Services); + + app.UseUmbracoCore(); + + + // assert results + var runtimeState = app.ApplicationServices.GetRequiredService(); + var mainDom = app.ApplicationServices.GetRequiredService(); Assert.IsTrue(mainDom.IsMainDom); Assert.IsNull(runtimeState.BootFailedException); Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); Assert.IsTrue(MyComponent.IsInit); - Assert.IsFalse(MyComponent.IsTerminated); await host.StopAsync(); Assert.IsTrue(MyComponent.IsTerminated); } + [Test] + public async Task Install_Database() + { + var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory); + var testHelper = new TestHelper(); + + var hostBuilder = new HostBuilder() + //TODO: Need to have a configured umb version for the runtime state + .UseLocalDb(Path.Combine(testHelper.CurrentAssemblyDirectory, "LocalDb")) + .UseUmbraco(serviceProviderFactory) + .ConfigureServices((hostContext, services) => + { + AddRequiredNetCoreServices(services, testHelper); + + // Add it! + services.AddUmbracoConfiguration(); + services.AddUmbracoCore(umbracoContainer, GetType().Assembly); + }); + + var host = await hostBuilder.StartAsync(); + var app = new ApplicationBuilder(host.Services); + + app.UseUmbracoCore(); + + + var runtimeState = (RuntimeState)app.ApplicationServices.GetRequiredService(); + Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); + + var dbBuilder = app.ApplicationServices.GetRequiredService(); + Assert.IsNotNull(dbBuilder); + + var canConnect = dbBuilder.CanConnectToDatabase; + Assert.IsTrue(canConnect); + + var dbResult = dbBuilder.CreateSchemaAndData(); + Assert.IsTrue(dbResult.Success); + + // TODO: Get this to work ... but to do that we need to mock or pass in a current umbraco version + //var dbFactory = app.ApplicationServices.GetRequiredService(); + //var profilingLogger = app.ApplicationServices.GetRequiredService(); + //runtimeState.DetermineRuntimeLevel(dbFactory, profilingLogger); + //Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level); + } [Ignore("This test just shows that resolving services from the container before the host is done resolves 2 different instances")] [Test] @@ -128,6 +222,8 @@ namespace Umbraco.Tests.Integration .UseUmbraco(serviceProviderFactory) .ConfigureServices((hostContext, services) => { + // TODO: Try to re-register the service as a callback and see if it resolves to the same instance + lifetime1 = services.BuildServiceProvider().GetRequiredService(); }); @@ -141,46 +237,6 @@ namespace Umbraco.Tests.Integration } - [Test] - public async Task UseUmbracoCore() - { - var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory); - var testHelper = new TestHelper(); - - var hostBuilder = new HostBuilder() - //TODO: Need to have a configured umb version for the runtime state - .UseLocalDb(Path.Combine(testHelper.CurrentAssemblyDirectory, "LocalDb")) - //.UseTestLifetime() - .UseUmbraco(serviceProviderFactory) - .ConfigureServices((hostContext, services) => - { - AddRequiredNetCoreServices(services, testHelper); - - // Add it! - services.AddUmbracoConfiguration(); - services.AddUmbracoCore(umbracoContainer, GetType().Assembly); - }); - - var host = await hostBuilder.StartAsync(); - - var runtimeState = (RuntimeState)umbracoContainer.GetInstance(); - Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); - - var dbBuilder = umbracoContainer.GetInstance(); - Assert.IsNotNull(dbBuilder); - - var canConnect = dbBuilder.CanConnectToDatabase; - Assert.IsTrue(canConnect); - - var dbResult = dbBuilder.CreateSchemaAndData(); - Assert.IsTrue(dbResult.Success); - - var dbFactory = umbracoContainer.GetInstance(); - var profilingLogger = umbracoContainer.GetInstance(); - runtimeState.DetermineRuntimeLevel(dbFactory, profilingLogger); - Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level); - } - private LightInjectContainer GetUmbracoContainer(out UmbracoServiceProviderFactory serviceProviderFactory) { var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); @@ -189,9 +245,15 @@ namespace Umbraco.Tests.Integration return umbracoContainer; } + /// + /// These services need to be manually added because they do not get added by the generic host + /// + /// + /// private void AddRequiredNetCoreServices(IServiceCollection services, TestHelper testHelper) { services.AddSingleton(x => testHelper.GetHttpContextAccessor()); + // the generic host does add IHostEnvironment but not this one because we are not actually in a web context services.AddSingleton(x => testHelper.GetWebHostEnvironment()); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs index 609dcd98b8..5eb57b6ca6 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs @@ -41,6 +41,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IEntityXmlSerializer _entitySerializer; private readonly IVariationContextAccessor _variationContextAccessor; private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IHostingEnvironmentLifetime _hostingLifetime; private readonly IHostingEnvironment _hostingEnvironment; #region Constructors @@ -57,6 +58,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache ILogger logger, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, + IHostingEnvironmentLifetime hostingLifetime, IShortStringHelper shortStringHelper, ISiteDomainHelper siteDomainHelper, IEntityXmlSerializer entitySerializer, @@ -67,7 +69,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor, documentRepository, mediaRepository, memberRepository, defaultCultureAccessor, - logger, globalSettings, hostingEnvironment, shortStringHelper, siteDomainHelper, entitySerializer, null, mainDom, testing, enableRepositoryEvents) + logger, globalSettings, hostingEnvironment, hostingLifetime, shortStringHelper, siteDomainHelper, entitySerializer, null, mainDom, testing, enableRepositoryEvents) { _umbracoContextAccessor = umbracoContextAccessor; } @@ -84,6 +86,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache ILogger logger, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, + IHostingEnvironmentLifetime hostingLifetime, IShortStringHelper shortStringHelper, ISiteDomainHelper siteDomainHelper, IEntityXmlSerializer entitySerializer, @@ -99,7 +102,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _xmlStore = new XmlStore(serviceContext.ContentTypeService, serviceContext.ContentService, scopeProvider, _routesCache, _contentTypeCache, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, - documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer, hostingEnvironment, shortStringHelper); + documentRepository, mediaRepository, memberRepository, entitySerializer, hostingEnvironment, hostingLifetime, shortStringHelper); _domainService = serviceContext.DomainService; _memberService = serviceContext.MemberService; @@ -113,6 +116,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _siteDomainHelper = siteDomainHelper; _entitySerializer = entitySerializer; _hostingEnvironment = hostingEnvironment; + _hostingLifetime = hostingLifetime; } public override void Dispose() diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index 238da68370..b051682ed7 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -45,9 +45,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IDocumentRepository _documentRepository; private readonly IMediaRepository _mediaRepository; private readonly IMemberRepository _memberRepository; - private readonly IGlobalSettings _globalSettings; private readonly IEntityXmlSerializer _entitySerializer; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IHostingEnvironmentLifetime _hostingLifetime; private readonly IShortStringHelper _shortStringHelper; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly PublishedContentTypeCache _contentTypeCache; @@ -58,7 +57,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private XmlStoreFilePersister _persisterTask; private volatile bool _released; - private bool _withRepositoryEvents; #region Constructors @@ -67,8 +65,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// /// The default constructor will boot the cache, load data from file or database, /// wire events in order to manage changes, etc. public XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) - : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer, hostingEnvironment, shortStringHelper) + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IHostingEnvironmentLifetime hostingLifetime, IShortStringHelper shortStringHelper) + : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, entitySerializer, hostingEnvironment, hostingLifetime, shortStringHelper) { } // internal for unit tests @@ -76,7 +74,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // TODO: er, we DO have a DB? internal XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, - bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) + bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IHostingEnvironmentLifetime hostingLifetime, IShortStringHelper shortStringHelper) { if (testing == false) EnsureConfigurationIsValid(); @@ -90,12 +88,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _documentRepository = documentRepository; _mediaRepository = mediaRepository; _memberRepository = memberRepository; - _globalSettings = globalSettings; _entitySerializer = entitySerializer; - _hostingEnvironment = hostingEnvironment; + _hostingLifetime = hostingLifetime; _shortStringHelper = shortStringHelper; - _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(_hostingEnvironment)); + _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); if (testing) { @@ -150,7 +147,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache LongRunning = true, KeepAlive = true, Hosted = false // main domain will take care of stopping the runner (see below) - }, logger, _hostingEnvironment); + }, logger, _hostingLifetime); // create (and add to runner) _persisterTask = new XmlStoreFilePersister(runner, this, logger); @@ -207,7 +204,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache MediaTypeService.ScopedRefreshedEntity += OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity += OnMemberTypeRefreshedEntity; - _withRepositoryEvents = true; } private void ClearEvents() @@ -226,7 +222,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache MediaTypeService.ScopedRefreshedEntity -= OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity -= OnMemberTypeRefreshedEntity; - _withRepositoryEvents = false; } private void LazyInitializeContent() diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index 561a6d945e..f9de3579c2 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -32,8 +32,7 @@ namespace Umbraco.Tests.Routing //create the module var logger = Mock.Of(); var globalSettings = TestObjects.GetGlobalSettings(); - var runtime = new RuntimeState(logger, globalSettings, - new Lazy(), new Lazy(), UmbracoVersion, HostingEnvironment, BackOfficeInfo); + var runtime = new RuntimeState(logger, globalSettings, UmbracoVersion, BackOfficeInfo); _module = new UmbracoInjectedModule ( diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 92ab825ee2..44481666bc 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -146,31 +146,31 @@ namespace Umbraco.Tests.Runtimes } */ - // because we don't even have the core runtime component, - // there are a few required stuff that we need to compose - public override void Compose(Composition composition) - { - base.Compose(composition); + //// because we don't even have the core runtime component, + //// there are a few required stuff that we need to compose + //public override void Compose(Composition composition) + //{ + // base.Compose(composition); - var scopeProvider = Mock.Of(); - Mock.Get(scopeProvider) - .Setup(x => x.CreateScope( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Mock.Of()); + // var scopeProvider = Mock.Of(); + // Mock.Get(scopeProvider) + // .Setup(x => x.CreateScope( + // It.IsAny(), + // It.IsAny(), + // It.IsAny(), + // It.IsAny(), + // It.IsAny(), + // It.IsAny())) + // .Returns(Mock.Of()); - composition.RegisterUnique(scopeProvider); - } + // composition.RegisterUnique(scopeProvider); + //} private IMainDom _mainDom; - public override IFactory Boot(IRegister container) + public override IFactory Configure(IRegister container) { - var factory = base.Boot(container); + var factory = base.Configure(container); _mainDom = factory.GetInstance(); return factory; } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 99a5352a17..f167aafb89 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Runtimes var mainDom = new SimpleMainDom(); var umbracoVersion = TestHelper.GetUmbracoVersion(); var backOfficeInfo = TestHelper.GetBackOfficeInfo(); - var runtimeState = new RuntimeState(logger, null, new Lazy(() => mainDom), new Lazy(() => factory.GetInstance()), umbracoVersion, hostingEnvironment, backOfficeInfo); + var runtimeState = new RuntimeState(logger, null, umbracoVersion, backOfficeInfo); var configs = TestHelper.GetConfigs(); var variationContextAccessor = TestHelper.VariationContextAccessor; @@ -84,7 +84,6 @@ namespace Umbraco.Tests.Runtimes // create the core runtime and have it compose itself var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder); - coreRuntime.Compose(composition); // determine actual runtime level runtimeState.DetermineRuntimeLevel(databaseFactory, logger); @@ -279,7 +278,6 @@ namespace Umbraco.Tests.Runtimes // create the core runtime and have it compose itself var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder); - coreRuntime.Compose(composition); // get the components // all of them? diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index f10b141916..18eb3d19a4 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -19,13 +19,13 @@ namespace Umbraco.Tests.Scheduling public class BackgroundTaskRunnerTests { private ILogger _logger; - private IHostingEnvironment _hostingEnvironment; + private IHostingEnvironmentLifetime _hostingEnvironment; [OneTimeSetUp] public void InitializeFixture() { _logger = new ConsoleLogger(new MessageTemplates()); - _hostingEnvironment = TestHelper.GetHostingEnvironment(); + _hostingEnvironment = TestHelper.GetHostingEnvironmentLifetime(); } [Test] @@ -934,7 +934,7 @@ namespace Umbraco.Tests.Scheduling [Test] public void SourceTaskTest() { - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, _logger, TestHelper.GetHostingEnvironment()); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, _logger, TestHelper.GetHostingEnvironmentLifetime()); var task = new SourceTask(); runner.Add(task); diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs index c65c7e3efb..01169abce2 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests2.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Scheduling public async Task ThreadResumeIssue() { var logger = new DebugDiagnosticsLogger(new MessageTemplates()); - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironment()); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironmentLifetime()); var work = new ThreadResumeIssueWorkItem(); runner.Add(work); @@ -77,7 +77,7 @@ namespace Umbraco.Tests.Scheduling public async Task DebuggerInterferenceIssue() { var logger = new DebugDiagnosticsLogger(new MessageTemplates()); - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironment()); + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions { KeepAlive = true, LongRunning = true }, logger, TestHelper.GetHostingEnvironmentLifetime()); var taskCompleted = false; runner.TaskCompleted += (sender, args) => { diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 05d4de6e23..54cbec46e9 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -21,12 +21,12 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Net; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Net; using Umbraco.Tests.Common; using Umbraco.Web; using Umbraco.Web.Hosting; @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers /// public static class TestHelper { - private static TestHelperInternal _testHelperInternal = new TestHelperInternal(); + private static readonly TestHelperInternal _testHelperInternal = new TestHelperInternal(); private class TestHelperInternal : TestHelperBase { public TestHelperInternal() : base(typeof(TestHelperInternal).Assembly) @@ -62,6 +62,9 @@ namespace Umbraco.Tests.TestHelpers public override IHostingEnvironment GetHostingEnvironment() => new AspNetHostingEnvironment(SettingsForTests.GetDefaultHostingSettings()); + public override IHostingEnvironmentLifetime GetHostingEnvironmentLifetime() + => new AspNetHostingLifetime(); + public override IIpResolver GetIpResolver() => new AspNetIpResolver(); } @@ -320,6 +323,8 @@ namespace Umbraco.Tests.TestHelpers public static IHostingEnvironment GetHostingEnvironment() => _testHelperInternal.GetHostingEnvironment(); + public static IHostingEnvironmentLifetime GetHostingEnvironmentLifetime() => _testHelperInternal.GetHostingEnvironmentLifetime(); + public static IIpResolver GetIpResolver() => _testHelperInternal.GetIpResolver(); public static IRequestCache GetRequestCache() => _testHelperInternal.GetRequestCache(); diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 5cf2c7b6bd..fbfada118a 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -265,6 +265,7 @@ namespace Umbraco.Tests.TestHelpers Logger, Factory.GetInstance(), HostingEnvironment, + HostingLifetime, ShortStringHelper, new SiteDomainHelper(), Factory.GetInstance(), diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index ca36a6049e..9222f7e4ab 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -51,10 +51,10 @@ using Umbraco.Web.Templates; using Umbraco.Web.PropertyEditors; using Umbraco.Core.Dictionary; using Umbraco.Core.Models; +using Umbraco.Core.Net; using Umbraco.Core.Request; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Net; using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Web.AspNet; using Umbraco.Web.Install; @@ -140,6 +140,7 @@ namespace Umbraco.Tests.Testing protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance(); protected IHostingEnvironment HostingEnvironment { get; } = new AspNetHostingEnvironment(TestHelpers.SettingsForTests.GetDefaultHostingSettings()); + protected IHostingEnvironmentLifetime HostingLifetime { get; } = new AspNetHostingLifetime(); protected IIpResolver IpResolver => Factory.GetInstance(); protected IBackOfficeInfo BackOfficeInfo => Factory.GetInstance(); protected AppCaches AppCaches => Factory.GetInstance(); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 5e5197a28a..c5e9556d05 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -423,6 +423,7 @@ namespace Umbraco.Tests.Web.Mvc new TestDefaultCultureAccessor(), Current.Logger, TestObjects.GetGlobalSettings(), TestHelper.GetHostingEnvironment(), + TestHelper.GetHostingEnvironmentLifetime(), ShortStringHelper, new SiteDomainHelper(), Factory.GetInstance(), diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs new file mode 100644 index 0000000000..e85f3cb3f9 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Hosting; +using Umbraco.Core.Net; +using Umbraco.Core.Runtime; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + /// + /// Adds/replaces AspNetCore specific services + /// + [ComposeBefore(typeof(ICoreComposer))] + [ComposeAfter(typeof(CoreInitialComposer))] + public class AspNetCoreComposer : IComposer + { + public void Compose(Composition composition) + { + // AspNetCore specific services + composition.RegisterUnique(); + + // Our own netcore implementations + composition.RegisterUnique(); + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs index 00bc894b0d..6f1298918d 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Concurrent; -using System.Diagnostics; +using System.Globalization; using System.IO; -using System.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Hosting; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -13,30 +10,29 @@ namespace Umbraco.Web.BackOffice.AspNetCore { public class AspNetCoreHostingEnvironment : Umbraco.Core.Hosting.IHostingEnvironment { - private readonly ConcurrentDictionary _registeredObjects = - new ConcurrentDictionary(); + private readonly IHostingSettings _hostingSettings; private readonly IWebHostEnvironment _webHostEnvironment; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IHostApplicationLifetime _hostApplicationLifetime; + private string _localTempPath; - public AspNetCoreHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor, IHostApplicationLifetime hostHostApplicationLifetime) + public AspNetCoreHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) { _hostingSettings = hostingSettings ?? throw new ArgumentNullException(nameof(hostingSettings)); _webHostEnvironment = webHostEnvironment; _httpContextAccessor = httpContextAccessor; - _hostApplicationLifetime = hostHostApplicationLifetime; SiteName = webHostEnvironment.ApplicationName; ApplicationId = AppDomain.CurrentDomain.Id.ToString(); ApplicationPhysicalPath = webHostEnvironment.ContentRootPath; ApplicationVirtualPath = "/"; //TODO how to find this, This is a server thing, not application thing. - IISVersion = new Version(0, 0); // TODO not necessary IIS + IISVersion = new Version(0, 0); // TODO not necessary IIS IsDebugMode = _hostingSettings.DebugMode; } + public bool IsHosted { get; } = true; public string SiteName { get; } public string ApplicationId { get; } @@ -56,7 +52,10 @@ namespace Umbraco.Web.BackOffice.AspNetCore switch (_hostingSettings.LocalTempStorageLocation) { case LocalTempStorage.AspNetTemp: - return _localTempPath = System.IO.Path.Combine(Path.GetTempPath(),ApplicationId, "UmbracoData"); + + // TODO: I don't think this is correct? but also we probably can remove AspNetTemp as an option entirely + // since this is legacy and we shouldn't use it + return _localTempPath = System.IO.Path.Combine(Path.GetTempPath(), ApplicationId, "UmbracoData"); case LocalTempStorage.EnvironmentTemp: @@ -84,8 +83,11 @@ namespace Umbraco.Web.BackOffice.AspNetCore } } - // TODO: This may need to take into account ~/ paths which means the ApplicationVirtualPath and is this the content root or web root? - public string MapPath(string path) => Path.Combine(_webHostEnvironment.WebRootPath, path); + public string MapPath(string path) + { + var newPath = path.TrimStart('~', '/').Replace('/', Path.DirectorySeparatorChar); + return Path.Combine(_webHostEnvironment.WebRootPath, newPath); + } // TODO: Need to take into account 'root' here public string ToAbsolute(string virtualPath, string root) @@ -101,43 +103,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore return applicationPath.Add(segment).Value; } - public void RegisterObject(IRegisteredObject registeredObject) - { - var wrapped = new RegisteredObjectWrapper(registeredObject); - if (!_registeredObjects.TryAdd(registeredObject, wrapped)) - { - throw new InvalidOperationException("Could not register object"); - } - - var cancellationTokenRegistration = _hostApplicationLifetime.ApplicationStopping.Register(() => wrapped.Stop(true)); - wrapped.CancellationTokenRegistration = cancellationTokenRegistration; - } - - public void UnregisterObject(IRegisteredObject registeredObject) - { - if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) - { - wrapped.CancellationTokenRegistration.Unregister(); - } - } - - - private class RegisteredObjectWrapper - { - private readonly IRegisteredObject _inner; - - public RegisteredObjectWrapper(IRegisteredObject inner) - { - _inner = inner; - } - - public CancellationTokenRegistration CancellationTokenRegistration { get; set; } - - public void Stop(bool immediate) - { - _inner.Stop(immediate); - } - } + } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironmentShutdown.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironmentShutdown.cs new file mode 100644 index 0000000000..3726e50e3b --- /dev/null +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironmentShutdown.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using Microsoft.Extensions.Hosting; +using Umbraco.Core; +using Umbraco.Core.Hosting; + +namespace Umbraco.Web.BackOffice.AspNetCore +{ + public class AspNetCoreHostingEnvironmentLifetime : IHostingEnvironmentLifetime + { + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly ConcurrentDictionary _registeredObjects = + new ConcurrentDictionary(); + + public AspNetCoreHostingEnvironmentLifetime(IHostApplicationLifetime hostApplicationLifetime) + { + _hostApplicationLifetime = hostApplicationLifetime; + } + + public void RegisterObject(IRegisteredObject registeredObject) + { + var wrapped = new RegisteredObjectWrapper(registeredObject); + if (!_registeredObjects.TryAdd(registeredObject, wrapped)) + { + throw new InvalidOperationException("Could not register object"); + } + + var cancellationTokenRegistration = _hostApplicationLifetime.ApplicationStopping.Register(() => wrapped.Stop(true)); + wrapped.CancellationTokenRegistration = cancellationTokenRegistration; + } + + public void UnregisterObject(IRegisteredObject registeredObject) + { + if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) + { + wrapped.CancellationTokenRegistration.Unregister(); + } + } + + + private class RegisteredObjectWrapper + { + private readonly IRegisteredObject _inner; + + public RegisteredObjectWrapper(IRegisteredObject inner) + { + _inner = inner; + } + + public CancellationTokenRegistration CancellationTokenRegistration { get; set; } + + public void Stop(bool immediate) + { + _inner.Stop(immediate); + } + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs index a79838bd3e..feb122a48b 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeApplicationBuilderExtensions.cs @@ -2,6 +2,9 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Hosting; namespace Umbraco.Web.BackOffice.AspNetCore { @@ -14,5 +17,52 @@ namespace Umbraco.Web.BackOffice.AspNetCore return app; } + /// + /// Start Umbraco + /// + /// + /// + public static IApplicationBuilder UseUmbracoCore(this IApplicationBuilder app) + { + if (app == null) throw new ArgumentNullException(nameof(app)); + + // Register a listener for application shutdown in order to terminate the runtime + var hostLifetime = app.ApplicationServices.GetRequiredService(); + var runtime = app.ApplicationServices.GetRequiredService(); + var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime); + hostLifetime.RegisterObject(runtimeShutdown); + + // Start the runtime! + runtime.Start(); + + return app; + } + + /// + /// Ensures the runtime is shutdown when the application is shutting down + /// + private class CoreRuntimeShutdown : IRegisteredObject + { + public CoreRuntimeShutdown(IRuntime runtime, IHostingEnvironmentLifetime hostLifetime) + { + _runtime = runtime; + _hostLifetime = hostLifetime; + } + + private bool _completed = false; + private readonly IRuntime _runtime; + private readonly IHostingEnvironmentLifetime _hostLifetime; + + public void Stop(bool immediate) + { + if (!_completed) + { + _completed = true; + _runtime.Terminate(); + _hostLifetime.UnregisterObject(this); + } + + } + } } } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs index 6c3cecad28..d283a7ffb1 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs @@ -19,8 +19,7 @@ using Umbraco.Core.Runtime; namespace Umbraco.Web.BackOffice.AspNetCore { - - + // TODO: Move to Umbraco.Web.Common public static class UmbracoCoreServiceCollectionExtensions { /// @@ -73,11 +72,12 @@ namespace Umbraco.Web.BackOffice.AspNetCore if (umbContainer is null) throw new ArgumentNullException(nameof(umbContainer)); if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly)); + // Special case! The generic host adds a few default services but we need to manually add this one here NOW because + // we resolve it before the host finishes configuring in the call to CreateCompositionRoot services.AddSingleton(); CreateCompositionRoot(services, out var logger, out var configs, out var ioHelper, out var hostingEnvironment, out var backOfficeInfo, out var profiler); - // TODO: Get rid of this 'Current' requirement var globalSettings = configs.Global(); var umbracoVersion = new UmbracoVersion(globalSettings); @@ -95,9 +95,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore backOfficeInfo, typeFinder); - hostingEnvironment.RegisterObject(new CoreRuntimeShutdown(coreRuntime)); - - var factory = coreRuntime.Boot(umbContainer); + var factory = coreRuntime.Configure(umbContainer); return services; } @@ -119,7 +117,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore ? (IMainDomLock)new SqlMainDomLock(logger, globalSettings, connStrings, dbProviderFactoryCreator) : new MainDomSemaphoreLock(logger, hostingEnvironment); - var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); + var mainDom = new MainDom(logger, mainDomLock); var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetCoreBootPermissionsChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder); @@ -137,10 +135,6 @@ namespace Umbraco.Web.BackOffice.AspNetCore var httpContextAccessor = serviceProvider.GetRequiredService(); var webHostEnvironment = serviceProvider.GetRequiredService(); - // TODO: I'm unsure about this, by doing this it means we are resolving a "Different" instance to the one - // that controls the whole app because the instances comes from a different service provider. This - // could cause some issues with shutdowns, etc... we need to investigate. - var hostApplicationLifetime = serviceProvider.GetRequiredService(); configs = serviceProvider.GetService(); if (configs == null) @@ -150,7 +144,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore var coreDebug = configs.CoreDebug(); var globalSettings = configs.Global(); - hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, httpContextAccessor, hostApplicationLifetime); + hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, httpContextAccessor); ioHelper = new IOHelper(hostingEnvironment, globalSettings); logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), @@ -170,28 +164,6 @@ namespace Umbraco.Web.BackOffice.AspNetCore } } - /// - /// Ensures the runtime is shutdown when the application is shutting down - /// - private class CoreRuntimeShutdown : IRegisteredObject - { - public CoreRuntimeShutdown(IRuntime runtime) - { - _runtime = runtime; - } - - private bool _completed = false; - private readonly IRuntime _runtime; - - public void Stop(bool immediate) - { - if (!_completed) - { - _completed = true; - _runtime.Terminate(); - } - - } - } + } } diff --git a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs index aa07894aa9..a61ad356d5 100644 --- a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs +++ b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs @@ -1,21 +1,15 @@ using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Web; using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using IRegisteredObject = Umbraco.Core.IRegisteredObject; namespace Umbraco.Web.Hosting { public class AspNetHostingEnvironment : IHostingEnvironment { - private readonly ConcurrentDictionary _registeredObjects = - new ConcurrentDictionary(); + private readonly IHostingSettings _hostingSettings; private string _localTempPath; @@ -46,23 +40,7 @@ namespace Umbraco.Web.Hosting } public string ToAbsolute(string virtualPath, string root) => VirtualPathUtility.ToAbsolute(virtualPath, root); - public void RegisterObject(IRegisteredObject registeredObject) - { - var wrapped = new RegisteredObjectWrapper(registeredObject); - if (!_registeredObjects.TryAdd(registeredObject, wrapped)) - { - throw new InvalidOperationException("Could not register object"); - } - HostingEnvironment.RegisterObject(wrapped); - } - - public void UnregisterObject(IRegisteredObject registeredObject) - { - if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) - { - HostingEnvironment.UnregisterObject(wrapped); - } - } + public string LocalTempPath { @@ -101,20 +79,7 @@ namespace Umbraco.Web.Hosting } } } - private class RegisteredObjectWrapper : System.Web.Hosting.IRegisteredObject - { - private readonly IRegisteredObject _inner; - - public RegisteredObjectWrapper(IRegisteredObject inner) - { - _inner = inner; - } - - public void Stop(bool immediate) - { - _inner.Stop(immediate); - } - } + } diff --git a/src/Umbraco.Web/AspNet/AspNetHostingLifetime.cs b/src/Umbraco.Web/AspNet/AspNetHostingLifetime.cs new file mode 100644 index 0000000000..316ecb8389 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetHostingLifetime.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Concurrent; +using System.Web.Hosting; +using Umbraco.Core.Hosting; +using IRegisteredObject = Umbraco.Core.IRegisteredObject; + +namespace Umbraco.Web.Hosting +{ + public class AspNetHostingLifetime : IHostingEnvironmentLifetime + { + private readonly ConcurrentDictionary _registeredObjects = + new ConcurrentDictionary(); + + public void RegisterObject(IRegisteredObject registeredObject) + { + var wrapped = new RegisteredObjectWrapper(registeredObject); + if (!_registeredObjects.TryAdd(registeredObject, wrapped)) + { + throw new InvalidOperationException("Could not register object"); + } + HostingEnvironment.RegisterObject(wrapped); + } + + public void UnregisterObject(IRegisteredObject registeredObject) + { + if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) + { + HostingEnvironment.UnregisterObject(wrapped); + } + } + + private class RegisteredObjectWrapper : System.Web.Hosting.IRegisteredObject + { + private readonly IRegisteredObject _inner; + + public RegisteredObjectWrapper(IRegisteredObject inner) + { + _inner = inner; + } + + public void Stop(bool immediate) + { + _inner.Stop(immediate); + } + } + } +} diff --git a/src/Umbraco.Web/Profiling/WebProfilingController.cs b/src/Umbraco.Web/Profiling/WebProfilingController.cs index b37a7eefb8..d8f71b5c85 100644 --- a/src/Umbraco.Web/Profiling/WebProfilingController.cs +++ b/src/Umbraco.Web/Profiling/WebProfilingController.cs @@ -1,6 +1,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Persistence; @@ -18,7 +19,7 @@ namespace Umbraco.Web.Profiling [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class WebProfilingController : UmbracoAuthorizedJsonController { - private readonly IRuntimeState _runtimeState; + private readonly IHostingEnvironment _hosting; public WebProfilingController( IGlobalSettings globalSettings, @@ -30,17 +31,18 @@ namespace Umbraco.Web.Profiling IRuntimeState runtimeState, IShortStringHelper shortStringHelper, UmbracoMapper umbracoMapper, - IPublishedUrlProvider publishedUrlProvider) + IPublishedUrlProvider publishedUrlProvider, + IHostingEnvironment hosting) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { - _runtimeState = runtimeState; + _hosting = hosting; } public object GetStatus() { return new { - Enabled = _runtimeState.Debug + Enabled = _hosting.IsDebugMode }; } }} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5eb60cb629..ce08d5498d 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -134,6 +134,7 @@ + diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 9f24da95e3..30d3fe5041 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web ? (IMainDomLock)new SqlMainDomLock(logger, globalSettings, connectionStrings, dbProviderFactoryCreator) : new MainDomSemaphoreLock(logger, hostingEnvironment); - var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); + var mainDom = new MainDom(logger, mainDomLock); var requestCache = new HttpRequestAppCache(() => HttpContext.Current?.Items); var umbracoBootPermissionChecker = new AspNetUmbracoBootPermissionChecker(); diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index ce5f1304cd..0963ad3c07 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -152,7 +152,9 @@ namespace Umbraco.Web Umbraco.Composing.Current.Profiler, Umbraco.Composing.Current.HostingEnvironment, Umbraco.Composing.Current.BackOfficeInfo); - _factory = Current.Factory = _runtime.Boot(register); + _factory = Current.Factory = _runtime.Configure(register); + + _runtime.Start(); } // called by ASP.NET (auto event wireup) once per app domain diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 6f5fc26f90..9cc651f359 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -150,7 +150,7 @@ namespace Umbraco.Web { var request = GetRequestFromContext(); //NOTE: the request can be null during app startup! - return Current.RuntimeState.Debug + return Current.HostingEnvironment.IsDebugMode && request != null && (string.IsNullOrEmpty(request["umbdebugshowtrace"]) == false || string.IsNullOrEmpty(request["umbdebug"]) == false diff --git a/src/Umbraco.Web/UrlHelperExtensions.cs b/src/Umbraco.Web/UrlHelperExtensions.cs index a0bf5d3ded..f0ee309630 100644 --- a/src/Umbraco.Web/UrlHelperExtensions.cs +++ b/src/Umbraco.Web/UrlHelperExtensions.cs @@ -143,12 +143,12 @@ namespace Umbraco.Web //in case the user bypasses the installer and just bumps the web.config or client dependency config //if in debug mode, always burst the cache - if (Current.RuntimeState.Debug) + if (Current.HostingEnvironment.IsDebugMode) { return DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture).GenerateHash(); } - var version = Current.RuntimeState.SemanticVersion.ToSemanticString(); + var version = Current.UmbracoVersion.SemanticVersion.ToSemanticString(); return $"{version}.{ClientDependencySettings.Instance.Version}".GenerateHash(); } }