diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs
index 3dbc68638a..d29bd99438 100644
--- a/src/Umbraco.Core/ApplicationContext.cs
+++ b/src/Umbraco.Core/ApplicationContext.cs
@@ -348,12 +348,6 @@ namespace Umbraco.Core
ApplicationCache.IsolatedRuntimeCache.ClearAllCaches();
}
- // reset all resolvers
- ResolverCollection.ResetAll();
-
- // reset resolution itself (though this should be taken care of by resetting any of the resolvers above)
- Resolution.Reset();
-
// reset the instance objects
ApplicationCache = null;
if (_databaseContext != null) //need to check the internal field here
diff --git a/src/Umbraco.Core/Components/BootLoader.cs b/src/Umbraco.Core/Components/BootLoader.cs
new file mode 100644
index 0000000000..6adf5f74d9
--- /dev/null
+++ b/src/Umbraco.Core/Components/BootLoader.cs
@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using LightInject;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Core.Components
+{
+ // note: this class is NOT thread-safe in any ways
+
+ internal class BootLoader
+ {
+ private readonly ServiceContainer _container;
+ private readonly ProfilingLogger _proflog;
+ private IUmbracoComponent[] _components;
+ private bool _booted;
+
+ private const int LogThresholdMilliseconds = 200;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The application container.
+ public BootLoader(ServiceContainer container)
+ {
+ if (container == null) throw new ArgumentNullException(nameof(container));
+ _container = container;
+ _proflog = container.GetInstance();
+ }
+
+ // fixme - sort out it all
+ // fixme - what about executing when no database? when not configured? see all event handler!
+ // rules
+ //
+ // UmbracoCoreComponent is special and requires every IUmbracoCoreComponent
+ // IUmbracoUserComponent is special and requires UmbracoCoreComponent
+ //
+ // process Enable/Disable for *all* regardless of whether they'll end up being enabled or disabled
+ // process Require *only* for those that end up being enabled
+ // requiring something that's disabled is going to cause an exception
+ //
+ // works:
+ // gets the list of all discovered components
+ // handles dependencies and order via topological graph
+ // handle enable/disable (precedence?)
+ // OR vice-versa as, if it's disabled, it has no dependency!
+ // BUT then the order is pretty much random - bah
+ // for each component, run Compose
+ // for each component, discover & run Initialize methods
+ //
+ // should we register components on a clone? (benchmark!)
+ // should we get then in a scope => disposed?
+ // do we want to keep them around?
+ // +
+ // what's with ServiceProvider and PluginManager?
+ //
+ // do we need events?
+ // initialize, starting, started
+ // become
+ // ?, compose, initialize
+
+ private class EnableInfo
+ {
+ public bool Enabled;
+ public int Weight = -1;
+ }
+
+ public void Boot(IEnumerable componentTypes)
+ {
+ if (_booted) throw new InvalidOperationException("Can not boot, has already booted.");
+
+ using (_proflog.TraceDuration($"Booting Umbraco {UmbracoVersion.GetSemanticVersion().ToSemanticString()} on {NetworkHelper.MachineName}.", "Booted."))
+ {
+ var orderedComponentTypes = PrepareComponentTypes(componentTypes);
+ InstanciateComponents(orderedComponentTypes);
+ ComposeComponents();
+ InitializeComponents();
+ }
+
+ // rejoice!
+ _booted = true;
+ }
+
+ private IEnumerable PrepareComponentTypes(IEnumerable componentTypes)
+ {
+ using (_proflog.DebugDuration("Preparing component types.", "Prepared component types."))
+ {
+ return PrepareComponentTypes2(componentTypes);
+ }
+ }
+
+ private static IEnumerable PrepareComponentTypes2(IEnumerable componentTypes)
+ {
+ var componentTypeList = componentTypes.ToList();
+
+ if (componentTypeList.Contains(typeof(UmbracoCoreComponent)) == false)
+ componentTypeList.Add(typeof(UmbracoCoreComponent));
+
+ var enabled = new Dictionary();
+
+ // process the enable/disable attributes
+ // remote declarations (when a component enables/disables *another* component)
+ // have priority over local declarations (when a component disables itself) so that
+ // ppl can enable components that, by default, are disabled
+ // what happens in case of conflicting remote declarations is unspecified. more
+ // precisely, the last declaration to be processed wins, but the order of the
+ // declarations depends on the type finder and is unspecified
+ // we *could* fix this by adding a weight property to both attributes...
+ foreach (var componentType in componentTypeList)
+ {
+ foreach (var attr in componentType.GetCustomAttributes())
+ {
+ var type = attr.EnabledType ?? componentType;
+ EnableInfo enableInfo;
+ if (enabled.TryGetValue(type, out enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
+ var weight = type == componentType ? 1 : 2;
+ if (enableInfo.Weight > weight) continue;
+
+ enableInfo.Enabled = true;
+ enableInfo.Weight = weight;
+ }
+ foreach (var attr in componentType.GetCustomAttributes())
+ {
+ var type = attr.DisabledType ?? componentType;
+ if (type == typeof(UmbracoCoreComponent)) throw new InvalidOperationException("Cannot disable UmbracoCoreComponent.");
+ EnableInfo enableInfo;
+ if (enabled.TryGetValue(type, out enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
+ var weight = type == componentType ? 1 : 2;
+ if (enableInfo.Weight > weight) continue;
+
+ enableInfo.Enabled = false;
+ enableInfo.Weight = weight;
+ }
+ }
+
+ // remove components that end up being disabled
+ foreach (var kvp in enabled.Where(x => x.Value.Enabled == false))
+ componentTypeList.Remove(kvp.Key);
+
+ // sort the components according to their dependencies
+ var coreComponentTypes = componentTypeList.Where(x => x.Implements()).ToArray();
+ var items = new List>();
+ var temp = new List(); // reduce allocs
+ foreach (var type in componentTypeList)
+ {
+ temp.Clear();
+ if (type == typeof(UmbracoCoreComponent)) temp.AddRange(coreComponentTypes);
+ if (type.Implements()) temp.Add(typeof(UmbracoCoreComponent));
+ temp.AddRange(type.GetCustomAttributes().Select(x => x.RequiredType));
+ var dependsOn = temp.Distinct().ToArray();
+
+ // check for broken dependencies
+ foreach (var broken in temp.Where(x => componentTypeList.Contains(x) == false))
+ throw new Exception($"Broken component dependency: {type.FullName} -> {broken.FullName}.");
+
+ items.Add(new TopologicalSorter.DependencyField(type.FullName, dependsOn.Select(x => x.FullName).ToArray(), new Lazy(() => type)));
+ }
+ return TopologicalSorter.GetSortedItems(items);
+ }
+
+ private void InstanciateComponents(IEnumerable types)
+ {
+ using (_proflog.DebugDuration("Instanciating components.", "Instanciated components."))
+ {
+ _components = types.Select(x => (IUmbracoComponent) Activator.CreateInstance(x)).ToArray();
+ }
+ }
+
+ private void ComposeComponents()
+ {
+ using (_proflog.DebugDuration($"Composing components. (log when >{LogThresholdMilliseconds}ms)", "Composed components."))
+ {
+ foreach (var component in _components)
+ {
+ var componentType = component.GetType();
+ using (_proflog.DebugDuration($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", LogThresholdMilliseconds))
+ {
+ component.Compose(_container);
+ }
+ }
+ }
+ }
+
+ private void InitializeComponents()
+ {
+ // use a container scope to ensure that PerScope instances are disposed
+ // components that require instances that should not survive should register them with PerScope lifetime
+ using (_proflog.DebugDuration($"Initializing components. (log when >{LogThresholdMilliseconds}ms)", "Initialized components."))
+ using (_container.BeginScope())
+ {
+ foreach (var component in _components)
+ {
+ var componentType = component.GetType();
+ var initializers = componentType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+ .Where(x => x.Name == "Initialize" && x.IsGenericMethod == false);
+ using (_proflog.DebugDuration($"Initializing {componentType.FullName}.", $"Initialised {componentType.FullName}.", LogThresholdMilliseconds))
+ {
+ foreach (var initializer in initializers)
+ {
+ var parameters = initializer.GetParameters()
+ .Select(x => _container.GetInstance(x.ParameterType))
+ .ToArray();
+ initializer.Invoke(component, parameters);
+ }
+ }
+ }
+ }
+ }
+
+ public void Terminate()
+ {
+ if (_booted == false) throw new InvalidOperationException("Cannot terminate, has not booted.");
+
+ using (_proflog.DebugDuration($"Terminating Umbraco. (log components when >{LogThresholdMilliseconds}ms)", "Terminated Umbraco."))
+ {
+ foreach (var component in _components)
+ {
+ var componentType = component.GetType();
+ using (_proflog.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", LogThresholdMilliseconds))
+ {
+ component.Terminate();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Components/DisableComponentAttribute.cs b/src/Umbraco.Core/Components/DisableComponentAttribute.cs
new file mode 100644
index 0000000000..d80681aeb7
--- /dev/null
+++ b/src/Umbraco.Core/Components/DisableComponentAttribute.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Umbraco.Core.Components
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class DisableComponentAttribute : Attribute
+ {
+ public DisableComponentAttribute()
+ { }
+
+ public DisableComponentAttribute(Type disabledType)
+ {
+ DisabledType = disabledType;
+ }
+
+ public Type DisabledType { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Components/EnableComponentAttribute.cs b/src/Umbraco.Core/Components/EnableComponentAttribute.cs
new file mode 100644
index 0000000000..81cb6666b5
--- /dev/null
+++ b/src/Umbraco.Core/Components/EnableComponentAttribute.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Umbraco.Core.Components
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class EnableComponentAttribute : Attribute
+ {
+ public EnableComponentAttribute()
+ { }
+
+ public EnableComponentAttribute(Type enabledType)
+ {
+ EnabledType = enabledType;
+ }
+
+ public Type EnabledType { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Components/IUmbracoComponent.cs b/src/Umbraco.Core/Components/IUmbracoComponent.cs
new file mode 100644
index 0000000000..787ce3c792
--- /dev/null
+++ b/src/Umbraco.Core/Components/IUmbracoComponent.cs
@@ -0,0 +1,11 @@
+using LightInject;
+
+namespace Umbraco.Core.Components
+{
+ public interface IUmbracoComponent
+ {
+ void Compose(ServiceContainer container);
+
+ void Terminate();
+ }
+}
diff --git a/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs b/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs
new file mode 100644
index 0000000000..459da50b5e
--- /dev/null
+++ b/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs
@@ -0,0 +1,5 @@
+namespace Umbraco.Core.Components
+{
+ public interface IUmbracoCoreComponent : IUmbracoComponent
+ { }
+}
diff --git a/src/Umbraco.Core/Components/IUmbracoUserComponent.cs b/src/Umbraco.Core/Components/IUmbracoUserComponent.cs
new file mode 100644
index 0000000000..260b479fa3
--- /dev/null
+++ b/src/Umbraco.Core/Components/IUmbracoUserComponent.cs
@@ -0,0 +1,5 @@
+namespace Umbraco.Core.Components
+{
+ public interface IUmbracoUserComponent : IUmbracoComponent
+ { }
+}
diff --git a/src/Umbraco.Core/Components/RequireComponentAttribute.cs b/src/Umbraco.Core/Components/RequireComponentAttribute.cs
new file mode 100644
index 0000000000..f0f406795f
--- /dev/null
+++ b/src/Umbraco.Core/Components/RequireComponentAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Umbraco.Core.Components
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
+ public class RequireComponentAttribute : Attribute
+ {
+ public RequireComponentAttribute(Type requiredType)
+ {
+ RequiredType = requiredType;
+ }
+
+ public Type RequiredType { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Components/UmbracoComponentBase.cs b/src/Umbraco.Core/Components/UmbracoComponentBase.cs
new file mode 100644
index 0000000000..7b89b25a31
--- /dev/null
+++ b/src/Umbraco.Core/Components/UmbracoComponentBase.cs
@@ -0,0 +1,13 @@
+using LightInject;
+
+namespace Umbraco.Core.Components
+{
+ public abstract class UmbracoComponentBase : IUmbracoComponent
+ {
+ public virtual void Compose(ServiceContainer container)
+ { }
+
+ public virtual void Terminate()
+ { }
+ }
+}
diff --git a/src/Umbraco.Core/Components/UmbracoCoreComponent.cs b/src/Umbraco.Core/Components/UmbracoCoreComponent.cs
new file mode 100644
index 0000000000..cbc29e137e
--- /dev/null
+++ b/src/Umbraco.Core/Components/UmbracoCoreComponent.cs
@@ -0,0 +1,5 @@
+namespace Umbraco.Core.Components
+{
+ public class UmbracoCoreComponent : UmbracoComponentBase
+ { }
+}
diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreRuntime.cs
similarity index 52%
rename from src/Umbraco.Core/CoreBootManager.cs
rename to src/Umbraco.Core/CoreRuntime.cs
index 6e09f7eb2b..187e11fb20 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreRuntime.cs
@@ -1,466 +1,491 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using System.Threading;
-using AutoMapper;
-using LightInject;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Configuration.UmbracoSettings;
-using Umbraco.Core.DependencyInjection;
-using Umbraco.Core.Exceptions;
-using Umbraco.Core.IO;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Manifest;
-using Umbraco.Core.Models.Mapping;
-using Umbraco.Core.Models.PublishedContent;
-using Umbraco.Core.ObjectResolution;
-using Umbraco.Core.Persistence.Migrations;
-using Umbraco.Core.Plugins;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Services;
-using Umbraco.Core.Sync;
-using Umbraco.Core.Strings;
-using Umbraco.Core._Legacy.PackageActions;
-
-
-namespace Umbraco.Core
-{
-
- ///
- /// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application
- ///
- ///
- /// This does not provide any startup functionality relating to web objects
- ///
- public class CoreBootManager : IBootManager
- {
- protected ProfilingLogger ProfilingLogger { get; private set; }
- private DisposableTimer _timer;
- protected PluginManager PluginManager { get; private set; }
-
- private IServiceContainer _appStartupEvtContainer;
- private bool _isInitialized;
- private bool _isStarted;
- private bool _isComplete;
- private readonly UmbracoApplicationBase _umbracoApplication;
- protected ApplicationContext ApplicationContext { get; private set; }
- protected CacheHelper ApplicationCache { get; private set; }
-
- protected UmbracoApplicationBase UmbracoApplication => _umbracoApplication;
-
- protected ServiceContainer Container => Current.Container; // fixme kill
-
- protected IServiceProvider ServiceProvider { get; private set; }
-
- public CoreBootManager(UmbracoApplicationBase umbracoApplication)
- {
- if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
- _umbracoApplication = umbracoApplication;
- }
-
- internal CoreBootManager(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
- {
- if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
- if (logger == null) throw new ArgumentNullException("logger");
- _umbracoApplication = umbracoApplication;
- ProfilingLogger = logger;
- }
-
- public virtual IBootManager Initialize()
- {
- if (_isInitialized)
- throw new InvalidOperationException("The boot manager has already been initialized");
-
- // the logger has been created by UmbracoApplicationBase
- // fixme why not the profiling logger etc?! OR have them all created by the boot manager?
- //Create logger/profiler, and their resolvers, these are special resolvers that can be resolved before frozen so we can start logging
- var logger = Current.Logger;
-
- var profiler = CreateProfiler();
- Container.RegisterInstance(profiler); // fixme - re-registered?!
- //ProfilerResolver.Current = new ProfilerResolver(profiler) { CanResolveBeforeFrozen = true };
-
- ProfilingLogger = new ProfilingLogger(logger, profiler);
-
- ApplicationCache = CreateApplicationCache();
-
- _timer = ProfilingLogger.TraceDuration(
- string.Format("Umbraco {0} application starting on {1}", UmbracoVersion.GetSemanticVersion().ToSemanticString(), NetworkHelper.MachineName),
- "Umbraco application startup complete");
-
- ServiceProvider = new ActivatorServiceProvider();
-
- //create the plugin manager
- //TODO: this is currently a singleton but it would be better if it weren't. Unfortunately the only way to get
- // rid of this singleton would be to put it into IoC and then use the ServiceLocator pattern.
- PluginManager.Current = PluginManager = new PluginManager(ApplicationCache.RuntimeCache, ProfilingLogger, true);
-
- //build up core IoC servoces
- ConfigureCoreServices(Container);
-
- //set the singleton resolved from the core container
- ApplicationContext.Current = ApplicationContext = Container.GetInstance();
-
- //TODO: Remove these for v8!
- LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors();
- LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors();
-
- //Create a 'child'container which is a copy of all of the current registrations and begin a sub scope for it
- // this child container will be used to manage the application event handler instances and the scope will be
- // completed at the end of the boot process to allow garbage collection
- _appStartupEvtContainer = Container.Clone();
- _appStartupEvtContainer.BeginScope();
- _appStartupEvtContainer.RegisterCollection(PluginManager.ResolveApplicationStartupHandlers());
-
- //build up standard IoC services
- ConfigureApplicationServices(Container);
-
- InitializeResolvers();
- InitializeModelMappers();
-
- //now we need to call the initialize methods
- Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
- {
- try
- {
- using (ProfilingLogger.DebugDuration(
- $"Executing {x.GetType()} in ApplicationInitialized",
- $"Executed {x.GetType()} in ApplicationInitialized",
- //only log if more than 150ms
- 150))
- {
- x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
- }
- }
- catch (Exception ex)
- {
- ProfilingLogger.Logger.Error("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
- throw;
- }
- });
-
- _isInitialized = true;
-
- return this;
- }
-
- ///
- /// Build the core container which contains all core things requird to build an app context
- ///
- internal virtual void ConfigureCoreServices(ServiceContainer container)
- {
- //Logging
- container.RegisterInstance(ProfilingLogger.Profiler);
- container.RegisterInstance(ProfilingLogger);
-
- //Config
- container.RegisterFrom();
-
- //Cache
- container.RegisterInstance(ApplicationCache);
- container.RegisterInstance(ApplicationCache.RuntimeCache);
-
- //Datalayer/Repositories/SQL/Database/etc...
- container.RegisterFrom();
-
- //Data Services/ServiceContext/etc...
- container.RegisterFrom();
-
- //ModelMappers
- container.RegisterFrom();
-
- //TODO: Don't think we'll need this when the resolvers are all container resolvers
- container.RegisterSingleton();
- container.RegisterInstance(PluginManager);
-
- container.RegisterSingleton();
- container.Register(factory => FileSystemProviderManager.Current.GetFileSystemProvider());
- }
-
- ///
- /// Called to customize the IoC container
- ///
- ///
- internal virtual void ConfigureApplicationServices(ServiceContainer container)
- {
-
- }
-
- ///
- /// Creates the ApplicationCache based on a new instance of System.Web.Caching.Cache
- ///
- protected virtual CacheHelper CreateApplicationCache()
- {
- var cacheHelper = new CacheHelper(
- //we need to have the dep clone runtime cache provider to ensure
- //all entities are cached properly (cloned in and cloned out)
- new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
- new StaticCacheProvider(),
- //we have no request based cache when not running in web-based context
- new NullCacheProvider(),
- new IsolatedRuntimeCache(type =>
- //we need to have the dep clone runtime cache provider to ensure
- //all entities are cached properly (cloned in and cloned out)
- new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
-
- return cacheHelper;
- }
-
- ///
- /// This method initializes all of the model mappers registered in the container
- ///
- protected void InitializeModelMappers()
- {
- Mapper.Initialize(configuration =>
- {
- //foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType())
- foreach (var m in Container.GetAllInstances())
- {
- m.ConfigureMappings(configuration, ApplicationContext);
- }
- });
- }
-
- ///
- /// Creates the application's IProfiler
- ///
- protected virtual IProfiler CreateProfiler()
- {
- return new LogProfiler(ProfilingLogger.Logger);
- }
-
- ///
- /// Special method to extend the use of Umbraco by enabling the consumer to overwrite
- /// the absolute path to the root of an Umbraco site/solution, which is used for stuff
- /// like Umbraco.Core.IO.IOHelper.MapPath etc.
- ///
- /// Absolute
- protected virtual void InitializeApplicationRootPath(string rootPath)
- {
- IO.IOHelper.SetRootDirectory(rootPath);
- }
-
- ///
- /// Fires after initialization and calls the callback to allow for customizations to occur &
- /// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
- ///
- ///
- ///
- public virtual IBootManager Startup(Action afterStartup)
- {
- if (_isStarted)
- throw new InvalidOperationException("The boot manager has already been initialized");
-
- //call OnApplicationStarting of each application events handler
- Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
- {
- try
- {
- using (ProfilingLogger.DebugDuration(
- $"Executing {x.GetType()} in ApplicationStarting",
- $"Executed {x.GetType()} in ApplicationStarting",
- //only log if more than 150ms
- 150))
- {
- x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
- }
- }
- catch (Exception ex)
- {
- ProfilingLogger.Logger.Error("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
- throw;
- }
- });
-
- if (afterStartup != null)
- {
- afterStartup(ApplicationContext.Current);
- }
-
- _isStarted = true;
-
- return this;
- }
-
- ///
- /// Fires after startup and calls the callback once customizations are locked
- ///
- ///
- ///
- public virtual IBootManager Complete(Action afterComplete)
- {
- if (_isComplete)
- throw new InvalidOperationException("The boot manager has already been completed");
-
- FreezeResolution();
-
- //Here we need to make sure the db can be connected to
- EnsureDatabaseConnection();
-
-
- //This is a special case for the user service, we need to tell it if it's an upgrade, if so we need to ensure that
- // exceptions are bubbled up if a user is attempted to be persisted during an upgrade (i.e. when they auth to login)
- ((UserService) ApplicationContext.Services.UserService).IsUpgrading = true;
-
-
-
- //call OnApplicationStarting of each application events handler
- Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
- {
- try
- {
- using (ProfilingLogger.DebugDuration(
- $"Executing {x.GetType()} in ApplicationStarted",
- $"Executed {x.GetType()} in ApplicationStarted",
- //only log if more than 150ms
- 150))
- {
- x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
- }
- }
- catch (Exception ex)
- {
- ProfilingLogger.Logger.Error("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex);
- throw;
- }
- });
-
- //end the current scope which was created to intantiate all of the startup handlers,
- //this will dispose them if they're IDisposable
- _appStartupEvtContainer.EndCurrentScope();
- //NOTE: DO NOT Dispose this cloned container since it will also dispose of any instances
- // resolved from the parent container
- _appStartupEvtContainer = null;
-
- if (afterComplete != null)
- {
- afterComplete(ApplicationContext.Current);
- }
-
- _isComplete = true;
-
- // we're ready to serve content!
- ApplicationContext.IsReady = true;
-
- //stop the timer and log the output
- _timer.Dispose();
- return this;
- }
-
- ///
- /// We cannot continue if the db cannot be connected to
- ///
- private void EnsureDatabaseConnection()
- {
- if (ApplicationContext.IsConfigured == false) return;
- if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
-
- //try now
- if (ApplicationContext.DatabaseContext.CanConnect)
- return;
-
- var currentTry = 0;
- while (currentTry < 5)
- {
- //first wait, then retry
- Thread.Sleep(1000);
-
- if (ApplicationContext.DatabaseContext.CanConnect)
- break;
-
- currentTry++;
- }
-
- if (currentTry == 5)
- {
- throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
- }
- }
-
- ///
- /// Freeze resolution to not allow Resolvers to be modified
- ///
- protected virtual void FreezeResolution()
- {
- Resolution.Freeze();
- }
-
- ///
- /// Create the resolvers
- ///
- protected virtual void InitializeResolvers()
- {
-
- var manifestParser = new ManifestParser(ProfilingLogger.Logger, new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationCache.RuntimeCache);
- var manifestBuilder = new ManifestBuilder(ApplicationCache.RuntimeCache, manifestParser);
-
- Container.Register(_ => manifestBuilder); // will be injected in eg PropertyEditorCollectionBuilder
-
- PropertyEditorCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolvePropertyEditors());
-
- ParameterEditorCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolveParameterEditors());
-
- // setup validators with our predefined validators
- ValidatorCollectionBuilder.Register(Container)
- .Add()
- .Add()
- .Add()
- .Add()
- .Add()
- .Add();
-
- //by default we'll use the db server registrar unless the developer has the legacy
- // dist calls enabled, in which case we'll use the config server registrar
- if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled)
- {
- Container.RegisterSingleton(_ => new ConfigServerRegistrar(UmbracoConfig.For.UmbracoSettings()));
- }
- else
- {
- Container.RegisterSingleton(_ =>
- new DatabaseServerRegistrar(
- new Lazy(() => ApplicationContext.Services.ServerRegistrationService),
- new DatabaseServerRegistrarOptions()));
- }
-
-
- //by default we'll use the database server messenger with default options (no callbacks),
- // this will be overridden in the web startup
- // fixme - painful, have to take care of lifetime! - we CANNOT ask users to remember!
- // fixme - same issue with PublishedContentModelFactory and many more, I guess!
- Container.Register(
- _ => new DatabaseServerMessenger(ApplicationContext, true, new DatabaseServerMessengerOptions()),
- new PerContainerLifetime());
-
- //RepositoryResolver.Current = new RepositoryResolver(
- // new RepositoryFactory(ApplicationCache));
-
- CacheRefresherCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolveCacheRefreshers());
-
- PackageActionCollectionBuilder.Register(Container)
- .AddProducer(f => f.GetInstance().ResolvePackageActions());
-
- //the database migration objects
- MigrationCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolveTypes());
-
- // need to filter out the ones we dont want!!
- PropertyValueConverterCollectionBuilder.Register(Container)
- .Append(PluginManager.ResolveTypes());
-
- // use the new DefaultShortStringHelper
- Container.RegisterSingleton(factory
- => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance())));
-
- UrlSegmentProviderCollectionBuilder.Register(Container)
- .Append();
-
- // by default, no factory (ie, noop) is activated
- Container.RegisterSingleton();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using AutoMapper;
+using LightInject;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Components;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.DependencyInjection;
+using Umbraco.Core.Exceptions;
+using Umbraco.Core.IO;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Manifest;
+using Umbraco.Core.Models.Mapping;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.ObjectResolution;
+using Umbraco.Core.Persistence.Migrations;
+using Umbraco.Core.Plugins;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Services;
+using Umbraco.Core.Sync;
+using Umbraco.Core.Strings;
+using Umbraco.Core._Legacy.PackageActions;
+
+namespace Umbraco.Core
+{
+
+ ///
+ /// Represents the Core Umbraco runtime.
+ ///
+ /// Does not handle any of the web-related aspects of Umbraco (startup, etc). It
+ /// should be possible to use this runtime in console apps.
+ public class CoreRuntime : IRuntime
+ {
+ private BootLoader _bootLoader;
+
+ private DisposableTimer _timer;
+
+ private IServiceContainer _appStartupEvtContainer;
+
+ private bool _isInitialized;
+ private bool _isStarted;
+ private bool _isComplete;
+
+
+ // what the UmbracoApplication does is...
+ // create the container and configure for core
+ // create and register a logger
+ // then:
+ //GetBootManager()
+ // .Initialize()
+ // .Startup(appContext => OnApplicationStarting(sender, e))
+ // .Complete(appContext => OnApplicationStarted(sender, e));
+ //
+ // edit so it becomes:
+ //GetBootLoader()
+ // .Boot();
+ //
+ // merge all RegisterX into one Register
+ //
+ // WebBootLoader should
+ // configure the container for web BUT that should be AFTER the components have initialized? OR?
+ //
+ // Startup runs all app event handler OnApplicationStarting methods
+ // then triggers the OnApplicationStarting event of the app
+ // Complete
+ // freezes resolution
+ // ensures database connection (else?)
+ // tells user service it is upgrading (?)
+ // runs all app event handler OnApplicationStarted methods
+ // then triggers the OnApplicationStarted event of the app
+ // and sets Ready
+ //
+ // note: what's deciding whether install, upgrade, run?
+
+ private RuntimeState _state; // fixme what about web?!
+
+ // fixme - temp
+ public virtual void Boot(ServiceContainer container)
+ {
+ // create and register essential stuff
+
+ Logger = container.GetInstance();
+ container.RegisterInstance(Profiler = GetProfiler());
+ container.RegisterInstance(ProfilingLogger = new ProfilingLogger(Current.Logger, Profiler));
+
+ container.RegisterInstance(_state = new RuntimeState());
+
+ // then compose
+ Compose(container);
+
+ // the boot loader boots using a container scope, so anything that is PerScope will
+ // be disposed after the boot loader has booted, and anything else will remain.
+ // note that this REQUIRES that perWebRequestScope has NOT been enabled yet, else
+ // the container will fail to create a scope since there is no http context when
+ // the application starts.
+ // the boot loader is kept in the runtime for as long as Umbraco runs, and components
+ // are NOT disposed - which is not a big deal as long as they remain lightweight
+ // objects.
+
+ _bootLoader = new BootLoader(container);
+ _bootLoader.Boot(GetComponentTypes());
+ }
+
+ public virtual void Terminate()
+ {
+ _bootLoader?.Terminate();
+ }
+
+ public virtual void Compose(ServiceContainer container)
+ {
+ // create and register essential stuff
+
+ var cache = GetApplicationCache();
+ container.RegisterInstance(cache);
+ container.RegisterInstance(cache.RuntimeCache);
+
+ container.RegisterInstance(new PluginManager(cache.RuntimeCache, ProfilingLogger));
+
+ // register from roots
+ container.RegisterFrom(); // fixme - used to be before caches?
+ container.RegisterFrom();
+ container.RegisterFrom();
+ container.RegisterFrom();
+ }
+
+ protected virtual void Compose1(ServiceContainer container)
+ {
+ // fixme need to cleanup below
+
+ //TODO: Don't think we'll need this when the resolvers are all container resolvers
+ container.RegisterSingleton();
+ container.RegisterSingleton();
+ container.Register(factory => FileSystemProviderManager.Current.GetFileSystemProvider());
+ }
+
+ protected virtual void Compose2(ServiceContainer container)
+ {
+ // fixme - should we capture Logger, etc here or use factory?
+
+ // register manifest builder, will be injected in eg PropertyEditorCollectionBuilder
+ container.RegisterSingleton(factory
+ => new ManifestParser(factory.GetInstance(), new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), factory.GetInstance()));
+ container.RegisterSingleton();
+
+ PropertyEditorCollectionBuilder.Register(container)
+ .AddProducer(factory => factory.GetInstance().ResolvePropertyEditors());
+
+ ParameterEditorCollectionBuilder.Register(container)
+ .AddProducer(factory => factory.GetInstance().ResolveParameterEditors());
+
+ // register our predefined validators
+ ValidatorCollectionBuilder.Register(container)
+ .Add()
+ .Add()
+ .Add()
+ .Add()
+ .Add()
+ .Add();
+
+ // register a server registrar, by default it's the db registrar unless the dev
+ // has the legacy dist calls enabled - fixme - should obsolete the legacy thing
+ container.RegisterSingleton(_ => UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled
+ ? (IServerRegistrar)new ConfigServerRegistrar(UmbracoConfig.For.UmbracoSettings())
+ : (IServerRegistrar)new DatabaseServerRegistrar(
+ new Lazy(() => ApplicationContext.Services.ServerRegistrationService),
+ new DatabaseServerRegistrarOptions()));
+
+ // by default we'll use the database server messenger with default options (no callbacks),
+ // this will be overridden in the web startup
+ // fixme - painful, have to take care of lifetime! - we CANNOT ask users to remember!
+ // fixme - same issue with PublishedContentModelFactory and many more, I guess!
+ container.RegisterSingleton(_ => new DatabaseServerMessenger(ApplicationContext, true, new DatabaseServerMessengerOptions()));
+
+ CacheRefresherCollectionBuilder.Register(container)
+ .AddProducer(factory => factory.GetInstance().ResolveCacheRefreshers());
+
+ PackageActionCollectionBuilder.Register(container)
+ .AddProducer(f => f.GetInstance().ResolvePackageActions());
+
+ MigrationCollectionBuilder.Register(container)
+ .AddProducer(factory => factory.GetInstance().ResolveTypes());
+
+ // need to filter out the ones we dont want!! fixme - what does that mean?
+ PropertyValueConverterCollectionBuilder.Register(container)
+ .Append(factory => factory.GetInstance().ResolveTypes());
+
+ container.RegisterSingleton(factory
+ => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance())));
+
+ UrlSegmentProviderCollectionBuilder.Register(container)
+ .Append();
+
+ // by default, register a noop factory
+ container.RegisterSingleton();
+ }
+
+ #region Locals
+
+ protected ILogger Logger { get; private set; }
+
+ protected IProfiler Profiler { get; private set; }
+
+ protected ProfilingLogger ProfilingLogger { get; private set; }
+
+ // fixme do we need that one?
+ protected PluginManager PluginManager { get; private set; }
+
+ #endregion
+
+ #region Getters
+
+ protected virtual IEnumerable GetComponentTypes() => Current.PluginManager.ResolveTypes();
+
+ protected virtual IProfiler GetProfiler() => new LogProfiler(Logger);
+
+ protected virtual CacheHelper GetApplicationCache() => new CacheHelper(
+ // we need to have the dep clone runtime cache provider to ensure
+ // all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
+ new StaticCacheProvider(),
+ // we have no request based cache when not running in web-based context
+ new NullCacheProvider(),
+ new IsolatedRuntimeCache(type =>
+ // we need to have the dep clone runtime cache provider to ensure
+ // all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
+
+ #endregion
+
+ #region Core
+
+ // cannot run if the db is not there
+ // tries to connect to db (if configured)
+ private void EnsureDatabaseConnection()
+ {
+ if (ApplicationContext.IsConfigured == false) return;
+ if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
+
+ for (var i = 0; i < 5; i++)
+ {
+ if (ApplicationContext.DatabaseContext.CanConnect) return;
+ Thread.Sleep(1000);
+ }
+
+ throw new UmbracoStartupFailedException("Umbraco cannot start: a connection string is configured but Umbraco could not connect to the database.");
+ }
+
+ protected void InitializeModelMappers()
+ {
+ Mapper.Initialize(configuration =>
+ {
+ // fixme why ApplicationEventHandler?!
+ //foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType())
+ foreach (var m in Container.GetAllInstances())
+ {
+ m.ConfigureMappings(configuration, ApplicationContext);
+ }
+ });
+ }
+
+ ///
+ /// Special method to extend the use of Umbraco by enabling the consumer to overwrite
+ /// the absolute path to the root of an Umbraco site/solution, which is used for stuff
+ /// like Umbraco.Core.IO.IOHelper.MapPath etc.
+ ///
+ /// Absolute Umbraco root path.
+ protected virtual void InitializeApplicationRootPath(string rootPath)
+ {
+ IOHelper.SetRootDirectory(rootPath);
+ }
+
+ #endregion
+
+ // FIXME everything below needs to be sorted out!
+
+ protected ApplicationContext ApplicationContext { get; private set; }
+
+ protected CacheHelper ApplicationCache { get; private set; }
+
+ protected UmbracoApplicationBase UmbracoApplication { get; }
+
+ protected ServiceContainer Container => Current.Container; // fixme kill
+
+ protected IServiceProvider ServiceProvider { get; private set; }
+
+ public CoreRuntime(UmbracoApplicationBase umbracoApplication)
+ {
+ if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
+ UmbracoApplication = umbracoApplication;
+ }
+
+ internal CoreRuntime(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
+ {
+ if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
+ if (logger == null) throw new ArgumentNullException("logger");
+ UmbracoApplication = umbracoApplication;
+ ProfilingLogger = logger;
+ }
+
+ public virtual IRuntime Initialize()
+ {
+ if (_isInitialized)
+ throw new InvalidOperationException("The boot manager has already been initialized");
+
+ //ApplicationCache = Container.GetInstance(); //GetApplicationCache();
+
+ _timer = ProfilingLogger.TraceDuration(
+ string.Format("Umbraco {0} application starting on {1}", UmbracoVersion.GetSemanticVersion().ToSemanticString(), NetworkHelper.MachineName),
+ "Umbraco application startup complete");
+
+ ServiceProvider = new ActivatorServiceProvider();
+
+ //create the plugin manager
+ //TODO: this is currently a singleton but it would be better if it weren't. Unfortunately the only way to get
+ // rid of this singleton would be to put it into IoC and then use the ServiceLocator pattern.
+ PluginManager.Current = PluginManager = Current.PluginManager; //new PluginManager(ApplicationCache.RuntimeCache, ProfilingLogger, true);
+
+ // register
+ Compose1(Container);
+
+ //set the singleton resolved from the core container
+ // fixme - last thing to understand before we merge the two Compose() methods!
+ ApplicationContext.Current = ApplicationContext = Container.GetInstance();
+
+ // register - why 2?
+ Compose2(Container);
+
+ //TODO: Remove these for v8!
+ LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors();
+ LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors();
+
+ InitializeModelMappers();
+
+ //now we need to call the initialize methods
+ //Create a 'child'container which is a copy of all of the current registrations and begin a sub scope for it
+ // this child container will be used to manage the application event handler instances and the scope will be
+ // completed at the end of the boot process to allow garbage collection
+ // using (Container.BeginScope()) { } // fixme - throws
+ _appStartupEvtContainer = Container.Clone(); // fixme - but WHY clone? because *then* it has its own scope?! bah ;-(
+ //using (_appStartupEvtContainer.BeginScope()) // fixme - works wtf?
+ _appStartupEvtContainer.BeginScope(); // fixme - but then attend to end a scope before all child scopes are completed wtf?!
+ _appStartupEvtContainer.RegisterCollection(factory => factory.GetInstance().ResolveApplicationStartupHandlers());
+
+ // fixme - parallel? what about our dependencies?
+ Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
+ {
+ try
+ {
+ using (ProfilingLogger.DebugDuration(
+ $"Executing {x.GetType()} in ApplicationInitialized",
+ $"Executed {x.GetType()} in ApplicationInitialized",
+ //only log if more than 150ms
+ 150))
+ {
+ x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
+ }
+ }
+ catch (Exception ex)
+ {
+ ProfilingLogger.Logger.Error("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
+ throw;
+ }
+ });
+
+ _isInitialized = true;
+
+ return this;
+ }
+
+
+ ///
+ /// Fires after initialization and calls the callback to allow for customizations to occur &
+ /// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
+ ///
+ ///
+ ///
+ public virtual IRuntime Startup(Action afterStartup)
+ {
+ if (_isStarted)
+ throw new InvalidOperationException("The boot manager has already been initialized");
+
+ //call OnApplicationStarting of each application events handler
+ Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
+ {
+ try
+ {
+ using (ProfilingLogger.DebugDuration(
+ $"Executing {x.GetType()} in ApplicationStarting",
+ $"Executed {x.GetType()} in ApplicationStarting",
+ //only log if more than 150ms
+ 150))
+ {
+ x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
+ }
+ }
+ catch (Exception ex)
+ {
+ ProfilingLogger.Logger.Error("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
+ throw;
+ }
+ });
+
+ if (afterStartup != null)
+ {
+ afterStartup(ApplicationContext.Current);
+ }
+
+ _isStarted = true;
+
+ return this;
+ }
+
+ ///
+ /// Fires after startup and calls the callback once customizations are locked
+ ///
+ ///
+ ///
+ public virtual IRuntime Complete(Action afterComplete)
+ {
+ if (_isComplete)
+ throw new InvalidOperationException("The boot manager has already been completed");
+
+ FreezeResolution();
+
+ //Here we need to make sure the db can be connected to
+ EnsureDatabaseConnection();
+
+
+ //This is a special case for the user service, we need to tell it if it's an upgrade, if so we need to ensure that
+ // exceptions are bubbled up if a user is attempted to be persisted during an upgrade (i.e. when they auth to login)
+ ((UserService) ApplicationContext.Services.UserService).IsUpgrading = true;
+
+
+
+ //call OnApplicationStarting of each application events handler
+ Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x =>
+ {
+ try
+ {
+ using (ProfilingLogger.DebugDuration(
+ $"Executing {x.GetType()} in ApplicationStarted",
+ $"Executed {x.GetType()} in ApplicationStarted",
+ //only log if more than 150ms
+ 150))
+ {
+ x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
+ }
+ }
+ catch (Exception ex)
+ {
+ ProfilingLogger.Logger.Error("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex);
+ throw;
+ }
+ });
+
+ //end the current scope which was created to intantiate all of the startup handlers,
+ //this will dispose them if they're IDisposable
+ _appStartupEvtContainer.EndCurrentScope();
+ //NOTE: DO NOT Dispose this cloned container since it will also dispose of any instances
+ // resolved from the parent container
+ _appStartupEvtContainer = null;
+
+ if (afterComplete != null)
+ {
+ afterComplete(ApplicationContext.Current);
+ }
+
+ _isComplete = true;
+
+ // we're ready to serve content!
+ ApplicationContext.IsReady = true;
+
+ //stop the timer and log the output
+ _timer.Dispose();
+ return this;
+ }
+
+
+ ///
+ /// Freeze resolution to not allow Resolvers to be modified
+ ///
+ protected virtual void FreezeResolution()
+ {
+ Resolution.Freeze();
+ }
+ }
+}
diff --git a/src/Umbraco.Core/DependencyInjection/Current.cs b/src/Umbraco.Core/DependencyInjection/Current.cs
index 5b0f179bf1..be20a65aad 100644
--- a/src/Umbraco.Core/DependencyInjection/Current.cs
+++ b/src/Umbraco.Core/DependencyInjection/Current.cs
@@ -5,6 +5,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.Plugins;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
@@ -15,19 +16,20 @@ namespace Umbraco.Core.DependencyInjection
// this class is here to support the transition from singletons and resolvers to injection,
// by providing a static access to singleton services - it is initialized once with a service
// container, in CoreBootManager.
- // ideally, it should not exist. practically, time will tell.
+ // obviously, this is some sort of service locator anti-pattern. ideally, it should not exist.
+ // practically... time will tell.
public static class Current
{
private static ServiceContainer _container;
- public static ServiceContainer Container
+ internal static ServiceContainer Container
{
get
{
if (_container == null) throw new Exception("No container has been set.");
return _container;
}
- internal set // ok to set - don't be stupid
+ set // ok to set - don't be stupid
{
if (_container != null) throw new Exception("A container has already been set.");
_container = value;
@@ -44,6 +46,7 @@ namespace Umbraco.Core.DependencyInjection
_shortStringHelper = null;
_logger = null;
_profiler = null;
+ _profilingLogger = null;
Resetted?.Invoke(null, EventArgs.Empty);
}
@@ -52,6 +55,9 @@ namespace Umbraco.Core.DependencyInjection
#region Getters
+ public static PluginManager PluginManager
+ => Container.GetInstance();
+
public static UrlSegmentProviderCollection UrlSegmentProviders
=> Container.GetInstance();
@@ -99,6 +105,7 @@ namespace Umbraco.Core.DependencyInjection
private static ILogger _logger;
private static IProfiler _profiler;
+ private static ProfilingLogger _profilingLogger;
public static ILogger Logger
=> _logger ?? (_logger = _container?.TryGetInstance()
@@ -108,6 +115,10 @@ namespace Umbraco.Core.DependencyInjection
=> _profiler ?? (_profiler = _container?.TryGetInstance()
?? new LogProfiler(Logger));
+ public static ProfilingLogger ProfilingLogger
+ => _profilingLogger ?? (_profilingLogger = _container?.TryGetInstance())
+ ?? new ProfilingLogger(Logger, Profiler);
+
#endregion
}
}
diff --git a/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs b/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs
index ca73b48145..c5fb643e27 100644
--- a/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs
+++ b/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs
@@ -153,6 +153,8 @@ namespace Umbraco.Core.DependencyInjection
return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService && x.ServiceName == name);
}
+ // FIXME or just use names?!
+
///
/// In order for LightInject to deal with enumerables of the same type, each one needs to be registered as their explicit types
///
@@ -167,9 +169,14 @@ namespace Umbraco.Core.DependencyInjection
where TLifetime : ILifetime, new()
{
foreach (var type in implementationTypes)
- {
container.Register(type, new TLifetime());
- }
+ }
+
+ public static void RegisterCollection(this IServiceContainer container, Func> implementationTypes)
+ where TLifetime : ILifetime, new()
+ {
+ foreach (var type in implementationTypes(container))
+ container.Register(type, new TLifetime());
}
///
@@ -188,5 +195,11 @@ namespace Umbraco.Core.DependencyInjection
container.Register(type);
}
}
+
+ public static void RegisterCollection(this IServiceContainer container, Func> implementationTypes)
+ {
+ foreach (var type in implementationTypes(container))
+ container.Register(type);
+ }
}
}
diff --git a/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs
index 55bf274af9..f6f185995f 100644
--- a/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs
+++ b/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs
@@ -58,6 +58,27 @@ namespace Umbraco.Core.DependencyInjection
return This;
}
+ ///
+ /// Appends types to the collections.
+ ///
+ /// The types to append.
+ /// The builder.
+ public TBuilder Append(Func> types)
+ {
+ Configure(list =>
+ {
+ foreach (var type in types(Container))
+ {
+ // would be detected by CollectionBuilderBase when registering, anyways, but let's fail fast
+ if (typeof(TItem).IsAssignableFrom(type) == false)
+ throw new InvalidOperationException($"Cannot register type {type.FullName} as it does not inherit from/implement {typeof(TItem).FullName}.");
+ if (list.Contains(type)) list.Remove(type);
+ list.Add(type);
+ }
+ });
+ return This;
+ }
+
///
/// Appends a type after another type.
///
diff --git a/src/Umbraco.Core/IBootManager.cs b/src/Umbraco.Core/IBootManager.cs
deleted file mode 100644
index d1adfb7bf4..0000000000
--- a/src/Umbraco.Core/IBootManager.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-
-namespace Umbraco.Core
-{
- ///
- /// A bootstrapper interface for the Umbraco application
- ///
- public interface IBootManager
- {
- ///
- /// Fires first in the application startup process before any customizations can occur
- ///
- ///
- IBootManager Initialize();
-
- ///
- /// Fires after initialization and calls the callback to allow for customizations to occur
- ///
- ///
- ///
- IBootManager Startup(Action afterStartup);
-
- ///
- /// Fires after startup and calls the callback once customizations are locked
- ///
- ///
- ///
- IBootManager Complete(Action afterComplete);
-
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/IRuntime.cs b/src/Umbraco.Core/IRuntime.cs
new file mode 100644
index 0000000000..5321e91272
--- /dev/null
+++ b/src/Umbraco.Core/IRuntime.cs
@@ -0,0 +1,62 @@
+using System;
+using LightInject;
+
+namespace Umbraco.Core
+{
+ // fixme - move!
+ public class RuntimeState
+ {
+ ///
+ /// Gets a value indicating whether the application is running in debug mode.
+ ///
+ public bool Debug { get; }
+
+ public RuntimeSomething Something { get; }
+ }
+
+ public enum RuntimeSomething
+ {
+ Boot,
+ Run
+ }
+
+ ///
+ /// Defines the Umbraco runtime.
+ ///
+ public interface IRuntime
+ {
+ ///
+ /// Boots the runtime.
+ ///
+ /// The application service container.
+ void Boot(ServiceContainer container);
+
+ ///
+ /// Terminates the runtime.
+ ///
+ void Terminate();
+
+ // fixme - everything below is obsolete!
+
+ ///
+ /// Fires first in the application startup process before any customizations can occur
+ ///
+ ///
+ IRuntime Initialize();
+
+ ///
+ /// Fires after initialization and calls the callback to allow for customizations to occur
+ ///
+ ///
+ ///
+ IRuntime Startup(Action afterStartup);
+
+ ///
+ /// Fires after startup and calls the callback once customizations are locked
+ ///
+ ///
+ ///
+ IRuntime Complete(Action afterComplete);
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
deleted file mode 100644
index 97c725609c..0000000000
--- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using System.Threading;
-//using Umbraco.Core.LightInject;
-//using Umbraco.Core.Logging;
-//using Umbraco.Core.Services;
-//using umbraco.interfaces;
-
-//namespace Umbraco.Core.ObjectResolution
-//{
-// ///
-// /// A resolver to return all IApplicationEvents objects
-// ///
-// ///
-// /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
-// ///
-// internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable
-// {
-// private IServiceContainer _container;
-// private readonly LegacyStartupHandlerResolver _legacyResolver;
-
-// public ApplicationEventsResolver(IServiceContainer container, IEnumerable applicationEventHandlers)
-// : base(container.GetInstance(), container.GetInstance(), applicationEventHandlers)
-// {
-// //create the legacy resolver and only include the legacy types
-// _legacyResolver = new LegacyStartupHandlerResolver(
-// container,
-// applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom(x) == false));
-// }
-
-// /////
-// ///// Constructor
-// /////
-// /////
-// /////
-// /////
-// /////
-// //internal ApplicationEventsResolver(IServiceContainer parentContainer, IServiceProvider serviceProvider, ILogger logger, IEnumerable applicationEventHandlers)
-// // : base(serviceProvider, logger, applicationEventHandlers)
-// //{
-// // //create the legacy resolver and only include the legacy types
-// // _legacyResolver = new LegacyStartupHandlerResolver(
-// // applicationEventHandlers.Where(x => !TypeHelper.IsTypeAssignableFrom(x)));
-// //}
-
-// ///
-// /// Override in order to only return types of IApplicationEventHandler and above,
-// /// do not include the legacy types of IApplicationStartupHandler
-// ///
-// protected override IEnumerable InstanceTypes
-// {
-// get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom); }
-// }
-
-// ///
-// /// Gets the implementations.
-// ///
-// public IEnumerable ApplicationEventHandlers
-// {
-// get
-// {
-
-// //return Values;
-// }
-// }
-
-// ///
-// /// Create instances of all of the legacy startup handlers
-// ///
-// public void InstantiateLegacyStartupHandlers()
-// {
-// //this will instantiate them all
-// var handlers = _legacyResolver.LegacyStartupHandlers;
-// }
-
-// protected override bool SupportsClear
-// {
-// get { return false; }
-// }
-
-// protected override bool SupportsInsert
-// {
-// get { return false; }
-// }
-
-// private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable
-// {
-// internal LegacyStartupHandlerResolver(IServiceContainer container, IEnumerable applicationEventHandlers)
-// : base(container.GetInstance(), container.GetInstance(), applicationEventHandlers)
-// {
-// }
-
-// //internal LegacyStartupHandlerResolver(IEnumerable legacyStartupHandlers)
-// // : base(legacyStartupHandlers)
-// //{
-
-// //}
-
-// public IEnumerable LegacyStartupHandlers
-// {
-// get { return Values; }
-// }
-
-// public void Dispose()
-// {
-// ResetCollections();
-// }
-// }
-
-// private bool _disposed;
-// private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
-
-// ///
-// /// Gets a value indicating whether this instance is disposed.
-// ///
-// ///
-// /// true if this instance is disposed; otherwise, false.
-// ///
-// public bool IsDisposed
-// {
-// get { return _disposed; }
-// }
-
-// ///
-// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
-// ///
-// /// 2
-// public void Dispose()
-// {
-// Dispose(true);
-
-// // Use SupressFinalize in case a subclass of this type implements a finalizer.
-// GC.SuppressFinalize(this);
-// }
-
-// ~ApplicationEventsResolver()
-// {
-// // Run dispose but let the class know it was due to the finalizer running.
-// Dispose(false);
-// }
-
-// private void Dispose(bool disposing)
-// {
-// // Only operate if we haven't already disposed
-// if (IsDisposed || disposing == false) return;
-
-// using (new WriteLock(_disposalLocker))
-// {
-// // Check again now we're inside the lock
-// if (IsDisposed) return;
-
-// // Call to actually release resources. This method is only
-// // kept separate so that the entire disposal logic can be used as a VS snippet
-// DisposeResources();
-
-// // Indicate that the instance has been disposed.
-// _disposed = true;
-// }
-// }
-
-// ///
-// /// Clear out all of the instances, we don't want them hanging around and cluttering up memory
-// ///
-// private void DisposeResources()
-// {
-// _legacyResolver.Dispose();
-// ResetCollections();
-// }
-
-// }
-//}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs b/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs
deleted file mode 100644
index ddfaa5882c..0000000000
--- a/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using System;
-using System.Linq;
-using LightInject;
-using Umbraco.Core.Persistence.Migrations.Syntax.Create;
-
-namespace Umbraco.Core.ObjectResolution
-{
-
- ///
- /// A single object resolver that can be configured to use IoC to instantiate and wire up the object
- ///
- ///
- ///
- public abstract class ContainerSingleObjectResolver : SingleObjectResolverBase
- where TResolved : class
- where TResolver : ResolverBase
- {
- private readonly IServiceContainer _container;
-
-
- #region Constructors used for test - ONLY so that a container is not required and will just revert to using the normal SingleObjectResolverBase
-
- [Obsolete("Used for tests only - should remove")]
- internal ContainerSingleObjectResolver()
- {
- }
-
- [Obsolete("Used for tests only - should remove")]
- internal ContainerSingleObjectResolver(TResolved value)
- : base(value)
- {
- }
-
- [Obsolete("Used for tests only - should remove")]
- internal ContainerSingleObjectResolver(bool canBeNull)
- : base(canBeNull)
- {
- }
-
- [Obsolete("Used for tests only - should remove")]
- internal ContainerSingleObjectResolver(TResolved value, bool canBeNull)
- : base(value, canBeNull)
- {
- }
- #endregion
-
- ///
- /// Initialize the resolver to use IoC, when using this contructor the type must be set manually
- ///
- ///
- internal ContainerSingleObjectResolver(IServiceContainer container)
- {
- if (container == null) throw new ArgumentNullException(nameof(container));
- _container = container;
- }
-
- ///
- /// Initializes the resolver to use IoC
- ///
- ///
- ///
- internal ContainerSingleObjectResolver(IServiceContainer container, Func implementationType)
- {
- _container = container;
- _container.Register(implementationType, new PerContainerLifetime());
- }
-
- ///
- /// Gets or sets the resolved object instance.
- ///
- ///
- /// value is set to null, but cannot be null (CanBeNull is false).
- /// value is read and is null, but cannot be null (CanBeNull is false),
- /// or value is set (read) and resolution is (not) frozen.
- protected override TResolved Value
- {
- get
- {
- return _container == null
- ? base.Value
- : _container.GetInstance();
- }
- set
- {
- if (_container != null)
- {
- var avail = _container.AvailableServices.Any(x => x.ServiceType == typeof (TResolved));
-
- if (avail)
- {
- // must override with the proper name!
- _container.Override(
- sr => sr.ServiceType == typeof (TResolved),
- (factory, registration) =>
- {
- registration.Value = value;
- registration.Lifetime = new PerContainerLifetime();
- return registration;
- });
- }
- else
- {
- // cannot override something that hasn't been registered yet!
- _container.Register(new ServiceRegistration
- {
- ServiceType = typeof (TResolved),
- // no! use Value below!
- //ImplementingType = value.GetType(),
- ServiceName = "",
- Lifetime = new PerContainerLifetime(),
- Value = value
- });
- }
- }
- base.Value = value;
- }
- }
-
- ///
- /// Gets a value indicating whether the resolved object instance is null.
- ///
- public override bool HasValue
- {
- get
- {
- if (_container == null) return base.HasValue;
- return (_container.TryGetInstance() == null) == false;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs b/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs
deleted file mode 100644
index e7330ae124..0000000000
--- a/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Umbraco.Core.ObjectResolution
-{
- ///
- /// Specifies the lifetime scope of resolved objects.
- ///
- public enum ObjectLifetimeScope
- {
- ///
- /// A per-request object instance is created.
- ///
- HttpRequest,
-
- ///
- /// A single application-wide object instance is created.
- ///
- Application,
-
- ///
- /// A new object instance is created each time one is requested.
- ///
- Transient
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/Resolution.cs b/src/Umbraco.Core/ObjectResolution/Resolution.cs
index 31661f6f7b..192d411683 100644
--- a/src/Umbraco.Core/ObjectResolution/Resolution.cs
+++ b/src/Umbraco.Core/ObjectResolution/Resolution.cs
@@ -1,10 +1,14 @@
using System;
-using System.Linq;
using System.Threading;
using Umbraco.Core.Logging;
namespace Umbraco.Core.ObjectResolution
{
+ // fixme
+ // this is the last bit that needs to go
+ // however, if it goes, we're missing Resolution.Frozen even which is used here and there
+ // => how can we do it?
+
///
/// Represents the status of objects resolution.
///
diff --git a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs
deleted file mode 100644
index b5e1e14df2..0000000000
--- a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using System;
-using System.Threading;
-using LightInject;
-
-namespace Umbraco.Core.ObjectResolution
-{
- ///
- /// Base non-generic class for resolvers
- ///
- public abstract class ResolverBase
- {
- protected ResolverBase(Action resetAction)
- {
- //add itself to the internal collection
- ResolverCollection.Add(this, resetAction);
- }
-
- }
-
- ///
- /// The base class for all resolvers.
- ///
- /// The type of the concrete resolver class.
- /// Provides singleton management to all resolvers.
- public abstract class ResolverBase : ResolverBase
- where TResolver : ResolverBase
- {
-
- ///
- /// The underlying singleton object instance
- ///
- static TResolver _resolver;
-
- ///
- /// The lock for the singleton.
- ///
- ///
- /// Though resharper says this is in error, it is actually correct. We want a different lock object for each generic type.
- /// See this for details: http://confluence.jetbrains.net/display/ReSharper/Static+field+in+generic+type
- ///
- static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
-
- ///
- /// Constructor set the reset action for the underlying object
- ///
- protected ResolverBase()
- : base(() => Reset())
- {
-
- }
-
- ///
- /// Gets or sets the resolver singleton instance.
- ///
- /// The value can be set only once, and cannot be read before it has been set.
- /// value is read before it has been set, or value is set again once it has already been set.
- /// value is null.
- public static TResolver Current
- {
- get
- {
- using (new ReadLock(ResolversLock))
- {
- if (_resolver == null)
- throw new InvalidOperationException(string.Format(
- "Current has not been initialized on {0}. You must initialize Current before trying to read it.",
- typeof(TResolver).FullName));
- return _resolver;
- }
- }
-
- set
- {
- using (Resolution.Configuration)
- using (new WriteLock(ResolversLock))
- {
- if (value == null)
- throw new ArgumentNullException("value");
- if (_resolver != null)
- throw new InvalidOperationException(string.Format(
- "Current has already been initialized on {0}. It is not possible to re-initialize Current once it has been initialized.",
- typeof(TResolver).FullName));
- _resolver = value;
- }
- }
- }
-
- ///
- /// Gets a value indicating whether a the singleton instance has been set.
- ///
- public static bool HasCurrent
- {
- get
- {
- using (new ReadLock(ResolversLock))
- {
- return _resolver != null;
- }
- }
- }
-
- ///
- /// Resets the resolver singleton instance to null.
- ///
- ///
- /// To be used in unit tests. DO NOT USE THIS DURING PRODUCTION.
- ///
- ///
- /// By default this is true because we always need to reset resolution before we reset a resolver, however in some insanely rare cases like unit testing you might not want to do this.
- ///
- protected internal static void Reset(bool resetResolution = true)
- {
-
- //In order to reset a resolver, we always must reset the resolution
- if (resetResolution)
- {
- Resolution.Reset();
- }
-
- //ensure its removed from the collection
- ResolverCollection.Remove(_resolver);
-
- using (Resolution.Configuration)
- using (new WriteLock(ResolversLock))
- {
- _resolver = null;
- }
-
- }
- }
-}
diff --git a/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs b/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs
deleted file mode 100644
index 34a7ff68bc..0000000000
--- a/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-
-namespace Umbraco.Core.ObjectResolution
-{
- ///
- /// Simply used to track all ManyObjectsResolverBase instances so that we can
- /// reset them all at once really easily.
- ///
- ///
- /// Normally we'd use TypeFinding for this but because many of the resolvers are internal this won't work.
- /// We'd rather not keep a static list of them so we'll dynamically add to this list based on the base
- /// class of the ManyObjectsResolverBase.
- ///
- internal static class ResolverCollection
- {
- private static readonly ConcurrentDictionary Resolvers = new ConcurrentDictionary();
-
- ///
- /// Returns the number of resolvers created
- ///
- internal static int Count
- {
- get { return Resolvers.Count; }
- }
-
- ///
- /// Resets all resolvers
- ///
- internal static void ResetAll()
- {
- //take out each item from the bag and reset it
- var keys = Resolvers.Keys.ToArray();
- foreach (var k in keys)
- {
- Action resetAction;
- while (Resolvers.TryRemove(k, out resetAction))
- {
- //call the reset action for the resolver
- resetAction();
- }
- }
- }
-
- ///
- /// This is called when the static Reset method or a ResolverBase{T} is called.
- ///
- internal static void Remove(ResolverBase resolver)
- {
- if (resolver == null) return;
- Action action;
- Resolvers.TryRemove(resolver, out action);
- }
-
- ///
- /// Adds a resolver to the collection
- ///
- ///
- ///
- ///
- /// This is called when the creation of a ResolverBase occurs
- ///
- internal static void Add(ResolverBase resolver, Action resetAction)
- {
- Resolvers.TryAdd(resolver, resetAction);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs
deleted file mode 100644
index 7bb502e9fd..0000000000
--- a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-using System;
-using System.Threading;
-
-namespace Umbraco.Core.ObjectResolution
-{
- ///
- /// The base class for all single-object resolvers.
- ///
- /// The type of the concrete resolver class.
- /// The type of the resolved object.
- ///
- /// Resolves "single" objects ie objects for which there is only one application-wide instance, such as the MVC Controller factory.
- ///
- public abstract class SingleObjectResolverBase : ResolverBase
- where TResolved : class
- where TResolver : ResolverBase
- {
- private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
- private readonly bool _canBeNull;
- private TResolved _value;
-
- #region Constructors
-
- ///
- /// Initialize a new instance of the class.
- ///
- /// By default CanBeNull is false, so Value has to be initialized before being read,
- /// otherwise an exception will be thrown when reading it.
- protected SingleObjectResolverBase()
- : this(false)
- { }
-
- ///
- /// Initialize a new instance of the class with an instance of the resolved object.
- ///
- /// An instance of the resolved object.
- /// By default CanBeNull is false, so value has to be non-null, or Value has to be
- /// initialized before being accessed, otherwise an exception will be thrown when reading it.
- protected SingleObjectResolverBase(TResolved value)
- : this(false)
- {
- _value = value;
- }
-
- ///
- /// Initialize a new instance of the class with a value indicating whether the resolved object instance can be null.
- ///
- /// A value indicating whether the resolved object instance can be null.
- /// If CanBeNull is false, Value has to be initialized before being read,
- /// otherwise an exception will be thrown when reading it.
- protected SingleObjectResolverBase(bool canBeNull)
- {
- _canBeNull = canBeNull;
- }
-
- ///
- /// Initialize a new instance of the class with an instance of the resolved object,
- /// and a value indicating whether that instance can be null.
- ///
- /// An instance of the resolved object.
- /// A value indicating whether the resolved object instance can be null.
- /// If CanBeNull is false, value has to be non-null, or Value has to be initialized before being read,
- /// otherwise an exception will be thrown when reading it.
- protected SingleObjectResolverBase(TResolved value, bool canBeNull)
- {
- _value = value;
- _canBeNull = canBeNull;
- }
-
- #endregion
-
- ///
- /// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
- ///
- /// This is false by default and is used for some special internal resolvers.
- internal bool CanResolveBeforeFrozen { get; set; }
-
- ///
- /// Gets a value indicating whether the resolved object instance can be null.
- ///
- public bool CanBeNull
- {
- get { return _canBeNull; }
- }
-
- ///
- /// Gets a value indicating whether the resolved object instance is null.
- ///
- public virtual bool HasValue
- {
- get { return _value != null; }
- }
-
- ///
- /// Gets or sets the resolved object instance.
- ///
- ///
- /// value is set to null, but cannot be null (CanBeNull is false).
- /// value is read and is null, but cannot be null (CanBeNull is false),
- /// or value is set (read) and resolution is (not) frozen.
- protected virtual TResolved Value
- {
- get
- {
- using (Resolution.Reader(CanResolveBeforeFrozen))
- using (new ReadLock(_lock))
- {
- if (!_canBeNull && _value == null)
- throw new InvalidOperationException(string.Format(
- "Resolver {0} requires a value, and none was supplied.", this.GetType().FullName));
-
- return _value;
- }
- }
-
- set
- {
- using (Resolution.Configuration)
- using (var l = new UpgradeableReadLock(_lock))
- {
- if (!_canBeNull && value == null)
- throw new ArgumentNullException("value");
-
- l.UpgradeToWriteLock();
- _value = value;
- }
- }
- }
- }
-}
diff --git a/src/Umbraco.Core/Plugins/PluginManager.cs b/src/Umbraco.Core/Plugins/PluginManager.cs
index 09c219a0fb..606589c6dc 100644
--- a/src/Umbraco.Core/Plugins/PluginManager.cs
+++ b/src/Umbraco.Core/Plugins/PluginManager.cs
@@ -96,6 +96,8 @@ namespace Umbraco.Core.Plugins
}
}
+ // fixme - somehow we NEED to get rid of this Current accessor
+
///
/// Gets the current plugin manager.
///
@@ -111,7 +113,7 @@ namespace Umbraco.Core.Plugins
{
var appctx = ApplicationContext.Current;
var cacheProvider = appctx == null // fixme - should Current have an ApplicationCache?
- ? new NullCacheProvider()
+ ? new NullCacheProvider()
: appctx.ApplicationCache.RuntimeCache;
ProfilingLogger profilingLogger;
if (appctx == null)
diff --git a/src/Umbraco.Core/TopologicalSorter.cs b/src/Umbraco.Core/TopologicalSorter.cs
index d25e1da406..699106d7bb 100644
--- a/src/Umbraco.Core/TopologicalSorter.cs
+++ b/src/Umbraco.Core/TopologicalSorter.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
namespace Umbraco.Core
{
@@ -21,8 +20,8 @@ namespace Umbraco.Core
_vertices = new int[size];
_matrix = new int[size, size];
_numVerts = 0;
- for (int i = 0; i < size; i++)
- for (int j = 0; j < size; j++)
+ for (var i = 0; i < size; i++)
+ for (var j = 0; j < size; j++)
_matrix[i, j] = 0;
_sortedArray = new int[size]; // sorted vert labels
}
@@ -45,7 +44,7 @@ namespace Umbraco.Core
while (_numVerts > 0) // while vertices remain,
{
// get a vertex with no successors, or -1
- int currentVertex = NoSuccessors();
+ var currentVertex = NoSuccessors();
if (currentVertex == -1) // must be a cycle
throw new Exception("Graph has cycles");
@@ -66,18 +65,16 @@ namespace Umbraco.Core
// returns vert with no successors (or -1 if no such verts)
private int NoSuccessors()
{
- for (int row = 0; row < _numVerts; row++)
+ for (var row = 0; row < _numVerts; row++)
{
- bool isEdge = false; // edge from row to column in adjMat
- for (int col = 0; col < _numVerts; col++)
+ var isEdge = false; // edge from row to column in adjMat
+ for (var col = 0; col < _numVerts; col++)
{
- if (_matrix[row, col] > 0) // if edge to another,
- {
- isEdge = true;
- break; // this vertex has a successor try another
- }
+ if (_matrix[row, col] <= 0) continue;
+ isEdge = true;
+ break; // this vertex has a successor try another
}
- if (!isEdge) // if no edges, has no successors
+ if (isEdge == false) // if no edges, has no successors
return row;
}
return -1; // no
@@ -88,13 +85,13 @@ namespace Umbraco.Core
// if not last vertex, delete from vertexList
if (delVert != _numVerts - 1)
{
- for (int j = delVert; j < _numVerts - 1; j++)
+ for (var j = delVert; j < _numVerts - 1; j++)
_vertices[j] = _vertices[j + 1];
- for (int row = delVert; row < _numVerts - 1; row++)
+ for (var row = delVert; row < _numVerts - 1; row++)
MoveRowUp(row, _numVerts);
- for (int col = delVert; col < _numVerts - 1; col++)
+ for (var col = delVert; col < _numVerts - 1; col++)
MoveColLeft(col, _numVerts - 1);
}
_numVerts--; // one less vertex
@@ -102,13 +99,13 @@ namespace Umbraco.Core
private void MoveRowUp(int row, int length)
{
- for (int col = 0; col < length; col++)
+ for (var col = 0; col < length; col++)
_matrix[row, col] = _matrix[row + 1, col];
}
private void MoveColLeft(int col, int length)
{
- for (int row = 0; row < length; row++)
+ for (var row = 0; row < length; row++)
_matrix[row, col] = _matrix[row, col + 1];
}
@@ -118,9 +115,9 @@ namespace Umbraco.Core
public static IEnumerable GetSortedItems(List> fields) where T : class
{
- int[] sortOrder = GetTopologicalSortOrder(fields);
+ var sortOrder = GetTopologicalSortOrder(fields);
var list = new List();
- for (int i = 0; i < sortOrder.Length; i++)
+ for (var i = 0; i < sortOrder.Length; i++)
{
var field = fields[sortOrder[i]];
list.Add(field.Item.Value);
@@ -131,34 +128,31 @@ namespace Umbraco.Core
internal static int[] GetTopologicalSortOrder(List> fields) where T : class
{
- var g = new TopologicalSorter(fields.Count());
+ var g = new TopologicalSorter(fields.Count);
var indexes = new Dictionary();
//add vertices
- for (int i = 0; i < fields.Count(); i++)
+ for (var i = 0; i < fields.Count; i++)
{
indexes[fields[i].Alias.ToLowerInvariant()] = g.AddVertex(i);
}
//add edges
- for (int i = 0; i < fields.Count; i++)
+ for (var i = 0; i < fields.Count; i++)
{
- if (fields[i].DependsOn != null)
- {
- for (int j = 0; j < fields[i].DependsOn.Length; j++)
- {
- if (indexes.ContainsKey(fields[i].DependsOn[j].ToLowerInvariant()) == false)
- throw new IndexOutOfRangeException(
- string.Format(
- "The alias '{0}' has an invalid dependency. The dependency '{1}' does not exist in the list of aliases",
- fields[i], fields[i].DependsOn[j]));
+ if (fields[i].DependsOn == null) continue;
- g.AddEdge(i, indexes[fields[i].DependsOn[j].ToLowerInvariant()]);
- }
+ for (var j = 0; j < fields[i].DependsOn.Length; j++)
+ {
+ if (indexes.ContainsKey(fields[i].DependsOn[j].ToLowerInvariant()) == false)
+ throw new IndexOutOfRangeException(
+ $"The alias '{fields[i]}' has an invalid dependency. The dependency '{fields[i].DependsOn[j]}' does not exist in the list of aliases");
+
+ g.AddEdge(i, indexes[fields[i].DependsOn[j].ToLowerInvariant()]);
}
}
- int[] result = g.Sort();
+ var result = g.Sort();
return result;
}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index d2d3dc456d..c83be03233 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -109,6 +109,15 @@
+
+
+
+
+
+
+
+
+
@@ -208,7 +217,7 @@
-
+
@@ -321,7 +330,6 @@
-
@@ -718,8 +726,6 @@
-
-
@@ -1123,7 +1129,7 @@
-
+
@@ -1137,10 +1143,7 @@
-
-
-
diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs
index fe0a13809c..734c3a2ffc 100644
--- a/src/Umbraco.Core/UmbracoApplicationBase.cs
+++ b/src/Umbraco.Core/UmbracoApplicationBase.cs
@@ -10,20 +10,21 @@ using Umbraco.Core.Logging;
namespace Umbraco.Core
{
-
///
- /// The abstract class for the Umbraco HttpApplication
+ /// Provides an abstract base class for the Umbraco HttpApplication.
///
///
- /// This is exposed in the core so that we can have the IApplicationEventHandler in the core project so that
- /// IApplicationEventHandler's can fire/execute outside of the web contenxt (i.e. in console applications)
+ /// This is exposed in Core so that we can have the IApplicationEventHandler in the core project so that
+ /// IApplicationEventHandler's can fire/execute outside of the web contenxt (i.e. in console applications). fixme wtf?
///
public abstract class UmbracoApplicationBase : HttpApplication
{
+ private IRuntime _runtime;
+
///
- /// Gets a boot manager.
+ /// Gets a runtime.
///
- protected abstract IBootManager GetBootManager();
+ protected abstract IRuntime GetRuntime();
///
/// Gets a logger.
@@ -33,11 +34,17 @@ namespace Umbraco.Core
return Logger.CreateWithDefaultLog4NetConfiguration();
}
- ///
- /// Boots up the Umbraco application.
- ///
- internal void StartApplication(object sender, EventArgs e)
+ #region Start
+
+ // fixme? dont make much sense!
+ public event EventHandler ApplicationStarting;
+ public event EventHandler ApplicationStarted;
+
+ // internal for tests
+ internal void HandleApplicationStart(object sender, EventArgs evargs)
{
+ // NOTE: THIS IS WHERE EVERYTHING BEGINS!
+
// create the container for the application, and configure.
// the boot manager is responsible for registrations
var container = new ServiceContainer();
@@ -48,6 +55,7 @@ namespace Umbraco.Core
// (profiler etc depend on boot manager)
var logger = GetLogger();
container.RegisterInstance(logger);
+ // now it is ok to use Current.Logger
// take care of unhandled exceptions - there is nothing we can do to
// prevent the entire w3wp process to go down but at least we can try
@@ -59,71 +67,170 @@ namespace Umbraco.Core
var msg = "Unhandled exception in AppDomain";
if (isTerminating) msg += " (terminating)";
+ msg += ".";
logger.Error(msg, exception);
};
- // boot
- GetBootManager()
+ // get runtime & boot
+ _runtime = GetRuntime();
+ _runtime.Boot(container);
+
+ // this is extra that should get removed
+ _runtime
.Initialize()
- .Startup(appContext => OnApplicationStarting(sender, e))
- .Complete(appContext => OnApplicationStarted(sender, e));
+ .Startup(appContext => OnApplicationStarting(sender, evargs))
+ .Complete(appContext => OnApplicationStarted(sender, evargs));
}
- #region Events
-
- public event EventHandler ApplicationStarting;
- public event EventHandler ApplicationStarted;
-
- ///
- /// Called when the HttpApplication.Init() is fired, allows developers to subscribe to the HttpApplication events
- ///
- ///
- /// Needs to be static otherwise null refs occur - though I don't know why FIXME wtf?
- ///
- public static event EventHandler ApplicationInit;
- public static event EventHandler ApplicationError;
- public static event EventHandler ApplicationEnd;
-
-
- ///
- /// Initializes the Umbraco application
- ///
- ///
- ///
- protected void Application_Start(object sender, EventArgs e)
+ // called by ASP.NET (auto event wireup) once per app domain
+ // do NOT set instance data here - only static (see docs)
+ // sender is System.Web.HttpApplicationFactory, evargs is EventArgs.Empty
+ protected void Application_Start(object sender, EventArgs evargs)
{
Thread.CurrentThread.SanitizeThreadCulture();
- StartApplication(sender, e);
+ HandleApplicationStart(sender, evargs);
}
- ///
- /// Override init and raise the event
- ///
- ///
- /// DID YOU KNOW? The Global.asax Init call is the thing that initializes all of the httpmodules, ties up a bunch of stuff with IIS, etc...
- /// Therefore, since OWIN is an HttpModule when running in IIS/ASP.Net the OWIN startup is not executed until this method fires and by that
- /// time, Umbraco has performed it's bootup sequence.
- ///
+ #endregion
+
+ #region Init
+
+ // this event can only be static since there will be several instances of this class
+ public static event EventHandler ApplicationInit;
+
+ private void OnApplicationInit(object sender, EventArgs evargs)
+ {
+ try
+ {
+ ApplicationInit?.Invoke(sender, evargs);
+ }
+ catch (Exception ex)
+ {
+ Current.Logger.Error("Exception in an ApplicationInit event handler.", ex);
+ throw;
+ }
+ }
+
+ // called by ASP.NET for every HttpApplication instance after all modules have been created
+ // which means that this will be called *many* times for different apps when Umbraco runs
public override void Init()
{
+ // note: base.Init() is what initializes all of the httpmodules, ties up a bunch of stuff with IIS, etc...
+ // therefore, since OWIN is an HttpModule when running in IIS/ASP.Net the OWIN startup is not executed
+ // until this method fires and by that time - Umbraco has booted already
+
base.Init();
OnApplicationInit(this, new EventArgs());
}
+ #endregion
+
+ #region End
+
+ // this event can only be static since there will be several instances of this class
+ public static event EventHandler ApplicationEnd;
+
+ protected virtual void OnApplicationEnd(object sender, EventArgs evargs)
+ {
+ ApplicationEnd?.Invoke(this, EventArgs.Empty);
+ }
+
+ // internal for tests
+ internal void HandleApplicationEnd()
+ {
+ if (_runtime != null)
+ {
+ _runtime.Terminate();
+ _runtime.DisposeIfDisposable();
+ _runtime = null;
+ }
+
+ if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted) return;
+
+ // try to log the detailed shutdown message (typical asp.net hack: http://weblogs.asp.net/scottgu/433194)
+ try
+ {
+ var runtime = (HttpRuntime) typeof(HttpRuntime).InvokeMember("_theRuntime",
+ BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField,
+ null, null, null);
+ if (runtime == null)
+ return;
+
+ var shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage",
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
+ null, runtime, null);
+
+ var shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack",
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
+ null, runtime, null);
+
+ var shutdownMsg = $"Application shutdown. Details: {HostingEnvironment.ShutdownReason}\r\n\r\n_shutDownMessage={shutDownMessage}\r\n\r\n_shutDownStack={shutDownStack}";
+
+ Current.Logger.Info(shutdownMsg);
+ }
+ catch (Exception)
+ {
+ //if for some reason that fails, then log the normal output
+ Current.Logger.Info("Application shutdown. Reason: " + HostingEnvironment.ShutdownReason);
+ }
+ }
+
+ // called by ASP.NET (auto event wireup) once per app domain
+ // sender is System.Web.HttpApplicationFactory, evargs is EventArgs.Empty
+ protected void Application_End(object sender, EventArgs evargs)
+ {
+ HandleApplicationEnd();
+ OnApplicationEnd(sender, evargs);
+ LogManager.Shutdown();
+ }
+
+ #endregion
+
+ #region Error
+
+ // this event can only be static since there will be several instances of this class
+ public static event EventHandler ApplicationError;
+
+ protected virtual void OnApplicationError(object sender, EventArgs evargs)
+ {
+ ApplicationError?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void HandleApplicationError()
+ {
+ var exception = Server.GetLastError();
+
+ // ignore HTTP errors
+ if (exception.GetType() == typeof(HttpException)) return;
+
+ Current.Logger.Error("An unhandled exception occurred.", exception);
+ }
+
+ // called by ASP.NET (auto event wireup) at any phase in the application life cycle
+ protected void Application_Error(object sender, EventArgs e)
+ {
+ // when unhandled errors occur
+ HandleApplicationError();
+ OnApplicationError(sender, e);
+ }
+
+ #endregion
+
+
///
/// Developers can override this method to modify objects on startup
///
///
- ///
- protected virtual void OnApplicationStarting(object sender, EventArgs e)
+ ///
+ protected virtual void OnApplicationStarting(object sender, EventArgs evargs)
{
try
{
- ApplicationStarting?.Invoke(sender, e);
+ ApplicationStarting?.Invoke(sender, evargs);
}
catch (Exception ex)
{
- LogHelper.Error("An error occurred in an ApplicationStarting event handler", ex);
+ Current.Logger.Error("An error occurred in an ApplicationStarting event handler", ex);
throw;
}
}
@@ -132,125 +239,18 @@ namespace Umbraco.Core
/// Developers can override this method to do anything they need to do once the application startup routine is completed.
///
///
- ///
- protected virtual void OnApplicationStarted(object sender, EventArgs e)
+ ///
+ protected virtual void OnApplicationStarted(object sender, EventArgs evargs)
{
try
{
- ApplicationStarted?.Invoke(sender, e);
+ ApplicationStarted?.Invoke(sender, evargs);
}
catch (Exception ex)
{
- LogHelper.Error("An error occurred in an ApplicationStarted event handler", ex);
+ Current.Logger.Error("An error occurred in an ApplicationStarted event handler", ex);
throw;
}
}
-
- ///
- /// Called to raise the ApplicationInit event
- ///
- ///
- ///
- private void OnApplicationInit(object sender, EventArgs e)
- {
- try
- {
- ApplicationInit?.Invoke(sender, e);
- }
- catch (Exception ex)
- {
- LogHelper.Error("An error occurred in an ApplicationInit event handler", ex);
- throw;
- }
- }
-
- ///
- /// A method that can be overridden to invoke code when the application has an error.
- ///
- ///
- ///
- protected virtual void OnApplicationError(object sender, EventArgs e)
- {
- ApplicationError?.Invoke(this, EventArgs.Empty);
- }
-
- protected void Application_Error(object sender, EventArgs e)
- {
- // Code that runs when an unhandled error occurs
-
- // Get the exception object.
- var exc = Server.GetLastError();
-
- // Ignore HTTP errors
- if (exc.GetType() == typeof(HttpException))
- {
- return;
- }
-
- Current.Logger.Error("An unhandled exception occurred", exc);
-
- OnApplicationError(sender, e);
- }
-
- ///
- /// A method that can be overridden to invoke code when the application shuts down.
- ///
- ///
- ///
- protected virtual void OnApplicationEnd(object sender, EventArgs e)
- {
- ApplicationEnd?.Invoke(this, EventArgs.Empty);
- }
-
- protected void Application_End(object sender, EventArgs e)
- {
- if (SystemUtilities.GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
- {
- //Try to log the detailed shutdown message (typical asp.net hack: http://weblogs.asp.net/scottgu/433194)
- try
- {
- var runtime = (HttpRuntime)typeof(HttpRuntime).InvokeMember("_theRuntime",
- BindingFlags.NonPublic
- | BindingFlags.Static
- | BindingFlags.GetField,
- null,
- null,
- null);
- if (runtime == null)
- return;
-
- var shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage",
- BindingFlags.NonPublic
- | BindingFlags.Instance
- | BindingFlags.GetField,
- null,
- runtime,
- null);
-
- var shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack",
- BindingFlags.NonPublic
- | BindingFlags.Instance
- | BindingFlags.GetField,
- null,
- runtime,
- null);
-
- var shutdownMsg = $"{HostingEnvironment.ShutdownReason}\r\n\r\n_shutDownMessage={shutDownMessage}\r\n\r\n_shutDownStack={shutDownStack}";
-
- Current.Logger.Info("Application shutdown. Details: " + shutdownMsg);
- }
- catch (Exception)
- {
- //if for some reason that fails, then log the normal output
- Current.Logger.Info("Application shutdown. Reason: " + HostingEnvironment.ShutdownReason);
- }
- }
- OnApplicationEnd(sender, e);
-
- // last thing to do is shutdown log4net
- LogManager.Shutdown();
- }
-
- #endregion
}
}
diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs
new file mode 100644
index 0000000000..8415755ebe
--- /dev/null
+++ b/src/Umbraco.Tests/Components/ComponentTests.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using LightInject;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core.Components;
+using Umbraco.Core.DependencyInjection;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Tests.Components
+{
+ [TestFixture]
+ public class ComponentTests
+ {
+ private static readonly List Composed = new List();
+ private static readonly List Initialized = new List();
+
+ [TearDown]
+ public void TearDown()
+ {
+ Current.Reset();
+ }
+
+ [Test]
+ public void Boot()
+ {
+ var container = new ServiceContainer();
+ container.ConfigureUmbracoCore();
+
+ var logger = Mock.Of();
+ var profiler = new LogProfiler(logger);
+ container.RegisterInstance(logger);
+ container.RegisterInstance(profiler);
+ container.RegisterInstance(new ProfilingLogger(logger, profiler));
+
+ var thing = new BootLoader(container);
+ Composed.Clear();
+ thing.Boot(new [] { typeof (Component1), typeof (Component2), typeof (Component3), typeof (Component4) });
+ Assert.AreEqual(4, Composed.Count);
+ Assert.AreEqual(typeof(Component1), Composed[0]);
+ Assert.AreEqual(typeof(Component4), Composed[1]);
+ Assert.AreEqual(typeof(Component2), Composed[2]);
+ Assert.AreEqual(typeof(Component3), Composed[3]);
+ }
+
+ [Test]
+ public void BrokenDependency()
+ {
+ var container = new ServiceContainer();
+ container.ConfigureUmbracoCore();
+
+ var logger = Mock.Of();
+ var profiler = new LogProfiler(logger);
+ container.RegisterInstance(logger);
+ container.RegisterInstance(profiler);
+ container.RegisterInstance(new ProfilingLogger(logger, profiler));
+
+ var thing = new BootLoader(container);
+ Composed.Clear();
+ try
+ {
+ thing.Boot(new[] { typeof(Component1), typeof(Component2), typeof(Component3) });
+ Assert.Fail("Expected exception.");
+ }
+ catch (Exception e)
+ {
+ Assert.AreEqual("Broken component dependency: Umbraco.Tests.Components.ComponentTests+Component2 -> Umbraco.Tests.Components.ComponentTests+Component4.", e.Message);
+ }
+ }
+
+ [Test]
+ public void Initialize()
+ {
+ var container = new ServiceContainer();
+ container.ConfigureUmbracoCore();
+
+ container.Register();
+
+ var logger = Mock.Of();
+ var profiler = new LogProfiler(logger);
+ container.RegisterInstance(logger);
+ container.RegisterInstance(profiler);
+ container.RegisterInstance(new ProfilingLogger(logger, profiler));
+
+ var thing = new BootLoader(container);
+ Composed.Clear();
+ thing.Boot(new[] { typeof(Component1), typeof(Component5) });
+ Assert.AreEqual(2, Composed.Count);
+ Assert.AreEqual(typeof(Component1), Composed[0]);
+ Assert.AreEqual(typeof(Component5), Composed[1]);
+ Assert.AreEqual(1, Initialized.Count);
+ Assert.AreEqual("Umbraco.Tests.Components.ComponentTests+SomeResource", Initialized[0]);
+ }
+
+ public class Component1 : UmbracoComponentBase
+ {
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed.Add(GetType());
+ }
+ }
+
+ [RequireComponent(typeof(Component4))]
+ public class Component2 : UmbracoComponentBase, IUmbracoCoreComponent
+ {
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed.Add(GetType());
+ }
+ }
+
+ public class Component3 : UmbracoComponentBase, IUmbracoUserComponent
+ {
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed.Add(GetType());
+ }
+ }
+
+ public class Component4 : UmbracoComponentBase
+ {
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed.Add(GetType());
+ }
+ }
+
+ public class Component5 : UmbracoComponentBase
+ {
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed.Add(GetType());
+ }
+
+ public void Initialize(ISomeResource resource)
+ {
+ Initialized.Add(resource.GetType().FullName);
+ }
+ }
+
+ public interface ISomeResource { }
+
+ public class SomeResource : ISomeResource { }
+ }
+}
diff --git a/src/Umbraco.Tests/Resolvers/ActionCollectionTests.cs b/src/Umbraco.Tests/DependencyInjection/ActionCollectionTests.cs
similarity index 95%
rename from src/Umbraco.Tests/Resolvers/ActionCollectionTests.cs
rename to src/Umbraco.Tests/DependencyInjection/ActionCollectionTests.cs
index dc75cd0431..755588b7b8 100644
--- a/src/Umbraco.Tests/Resolvers/ActionCollectionTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/ActionCollectionTests.cs
@@ -1,15 +1,10 @@
using System.Linq;
-using LightInject;
-using Moq;
using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.DependencyInjection;
-using Umbraco.Core.ObjectResolution;
-using Umbraco.Web.UI.Pages;
using Umbraco.Web;
+using Umbraco.Web.UI.Pages;
using Umbraco.Web._Legacy.Actions;
-namespace Umbraco.Tests.Resolvers
+namespace Umbraco.Tests.DependencyInjection
{
[TestFixture]
public class ActionCollectionTests : ResolverBaseTest
diff --git a/src/Umbraco.Tests/Resolvers/CollectionBuildersTests.cs b/src/Umbraco.Tests/DependencyInjection/CollectionBuildersTests.cs
similarity index 99%
rename from src/Umbraco.Tests/Resolvers/CollectionBuildersTests.cs
rename to src/Umbraco.Tests/DependencyInjection/CollectionBuildersTests.cs
index 4fbf66aa96..b2e4efef63 100644
--- a/src/Umbraco.Tests/Resolvers/CollectionBuildersTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/CollectionBuildersTests.cs
@@ -5,7 +5,7 @@ using LightInject;
using NUnit.Framework;
using Umbraco.Core.DependencyInjection;
-namespace Umbraco.Tests.Resolvers
+namespace Umbraco.Tests.DependencyInjection
{
[TestFixture]
public class CollectionBuildersTests
diff --git a/src/Umbraco.Tests/Resolvers/LazyCollectionBuilderTests.cs b/src/Umbraco.Tests/DependencyInjection/LazyCollectionBuilderTests.cs
similarity index 99%
rename from src/Umbraco.Tests/Resolvers/LazyCollectionBuilderTests.cs
rename to src/Umbraco.Tests/DependencyInjection/LazyCollectionBuilderTests.cs
index fcaac5a80c..d7186d8b67 100644
--- a/src/Umbraco.Tests/Resolvers/LazyCollectionBuilderTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/LazyCollectionBuilderTests.cs
@@ -6,7 +6,7 @@ using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
-namespace Umbraco.Tests.Resolvers
+namespace Umbraco.Tests.DependencyInjection
{
[TestFixture]
public class LazyCollectionBuilderTests
diff --git a/src/Umbraco.Tests/Resolvers/PackageActionsResolverTests.cs b/src/Umbraco.Tests/DependencyInjection/PackageActionCollectionTests.cs
similarity index 89%
rename from src/Umbraco.Tests/Resolvers/PackageActionsResolverTests.cs
rename to src/Umbraco.Tests/DependencyInjection/PackageActionCollectionTests.cs
index 3e3bebb507..a916f40a98 100644
--- a/src/Umbraco.Tests/Resolvers/PackageActionsResolverTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/PackageActionCollectionTests.cs
@@ -1,90 +1,88 @@
-using System;
-using System.Linq;
-using System.Xml;
-using LightInject;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.DependencyInjection;
-using Umbraco.Core.Plugins;
-using Umbraco.Core.ObjectResolution;
-using Umbraco.Core._Legacy.PackageActions;
-
-namespace Umbraco.Tests.Resolvers
-{
- [TestFixture]
- public class PackageActionsResolverTests : ResolverBaseTest
- {
- // NOTE
- // ManyResolverTests ensure that we'll get our actions back and PackageActionResolver works,
- // so all we're testing here is that plugin manager _does_ find our package actions
- // which should be ensured by PlugingManagerTests anyway, so this is useless?
- [Test]
- public void FindAllPackageActions()
- {
- var container = new ServiceContainer();
- container.ConfigureUmbracoCore();
-
- PackageActionCollectionBuilder.Register(container)
- .AddProducer(() => PluginManager.ResolvePackageActions());
-
- var actions = Current.PackageActions;
- Assert.AreEqual(2, actions.Count());
-
- // order is unspecified, but both must be there
- bool hasAction1 = actions.ElementAt(0) is PackageAction1 || actions.ElementAt(1) is PackageAction1;
- bool hasAction2 = actions.ElementAt(0) is PackageAction2 || actions.ElementAt(1) is PackageAction2;
- Assert.IsTrue(hasAction1);
- Assert.IsTrue(hasAction2);
- }
-
- #region Classes for tests
-
- public class PackageAction1 : IPackageAction
- {
- public bool Execute(string packageName, XmlNode xmlData)
- {
- throw new NotImplementedException();
- }
-
- public string Alias()
- {
- return "pa1";
- }
-
- public bool Undo(string packageName, XmlNode xmlData)
- {
- throw new NotImplementedException();
- }
-
- public XmlNode SampleXml()
- {
- throw new NotImplementedException();
- }
- }
-
- public class PackageAction2 : IPackageAction
- {
- public bool Execute(string packageName, XmlNode xmlData)
- {
- throw new NotImplementedException();
- }
-
- public string Alias()
- {
- return "pa2";
- }
-
- public bool Undo(string packageName, XmlNode xmlData)
- {
- throw new NotImplementedException();
- }
-
- public XmlNode SampleXml()
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
- }
+using System;
+using System.Linq;
+using System.Xml;
+using LightInject;
+using NUnit.Framework;
+using Umbraco.Core.DependencyInjection;
+using Umbraco.Core.Plugins;
+using Umbraco.Core._Legacy.PackageActions;
+
+namespace Umbraco.Tests.DependencyInjection
+{
+ [TestFixture]
+ public class PackageActionCollectionTests : ResolverBaseTest
+ {
+ // NOTE
+ // ManyResolverTests ensure that we'll get our actions back and PackageActionResolver works,
+ // so all we're testing here is that plugin manager _does_ find our package actions
+ // which should be ensured by PlugingManagerTests anyway, so this is useless?
+ [Test]
+ public void FindAllPackageActions()
+ {
+ var container = new ServiceContainer();
+ container.ConfigureUmbracoCore();
+
+ PackageActionCollectionBuilder.Register(container)
+ .AddProducer(() => PluginManager.ResolvePackageActions());
+
+ var actions = Current.PackageActions;
+ Assert.AreEqual(2, actions.Count());
+
+ // order is unspecified, but both must be there
+ bool hasAction1 = actions.ElementAt(0) is PackageAction1 || actions.ElementAt(1) is PackageAction1;
+ bool hasAction2 = actions.ElementAt(0) is PackageAction2 || actions.ElementAt(1) is PackageAction2;
+ Assert.IsTrue(hasAction1);
+ Assert.IsTrue(hasAction2);
+ }
+
+ #region Classes for tests
+
+ public class PackageAction1 : IPackageAction
+ {
+ public bool Execute(string packageName, XmlNode xmlData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string Alias()
+ {
+ return "pa1";
+ }
+
+ public bool Undo(string packageName, XmlNode xmlData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public XmlNode SampleXml()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class PackageAction2 : IPackageAction
+ {
+ public bool Execute(string packageName, XmlNode xmlData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string Alias()
+ {
+ return "pa2";
+ }
+
+ public bool Undo(string packageName, XmlNode xmlData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public XmlNode SampleXml()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Resolvers/ResolverBaseTest.cs b/src/Umbraco.Tests/DependencyInjection/ResolverBaseTest.cs
similarity index 73%
rename from src/Umbraco.Tests/Resolvers/ResolverBaseTest.cs
rename to src/Umbraco.Tests/DependencyInjection/ResolverBaseTest.cs
index bb30d46883..2748ab83a5 100644
--- a/src/Umbraco.Tests/Resolvers/ResolverBaseTest.cs
+++ b/src/Umbraco.Tests/DependencyInjection/ResolverBaseTest.cs
@@ -2,18 +2,15 @@ using System.Collections.Generic;
using System.Reflection;
using Moq;
using NUnit.Framework;
-using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Plugins;
-using Umbraco.Core.Profiling;
-using Umbraco.Core._Legacy.PackageActions;
-namespace Umbraco.Tests.Resolvers
+namespace Umbraco.Tests.DependencyInjection
{
- public abstract class ResolverBaseTest
+ public abstract class ResolverBaseTest // fixme rename, do something!
{
protected PluginManager PluginManager { get; private set; }
protected ProfilingLogger ProfilingLogger { get; private set; }
@@ -38,15 +35,10 @@ namespace Umbraco.Tests.Resolvers
Current.Reset();
}
- protected virtual IEnumerable AssembliesToScan
- {
- get
+ protected virtual IEnumerable AssembliesToScan
+ => new[]
{
- return new[]
- {
- this.GetType().Assembly // this assembly only
- };
- }
- }
+ GetType().Assembly // this assembly only
+ };
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/DependencyInjection/TempTests.cs b/src/Umbraco.Tests/DependencyInjection/TempTests.cs
index 88a62e4769..cf933ca3d2 100644
--- a/src/Umbraco.Tests/DependencyInjection/TempTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/TempTests.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using LightInject;
+using LightInject;
using NUnit.Framework;
using Umbraco.Core.DependencyInjection;
using Umbraco.Web.Routing;
diff --git a/src/Umbraco.Tests/Resolvers/XsltExtensionsResolverTests.cs b/src/Umbraco.Tests/DependencyInjection/XsltExtensionsResolverTests.cs
similarity index 83%
rename from src/Umbraco.Tests/Resolvers/XsltExtensionsResolverTests.cs
rename to src/Umbraco.Tests/DependencyInjection/XsltExtensionsResolverTests.cs
index 913e63acb0..a3d1c0b742 100644
--- a/src/Umbraco.Tests/Resolvers/XsltExtensionsResolverTests.cs
+++ b/src/Umbraco.Tests/DependencyInjection/XsltExtensionsResolverTests.cs
@@ -1,58 +1,49 @@
-using System.Linq;
-using LightInject;
-using Moq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Macros;
-using Umbraco.Core.ObjectResolution;
-using Umbraco.Core.Profiling;
-using Umbraco.Web;
-using Umbraco.Web.Macros;
-using umbraco;
-using Umbraco.Web._Legacy.Actions;
-
-namespace Umbraco.Tests.Resolvers
-{
- [TestFixture]
- public class XsltExtensionsResolverTests : ResolverBaseTest
- {
- // NOTE
- // ManyResolverTests ensure that we'll get our actions back and ActionsResolver works,
- // so all we're testing here is that plugin manager _does_ find our actions
- // which should be ensured by PlugingManagerTests anyway, so this is useless?
- // maybe not as it seems to handle the "instance" thing... so we test that we respect the singleton?
- [Test]
- public void Find_All_Extensions()
- {
- var container = new ServiceContainer();
- var builder = new XsltExtensionCollectionBuilder(container);
- builder.AddExtensionObjectProducer(() => PluginManager.ResolveXsltExtensions());
- var extensions = builder.CreateCollection();
-
- Assert.AreEqual(3, extensions.Count());
-
- Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof (XsltEx1)));
- Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof(XsltEx2)));
- Assert.AreEqual("test1", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx1)).Namespace);
- Assert.AreEqual("test2", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx2)).Namespace);
- }
-
- #region Classes for tests
-
- [Umbraco.Core.Macros.XsltExtension("test1")]
- public class XsltEx1
- {
-
- }
-
- //test with legacy one
- [umbraco.XsltExtension("test2")]
- public class XsltEx2
- {
- }
-
- #endregion
- }
+using System.Linq;
+using LightInject;
+using NUnit.Framework;
+using Umbraco.Core.Macros;
+using Umbraco.Web;
+
+namespace Umbraco.Tests.DependencyInjection
+{
+ [TestFixture]
+ public class XsltExtensionsResolverTests : ResolverBaseTest
+ {
+ // NOTE
+ // ManyResolverTests ensure that we'll get our actions back and ActionsResolver works,
+ // so all we're testing here is that plugin manager _does_ find our actions
+ // which should be ensured by PlugingManagerTests anyway, so this is useless?
+ // maybe not as it seems to handle the "instance" thing... so we test that we respect the singleton?
+ [Test]
+ public void Find_All_Extensions()
+ {
+ var container = new ServiceContainer();
+ var builder = new XsltExtensionCollectionBuilder(container);
+ builder.AddExtensionObjectProducer(() => PluginManager.ResolveXsltExtensions());
+ var extensions = builder.CreateCollection();
+
+ Assert.AreEqual(3, extensions.Count());
+
+ Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof (XsltEx1)));
+ Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof(XsltEx2)));
+ Assert.AreEqual("test1", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx1)).Namespace);
+ Assert.AreEqual("test2", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx2)).Namespace);
+ }
+
+ #region Classes for tests
+
+ [Umbraco.Core.Macros.XsltExtension("test1")]
+ public class XsltEx1
+ {
+
+ }
+
+ //test with legacy one
+ [umbraco.XsltExtension("test2")]
+ public class XsltEx2
+ {
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Resolvers/ResolutionTests.cs b/src/Umbraco.Tests/Resolvers/ResolutionTests.cs
deleted file mode 100644
index 9dc3166616..0000000000
--- a/src/Umbraco.Tests/Resolvers/ResolutionTests.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Umbraco.Core;
-using Umbraco.Core.ObjectResolution;
-using NUnit.Framework;
-
-namespace Umbraco.Tests.Resolvers
-{
- [TestFixture]
- public class ResolutionTests
- {
- [SetUp]
- public void Setup()
- {
- }
-
- [TearDown]
- public void TearDown()
- {
- BaseResolver.Reset();
- BaseResolver2.Reset();
- BaseResolver3.Reset();
- }
-
- #region Resolvers and Resolved
-
- class BaseResolver : ResolverBase
- { }
-
- class BaseResolver2 : ResolverBase
- { }
-
- class BaseResolver3 : ResolverBase
- { }
-
- #endregion
-
- #region Test Resolution
-
- [Test]
- public void ResolutionCanFreeze()
- {
- Resolution.Freeze();
- Assert.IsTrue(Resolution.IsFrozen);
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void ResolutionCannotFreezeAgain()
- {
- Resolution.Freeze();
- Resolution.Freeze(); // throws
- }
-
- [Test]
- public void ResolutionCanReset()
- {
- Resolution.Freeze();
- Resolution.Reset();
- Assert.IsFalse(Resolution.IsFrozen);
- }
-
- [Test]
- public void ResolutionCanConfigureBeforeFreeze()
- {
- using (Resolution.Configuration)
- { }
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void ResolutionCannotConfigureOnceFrozen()
- {
- Resolution.Freeze();
-
- using (Resolution.Configuration) // throws
- { }
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void ResolutionCanDetectIfNotFrozen()
- {
- using (Resolution.Reader()) // throws
- {}
- }
-
- [Test]
- public void ResolutionCanEnsureIsFrozen()
- {
- Resolution.Freeze();
- using (Resolution.Reader()) // ok
- {}
- }
-
- [Test]
- public void ResolutionFrozenEventTriggers()
- {
- int count = 0;
- object copySender = null;
- EventArgs copyArgs = null;
- Resolution.Frozen += (sender, args) =>
- {
- copySender = sender;
- copyArgs = args;
- count++;
- };
- Assert.AreEqual(0, count);
- Resolution.Freeze();
-
- Assert.AreEqual(1, count);
- Assert.IsNull(copySender);
- Assert.IsNull(copyArgs);
- }
-
- [Test]
- public void ResolutionResetClearsFrozenEvent()
- {
- int count = 0;
- Resolution.Frozen += (sender, args) =>
- {
- count++;
- };
- Resolution.Freeze();
- Assert.AreEqual(1, count);
-
- Resolution.Reset();
-
- Resolution.Freeze();
- Assert.AreEqual(1, count);
- }
-
- #endregion
-
- #region Test ResolverBase
-
- // unused
-
- [Test]
- public void BaseResolverCurrentCanBeNullOnFreeze()
- {
- // does not throw, even though BaseResolver.Current is not initialized
- Resolution.Freeze();
- }
-
- // set
-
- [Test]
- public void BaseResolverCurrentCanSetBeforeFreeze()
- {
- BaseResolver.Current = new BaseResolver();
- }
-
- [Test]
- [ExpectedException(typeof(ArgumentNullException))]
- public void BaseResolverCurrentCannotSetToNull()
- {
- BaseResolver.Current = null; // throws
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void BaseResolverCurrentCannotSetAgain()
- {
- BaseResolver.Current = new BaseResolver();
-
- // cannot set again
- BaseResolver.Current = new BaseResolver(); // throws
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void BaseResolverCurrentCannotSetOnceFrozen()
- {
- Resolution.Freeze();
-
- // cannot set once frozen
- BaseResolver.Current = new BaseResolver(); // throws
- }
-
- // get
-
- // ignore: see BaseResolverCurrentCanGetBeforeFreeze
- //[Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void BaseResolverCurrentCannotGetBeforeFreeze()
- {
- BaseResolver.Current = new BaseResolver();
-
- // cannot get before freeze
- var resolver = BaseResolver.Current; // throws
- }
-
- [Test]
- public void BaseResolverCurrentCanGetBeforeFreeze()
- {
- BaseResolver.Current = new BaseResolver();
- var resolver = BaseResolver.Current;
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void BaseResolverCurrentCannotGetIfNotSet()
- {
- Resolution.Freeze();
-
- // cannot get before freeze
- var resolver = BaseResolver.Current; // throws
- }
-
- [Test]
- public void BaseResolverCurrentCanGetOnceFrozen()
- {
- BaseResolver.Current = new BaseResolver();
- Resolution.Freeze();
- var resolver = BaseResolver.Current;
- }
-
- #endregion
-
- [Test]
- public void Resolver_Collection_Is_Updated()
- {
- BaseResolver.Current = new BaseResolver();
- BaseResolver2.Current = new BaseResolver2();
- BaseResolver3.Current = new BaseResolver3();
- Assert.AreEqual(3, ResolverCollection.Count);
- }
-
- [Test]
- public void Resolver_Collection_Is_Reset()
- {
- BaseResolver.Current = new BaseResolver();
- BaseResolver2.Current = new BaseResolver2();
- BaseResolver3.Current = new BaseResolver3();
-
- ResolverCollection.ResetAll();
-
- Assert.AreEqual(0, ResolverCollection.Count);
- Assert.Throws(() =>
- {
- var c = BaseResolver.Current;
- });
- Assert.Throws(() =>
- {
- var c = BaseResolver2.Current;
- });
- Assert.Throws(() =>
- {
- var c = BaseResolver3.Current;
- });
-
- //this should not error!
- BaseResolver.Current = new BaseResolver();
- BaseResolver2.Current = new BaseResolver2();
- BaseResolver3.Current = new BaseResolver3();
-
- Assert.Pass();
- }
- }
-}
diff --git a/src/Umbraco.Tests/Resolvers/SingleResolverTests.cs b/src/Umbraco.Tests/Resolvers/SingleResolverTests.cs
deleted file mode 100644
index c97360307c..0000000000
--- a/src/Umbraco.Tests/Resolvers/SingleResolverTests.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using Umbraco.Core;
-using Umbraco.Core.ObjectResolution;
-using NUnit.Framework;
-
-namespace Umbraco.Tests.Resolvers
-{
- [TestFixture]
- public class SingleResolverTests
- {
- [SetUp]
- public void Setup()
- {
- SingleResolver.Reset();
- }
-
- [TearDown]
- public void TearDown()
- {
- SingleResolver.Reset();
- }
-
- #region Resolvers and Resolved
-
- public class Resolved
- { }
-
- public sealed class SingleResolver : SingleObjectResolverBase
- {
- public SingleResolver()
- : base()
- { }
-
- public SingleResolver(bool canBeNull)
- : base(canBeNull)
- { }
-
- public SingleResolver(Resolved value)
- : base(value)
- { }
-
- public SingleResolver(Resolved value, bool canBeNull)
- : base(value, canBeNull)
- { }
-
- public Resolved Resolved { get { return Value; } set { Value = value; } }
- }
-
- #endregion
-
- #region Test SingleResolver
-
- [Test]
- public void SingleResolverCanSetValueBeforeFreeze()
- {
- var resolver = new SingleResolver();
- resolver.Resolved = new Resolved();
- }
-
- [Test] // is this normal?
- public void SingleResolverCanInitializeWithNullValueEvenIfCannotBeNull()
- {
- var resolver = new SingleResolver(null, false);
- }
-
- [Test]
- [ExpectedException(typeof(ArgumentNullException))]
- public void SingleResolverCannotSetValueToNullIfCannotBeNull()
- {
- var resolver = new SingleResolver();
- resolver.Resolved = null; // throws
- }
-
- [Test]
- public void SingleResolverCanSetValueToNullIfCanBeNull()
- {
- var resolver = new SingleResolver(true);
- resolver.Resolved = null;
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void SingleResolverCannotSetValueOnceFrozen()
- {
- var resolver = new SingleResolver();
- Resolution.Freeze();
- resolver.Resolved = new Resolved(); // throws
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void SingleResolverCannotGetValueBeforeFreeze()
- {
- var resolver = new SingleResolver();
- resolver.Resolved = new Resolved();
- var resolved = resolver.Resolved; // throws
- }
-
- [Test]
- public void SingleResolverCanGetValueOnceFrozen()
- {
- var resolver = new SingleResolver();
- var resolved = resolver.Resolved = new Resolved();
- Resolution.Freeze();
- Assert.AreSame(resolved, resolver.Resolved);
- }
-
- [Test]
- [ExpectedException(typeof(InvalidOperationException))]
- public void SingleResolverCannotGetNullValueIfCannotBeNull()
- {
- var resolver = new SingleResolver();
- Resolution.Freeze();
- var resolved = resolver.Resolved; // throws
- }
-
- [Test]
- public void SingleResolverCanGetNullValueIfCanBeNull()
- {
- var resolver = new SingleResolver(true);
- Resolution.Freeze();
- Assert.IsNull(resolver.Resolved);
- }
-
- #endregion
- }
-}
diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
index b25d6bfa55..c4fbc775f3 100644
--- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
+++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
@@ -29,7 +29,7 @@ namespace Umbraco.Tests.Routing
SettingsForTests.UmbracoPath = "~/umbraco";
- var webBoot = new WebBootManager(new UmbracoApplication(), new ProfilingLogger(Mock.Of(), Mock.Of()), true);
+ var webBoot = new WebRuntime(new UmbracoApplication(), new ProfilingLogger(Mock.Of(), Mock.Of()), true);
//webBoot.Initialize();
//webBoot.Startup(null); -> don't call startup, we don't want any other application event handlers to bind for this test.
//webBoot.Complete(null);
diff --git a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
similarity index 57%
rename from src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs
rename to src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
index 49bf42d9e0..612ea9d54d 100644
--- a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs
+++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
@@ -1,172 +1,218 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Web;
-using Examine;
-using LightInject;
-using Moq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Logging;
-using Umbraco.Core.ObjectResolution;
-using Umbraco.Core.Persistence.SqlSyntax;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Core.Configuration.UmbracoSettings;
-using Umbraco.Core.DependencyInjection;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Profiling;
-using Umbraco.Core.Services;
-using UmbracoExamine;
-
-namespace Umbraco.Tests.BootManagers
-{
- [TestFixture]
- public class CoreBootManagerTests : BaseUmbracoConfigurationTest
- {
- [TearDown]
- public override void TearDown()
- {
- base.TearDown();
-
- ResolverCollection.ResetAll();
- TestApplicationEventHandler.Reset();
- Resolution.Reset();
-
- Current.Reset();
- }
-
-
- ///
- /// test application using a CoreBootManager instance to boot
- ///
- public class TestApp : UmbracoApplicationBase
- {
- private readonly ILogger _logger = Mock.Of();
-
- protected override IBootManager GetBootManager()
- {
- return new TestBootManager(this, new ProfilingLogger(_logger, Mock.Of()));
- }
-
- protected override ILogger GetLogger()
- {
- return _logger;
- }
- }
-
- ///
- /// Test boot manager to add a custom application event handler
- ///
- public class TestBootManager : CoreBootManager
- {
- public TestBootManager(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
- : base(umbracoApplication, logger)
- { }
-
- internal override void ConfigureCoreServices(ServiceContainer container)
- {
- base.ConfigureCoreServices(container);
-
- container.Register(factory => SettingsForTests.GetDefault());
- container.Register(factory => new DatabaseContext(
- factory.GetInstance(),
- factory.GetInstance()), new PerContainerLifetime());
- container.RegisterSingleton();
- }
- }
-
- ///
- /// test event handler
- ///
- public class TestApplicationEventHandler : DisposableObject, IApplicationEventHandler
- {
- public static void Reset()
- {
- Initialized = false;
- Starting = false;
- Started = false;
- HasBeenDisposed = false;
- }
-
- public static bool Initialized;
- public static bool Starting;
- public static bool Started;
- public static bool HasBeenDisposed;
-
- public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
- {
- Initialized = true;
- }
-
- public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
- {
- Starting = true;
- }
-
- public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
- {
- Started = true;
- }
-
- protected override void DisposeResources()
- {
- HasBeenDisposed = true;
- }
- }
-
- [Test]
- public void Disposes_App_Startup_Handlers_After_Startup()
- {
- using (var app = new TestApp())
- {
- app.StartApplication(app, new EventArgs());
-
- Assert.IsTrue(TestApplicationEventHandler.HasBeenDisposed);
- }
- }
-
- [Test]
- public void Handle_IApplicationEventHandler_Objects_Outside_Web_Context()
- {
- using (var app = new TestApp())
- {
- app.StartApplication(app, new EventArgs());
-
- Assert.IsTrue(TestApplicationEventHandler.Initialized);
- Assert.IsTrue(TestApplicationEventHandler.Starting);
- Assert.IsTrue(TestApplicationEventHandler.Started);
- }
- }
-
- [Test]
- public void Raises_Starting_Events()
- {
- using (var app = new TestApp())
- {
- EventHandler starting = (sender, args) =>
- {
- Assert.IsTrue(TestApplicationEventHandler.Initialized);
- Assert.IsTrue(TestApplicationEventHandler.Starting);
- Assert.IsFalse(TestApplicationEventHandler.Started);
- };
- EventHandler started = (sender, args) =>
- {
- Assert.IsTrue(TestApplicationEventHandler.Initialized);
- Assert.IsTrue(TestApplicationEventHandler.Starting);
- Assert.IsTrue(TestApplicationEventHandler.Started);
- };
-
- app.ApplicationStarting += starting;
- app.ApplicationStarted += started;
-
- app.StartApplication(app, new EventArgs());
-
- app.ApplicationStarting -= starting;
- app.ApplicationStarting -= started;
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using LightInject;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Components;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.DependencyInjection;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence;
+using Umbraco.Tests.TestHelpers;
+using UmbracoExamine;
+
+namespace Umbraco.Tests.Runtimes
+{
+ [TestFixture]
+ public class CoreRuntimeTests : BaseUmbracoConfigurationTest
+ {
+ [TearDown] // fixme TearDown is INHERITED
+ public override void TearDown()
+ {
+ base.TearDown();
+
+ TestApplicationEventHandler.Reset();
+
+ Current.Reset();
+ }
+
+ // test application
+ public class TestUmbracoApplication : UmbracoApplicationBase
+ {
+ private readonly ILogger _logger = Mock.Of();
+
+ protected override IRuntime GetRuntime()
+ {
+ return new TestRuntime(this, new ProfilingLogger(_logger, Mock.Of()));
+ }
+
+ protected override ILogger GetLogger()
+ {
+ return _logger;
+ }
+ }
+
+ // test runtime - inheriting from core runtime
+ public class TestRuntime : CoreRuntime
+ {
+ public TestRuntime(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
+ : base(umbracoApplication, logger)
+ { }
+
+ protected override void Compose1(ServiceContainer container)
+ {
+ base.Compose1(container);
+
+ container.Register(factory => SettingsForTests.GetDefault());
+ container.Register(factory => new DatabaseContext(
+ factory.GetInstance(),
+ factory.GetInstance()), new PerContainerLifetime());
+ container.RegisterSingleton();
+ }
+
+ protected override IEnumerable GetComponentTypes()
+ {
+ return new[] { typeof(TestComponent) };
+ }
+ }
+
+ public class TestComponent : UmbracoComponentBase
+ {
+ // test
+ public static bool Ctored;
+ public static bool Composed;
+ public static bool Initialized1;
+ public static bool Initialized2;
+ public static bool Terminated;
+
+ public TestComponent()
+ : base()
+ {
+ Ctored = true;
+ }
+
+ public override void Compose(ServiceContainer container)
+ {
+ base.Compose(container);
+ Composed = true;
+ }
+
+ public void Initialize()
+ {
+ Initialized1 = true;
+ }
+
+ public void Initialize(ILogger logger)
+ {
+ Initialized2 = true;
+ }
+
+ public override void Terminate()
+ {
+ base.Terminate();
+ Terminated = true;
+ }
+ }
+
+ // test all event handler
+ // fixme should become test component
+ public class TestApplicationEventHandler : DisposableObject, IApplicationEventHandler
+ {
+ public static void Reset()
+ {
+ Initialized = false;
+ Starting = false;
+ Started = false;
+ HasBeenDisposed = false;
+ }
+
+ public static bool Initialized;
+ public static bool Starting;
+ public static bool Started;
+ public static bool HasBeenDisposed;
+
+ public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+ {
+ Initialized = true;
+ }
+
+ public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+ {
+ Starting = true;
+ }
+
+ public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+ {
+ Started = true;
+ }
+
+ protected override void DisposeResources()
+ {
+ HasBeenDisposed = true;
+ }
+ }
+
+ [Test]
+ public void ComponentLifeCycle()
+ {
+ using (var app = new TestUmbracoApplication())
+ {
+ app.HandleApplicationStart(app, new EventArgs());
+
+ Assert.IsTrue(TestComponent.Ctored);
+ Assert.IsTrue(TestComponent.Composed);
+ Assert.IsTrue(TestComponent.Initialized1);
+ Assert.IsTrue(TestComponent.Initialized2);
+
+ Assert.IsFalse(TestComponent.Terminated);
+
+ app.HandleApplicationEnd();
+ Assert.IsTrue(TestComponent.Terminated);
+ }
+ }
+
+ // note: components are NOT disposed after boot
+ [Test]
+ public void Disposes_App_Startup_Handlers_After_Startup()
+ {
+ using (var app = new TestUmbracoApplication())
+ {
+ app.HandleApplicationStart(app, new EventArgs());
+
+ Assert.IsTrue(TestApplicationEventHandler.HasBeenDisposed);
+ }
+ }
+
+ [Test]
+ public void Handle_IApplicationEventHandler_Objects_Outside_Web_Context()
+ {
+ using (var app = new TestUmbracoApplication())
+ {
+ app.HandleApplicationStart(app, new EventArgs());
+
+ Assert.IsTrue(TestApplicationEventHandler.Initialized);
+ Assert.IsTrue(TestApplicationEventHandler.Starting);
+ Assert.IsTrue(TestApplicationEventHandler.Started);
+ }
+ }
+
+ [Test]
+ public void Raises_Starting_Events()
+ {
+ using (var app = new TestUmbracoApplication())
+ {
+ EventHandler starting = (sender, args) =>
+ {
+ Assert.IsTrue(TestApplicationEventHandler.Initialized);
+ Assert.IsTrue(TestApplicationEventHandler.Starting);
+ Assert.IsFalse(TestApplicationEventHandler.Started);
+ };
+ EventHandler started = (sender, args) =>
+ {
+ Assert.IsTrue(TestApplicationEventHandler.Initialized);
+ Assert.IsTrue(TestApplicationEventHandler.Starting);
+ Assert.IsTrue(TestApplicationEventHandler.Started);
+ };
+
+ app.ApplicationStarting += starting;
+ app.ApplicationStarted += started;
+
+ app.HandleApplicationStart(app, new EventArgs());
+
+ app.ApplicationStarting -= starting;
+ app.ApplicationStarting -= started;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/BootManagers/WebBootManagerTests.cs b/src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs
similarity index 84%
rename from src/Umbraco.Tests/BootManagers/WebBootManagerTests.cs
rename to src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs
index b6c2a6a553..5bae96f92b 100644
--- a/src/Umbraco.Tests/BootManagers/WebBootManagerTests.cs
+++ b/src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs
@@ -5,10 +5,10 @@ using Umbraco.Core.Profiling;
using Umbraco.Web;
using Umbraco.Web.Mvc;
-namespace Umbraco.Tests.BootManagers
+namespace Umbraco.Tests.Runtimes
{
[TestFixture]
- public class WebBootManagerTests
+ public class WebRuntimeTests
{
[Test]
public void WrapViewEngines_HasEngines_WrapsAll()
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.BootManagers
new PluginViewEngine()
};
- WebBootManager.WrapViewEngines(engines);
+ WebRuntime.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(engines[0], Is.InstanceOf());
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.BootManagers
new PluginViewEngine()
};
- WebBootManager.WrapViewEngines(engines);
+ WebRuntime.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf());
@@ -52,7 +52,7 @@ namespace Umbraco.Tests.BootManagers
profiledEngine
};
- WebBootManager.WrapViewEngines(engines);
+ WebRuntime.WrapViewEngines(engines);
Assert.That(engines[0], Is.SameAs(profiledEngine));
}
@@ -61,7 +61,7 @@ namespace Umbraco.Tests.BootManagers
public void WrapViewEngines_CollectionIsNull_DoesNotThrow()
{
IList engines = null;
- Assert.DoesNotThrow(() => WebBootManager.WrapViewEngines(engines));
+ Assert.DoesNotThrow(() => WebRuntime.WrapViewEngines(engines));
Assert.That(engines, Is.Null);
}
diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
index 279f382923..5d6196d0c9 100644
--- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
+++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
@@ -250,8 +250,7 @@ namespace Umbraco.Tests.TestHelpers
{
if (PluginManager.Current == null || PluginManagerResetRequired)
{
- PluginManager.Current = new PluginManager(
- CacheHelper.RuntimeCache, ProfilingLogger, false)
+ PluginManager.Current = new PluginManager(CacheHelper.RuntimeCache, ProfilingLogger, false)
{
AssembliesToScan = new[]
{
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index edefbadf79..3b1b4a25cb 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -227,6 +227,7 @@
+
@@ -270,7 +271,7 @@
-
+
@@ -316,8 +317,8 @@
-
-
+
+
@@ -389,7 +390,7 @@
-
+
@@ -478,7 +479,7 @@
-
+
@@ -496,10 +497,8 @@
-
-
-
-
+
+
@@ -561,7 +560,7 @@
-
+
diff --git a/src/Umbraco.Web/Current.cs b/src/Umbraco.Web/Current.cs
index 063c762ada..0056c4da68 100644
--- a/src/Umbraco.Web/Current.cs
+++ b/src/Umbraco.Web/Current.cs
@@ -8,6 +8,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Macros;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence.Migrations;
+using Umbraco.Core.Plugins;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
@@ -24,9 +25,7 @@ using CoreCurrent = Umbraco.Core.DependencyInjection.Current;
namespace Umbraco.Web
{
- // must remain internal - this class is here to support the transition from singletons
- // and resolvers to injection - by providing a static access to singleton services - it
- // is initialized once with a service container, in WebBootManager.
+ // see notes in Umbraco.Core.DependencyInjection.Current.
public static class Current
{
private static readonly object Locker = new object();
@@ -51,7 +50,7 @@ namespace Umbraco.Web
CoreCurrent.Reset();
}
- public static ServiceContainer Container
+ private static ServiceContainer Container
=> CoreCurrent.Container;
// Facade
@@ -200,6 +199,8 @@ namespace Umbraco.Web
// proxy Core for convenience
+ public static PluginManager PluginManager => CoreCurrent.PluginManager;
+
public static UrlSegmentProviderCollection UrlSegmentProviders => CoreCurrent.UrlSegmentProviders;
public static CacheRefresherCollection CacheRefreshers => CoreCurrent.CacheRefreshers;
@@ -228,6 +229,8 @@ namespace Umbraco.Web
public static IProfiler Profiler => CoreCurrent.Profiler;
+ public static ProfilingLogger ProfilingLogger => CoreCurrent.ProfilingLogger;
+
#endregion
}
}
diff --git a/src/Umbraco.Web/ManifestWatcherComponent.cs b/src/Umbraco.Web/ManifestWatcherComponent.cs
new file mode 100644
index 0000000000..a7e0e5a032
--- /dev/null
+++ b/src/Umbraco.Web/ManifestWatcherComponent.cs
@@ -0,0 +1,44 @@
+using System.IO;
+using Umbraco.Core;
+using Umbraco.Core.Components;
+using Umbraco.Core.IO;
+using Umbraco.Core.Manifest;
+
+namespace Umbraco.Web
+{
+ //[RequireComponent(typeof(object))] // fixme - the one that ensures that runtime.Something is ok
+ public class ManifestWatcherComponent : UmbracoComponentBase, IUmbracoCoreComponent
+ {
+ // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for
+ // package.manifest chances and restarts the application on any change
+ private ManifestWatcher _mw;
+
+ public void Initialize(RuntimeState runtime)
+ {
+ // fixme
+ // if this is a core component it cannot depend on UmbracoCoreComponent - indicates that everything is OK
+ // so what-if UmbracoCoreComponent ... aha ... need another one? UmbracoRuntimeComponent?
+ // or should we have it in IRuntime AND inject IRuntime? IRuntimeInternal vs IRuntime?
+
+ // runtime should be INJECTED! aha!
+ // BUT how can we tell that runtime is "ready enough"? need to depend on some sort of UmbracoRuntimeComponent?
+ // and... will this kind of dependency issue be repro everywhere?!
+ if (runtime.Something < RuntimeSomething.Run || runtime.Debug == false) return;
+
+ //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false)
+ // return;
+
+ var appPlugins = IOHelper.MapPath("~/App_Plugins/");
+ if (Directory.Exists(appPlugins) == false) return;
+
+ _mw = new ManifestWatcher(Current.Logger);
+ _mw.Start(Directory.GetDirectories(appPlugins));
+ }
+
+ public override void Terminate()
+ {
+ _mw?.Dispose();
+ _mw = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Strategies/NotificationsHandler.cs b/src/Umbraco.Web/Strategies/NotificationsHandler.cs
index 8f51104661..a13c9ceda3 100644
--- a/src/Umbraco.Web/Strategies/NotificationsHandler.cs
+++ b/src/Umbraco.Web/Strategies/NotificationsHandler.cs
@@ -74,6 +74,5 @@ namespace Umbraco.Web.Strategies
content, ActionUnPublish.Instance, applicationContext));
}
-
}
}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 6e88a0e6e3..1bab99e1ef 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -210,6 +210,7 @@
+
@@ -1087,7 +1088,7 @@
-
+
diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs
index 533ec6c440..dc0dc238eb 100644
--- a/src/Umbraco.Web/UmbracoApplication.cs
+++ b/src/Umbraco.Web/UmbracoApplication.cs
@@ -1,9 +1,4 @@
-using System;
-using System.IO;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.IO;
-using Umbraco.Core.Manifest;
+using Umbraco.Core;
namespace Umbraco.Web
{
@@ -12,33 +7,9 @@ namespace Umbraco.Web
///
public class UmbracoApplication : UmbracoApplicationBase
{
- // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for
- // package.manifest chances and restarts the application on any change
- private ManifestWatcher _mw;
-
- protected override void OnApplicationStarted(object sender, EventArgs e)
+ protected override IRuntime GetRuntime()
{
- base.OnApplicationStarted(sender, e);
-
- if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false)
- return;
-
- var appPlugins = IOHelper.MapPath("~/App_Plugins/");
- if (Directory.Exists(appPlugins) == false) return;
-
- _mw = new ManifestWatcher(Current.Logger);
- _mw.Start(Directory.GetDirectories(appPlugins));
- }
-
- protected override void OnApplicationEnd(object sender, EventArgs e)
- {
- base.OnApplicationEnd(sender, e);
- _mw?.Dispose();
- }
-
- protected override IBootManager GetBootManager()
- {
- return new WebBootManager(this);
+ return new WebRuntime(this);
}
}
}
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebRuntime.cs
similarity index 77%
rename from src/Umbraco.Web/WebBootManager.cs
rename to src/Umbraco.Web/WebRuntime.cs
index 60f9cf70ef..46b6faff91 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebRuntime.cs
@@ -1,585 +1,562 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Configuration;
-using System.Linq;
-using System.Web;
-using System.Web.Configuration;
-using System.Web.Http;
-using System.Web.Http.Dispatcher;
-using System.Web.Mvc;
-using System.Web.Routing;
-using ClientDependency.Core.Config;
-using Examine;
-using LightInject;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Dictionary;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Macros;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.PropertyEditors.ValueConverters;
-using Umbraco.Core.Sync;
-using Umbraco.Web.Dictionary;
-using Umbraco.Web.Install;
-using Umbraco.Web.Media;
-using Umbraco.Web.Media.ThumbnailProviders;
-using Umbraco.Web.Mvc;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.Routing;
-using Umbraco.Web.Security;
-using Umbraco.Web.UI.JavaScript;
-using Umbraco.Web.WebApi;
-using Umbraco.Core.Events;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Services;
-using Umbraco.Web.Services;
-using Umbraco.Web.Editors;
-using Umbraco.Core.DependencyInjection;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.UnitOfWork;
-using Umbraco.Core.Services.Changes;
-using Umbraco.Web.Cache;
-using Umbraco.Web.DependencyInjection;
-using Umbraco.Web.HealthCheck;
-using Umbraco.Web.HealthCheck.Checks.DataIntegrity;
-using Umbraco.Web._Legacy.Actions;
-using UmbracoExamine;
-using Action = System.Action;
-using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
-using ProfilingViewEngine = Umbraco.Core.Profiling.ProfilingViewEngine;
-using TypeHelper = Umbraco.Core.Plugins.TypeHelper;
-
-
-namespace Umbraco.Web
-{
- ///
- /// A bootstrapper for the Umbraco application which initializes all objects including the Web portion of the application
- ///
- public class WebBootManager : CoreBootManager
- {
- //TODO: Fix this - we need to manually perform re-indexing on startup when necessary Examine lib no longer does this
- //NOTE: see the Initialize method for what this is used for
- //private static readonly List IndexesToRebuild = new List();
-
- public WebBootManager(UmbracoApplicationBase umbracoApplication)
- : base(umbracoApplication)
- { }
-
- ///
- /// Constructor for unit tests, ensures some resolvers are not initialized
- ///
- ///
- ///
- ///
- internal WebBootManager(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger, bool isForTesting)
- : base(umbracoApplication, logger)
- { }
-
- ///
- /// Initialize objects before anything during the boot cycle happens
- ///
- ///
- public override IBootManager Initialize()
- {
- //TODO: Fix this - we need to manually perform re-indexing on startup when necessary Examine lib no longer does this
- ////This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976
- // // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
- // // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
- // // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
- // // boot process has completed. It's a hack but it works.
- //ExamineManager.Instance.BuildingEmptyIndexOnStartup += OnInstanceOnBuildingEmptyIndexOnStartup;
-
- base.Initialize();
-
- //setup mvc and webapi services
- SetupMvcAndWebApi();
-
- // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK]
- ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency";
- ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings;
-
- var section = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
- if (section != null)
- {
- //set the max url length for CDF to be the smallest of the max query length, max request length
- ClientDependency.Core.CompositeFiles.CompositeDependencyHandler.MaxHandlerUrlLength = Math.Min(section.MaxQueryStringLength, section.MaxRequestLength);
- }
-
- //Register a custom renderer - used to process property editor dependencies
- var renderer = new DependencyPathRenderer();
- renderer.Initialize("Umbraco.DependencyPathRenderer", new NameValueCollection
- {
- { "compositeFileHandlerPath", ClientDependencySettings.Instance.CompositeFileHandlerPath }
- });
- ClientDependencySettings.Instance.MvcRendererCollection.Add(renderer);
-
- // Disable the X-AspNetMvc-Version HTTP Header
- MvcHandler.DisableMvcResponseHeader = true;
-
- InstallHelper.DeleteLegacyInstaller();
-
- return this;
- }
-
- ///
- /// Override this method in order to ensure that the UmbracoContext is also created, this can only be
- /// created after resolution is frozen!
- ///
- protected override void FreezeResolution()
- {
- base.FreezeResolution();
-
- //before we do anything, we'll ensure the umbraco context
- //see: http://issues.umbraco.org/issue/U4-1717
- var httpContext = new HttpContextWrapper(UmbracoApplication.Context);
- UmbracoContext.EnsureContext(
- httpContext, ApplicationContext,
- Current.FacadeService,
- new WebSecurity(httpContext, ApplicationContext),
- UmbracoConfig.For.UmbracoSettings(),
- Current.UrlProviders,
- false);
- }
-
- ///
- /// Ensure the current profiler is the web profiler
- ///
- protected override IProfiler CreateProfiler()
- {
- return new WebProfiler();
- }
-
- ///
- /// Ensure that the OnApplicationStarted methods of the IApplicationEvents are called
- ///
- ///
- ///
- public override IBootManager Complete(Action afterComplete)
- {
- //Wrap viewengines in the profiling engine
- WrapViewEngines(ViewEngines.Engines);
-
- //add global filters
- ConfigureGlobalFilters();
-
- //set routes
- CreateRoutes();
-
- base.Complete(afterComplete);
-
- //rebuild any empty indexes
- //TODO: Do we want to make this optional? otherwise the only way to disable this on startup
- // would be to implement a custom WebBootManager and override this method
- RebuildIndexes(true);
-
- //Now ensure webapi is initialized after everything
- GlobalConfiguration.Configuration.EnsureInitialized();
-
- return this;
- }
-
- internal static void ConfigureGlobalFilters()
- {
- GlobalFilters.Filters.Add(new EnsurePartialViewMacroViewContextFilterAttribute());
- }
-
- internal static void WrapViewEngines(IList viewEngines)
- {
- if (viewEngines == null || viewEngines.Count == 0) return;
-
- var originalEngines = viewEngines.Select(e => e).ToArray();
- viewEngines.Clear();
- foreach (var engine in originalEngines)
- {
- var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine);
- viewEngines.Add(wrappedEngine);
- }
- }
-
- ///
- /// Creates the application cache based on the HttpRuntime cache
- ///
- protected override CacheHelper CreateApplicationCache()
- {
- //create a web-based cache helper
- var cacheHelper = new CacheHelper(
- //we need to have the dep clone runtime cache provider to ensure
- //all entities are cached properly (cloned in and cloned out)
- new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)),
- new StaticCacheProvider(),
- //we need request based cache when running in web-based context
- new HttpRequestCacheProvider(),
- new IsolatedRuntimeCache(type =>
- //we need to have the dep clone runtime cache provider to ensure
- //all entities are cached properly (cloned in and cloned out)
- new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
-
- return cacheHelper;
- }
-
- ///
- /// Creates the routes
- ///
- protected internal void CreateRoutes()
- {
- var umbracoPath = GlobalSettings.UmbracoMvcArea;
-
- //Create the front-end route
- var defaultRoute = RouteTable.Routes.MapRoute(
- "Umbraco_default",
- umbracoPath + "/RenderMvc/{action}/{id}",
- new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional }
- );
- defaultRoute.RouteHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory());
-
- //register install routes
- RouteTable.Routes.RegisterArea();
-
- //register all back office routes
- RouteTable.Routes.RegisterArea();
-
- //plugin controllers must come first because the next route will catch many things
- RoutePluginControllers();
- }
-
- private void RoutePluginControllers()
- {
- var umbracoPath = GlobalSettings.UmbracoMvcArea;
-
- //we need to find the plugin controllers and route them
- var pluginControllers = Current.SurfaceControllerTypes
- .Concat(Current.UmbracoApiControllerTypes)
- .ToArray();
-
- //local controllers do not contain the attribute
- var localControllers = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace());
- foreach (var s in localControllers)
- {
- if (TypeHelper.IsTypeAssignableFrom(s))
- {
- RouteLocalSurfaceController(s, umbracoPath);
- }
- else if (TypeHelper.IsTypeAssignableFrom(s))
- {
- RouteLocalApiController(s, umbracoPath);
- }
- }
-
- //need to get the plugin controllers that are unique to each area (group by)
- var pluginSurfaceControlleres = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace() == false);
- var groupedAreas = pluginSurfaceControlleres.GroupBy(controller => PluginController.GetMetadata(controller).AreaName);
- //loop through each area defined amongst the controllers
- foreach (var g in groupedAreas)
- {
- //create an area for the controllers (this will throw an exception if all controllers are not in the same area)
- var pluginControllerArea = new PluginControllerArea(g.Select(PluginController.GetMetadata));
- //register it
- RouteTable.Routes.RegisterArea(pluginControllerArea);
- }
- }
-
- private void RouteLocalApiController(Type controller, string umbracoPath)
- {
- var meta = PluginController.GetMetadata(controller);
-
- //url to match
- var routePath = meta.IsBackOffice == false
- ? umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}"
- : umbracoPath + "/BackOffice/Api/" + meta.ControllerName + "/{action}/{id}";
-
- var route = RouteTable.Routes.MapHttpRoute(
- string.Format("umbraco-{0}-{1}", "api", meta.ControllerName),
- routePath,
- new { controller = meta.ControllerName, id = UrlParameter.Optional },
- new[] { meta.ControllerNamespace });
- //web api routes don't set the data tokens object
- if (route.DataTokens == null)
- {
- route.DataTokens = new RouteValueDictionary();
- }
- route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "api"); //ensure the umbraco token is set
- }
-
- private void RouteLocalSurfaceController(Type controller, string umbracoPath)
- {
- var meta = PluginController.GetMetadata(controller);
- var route = RouteTable.Routes.MapRoute(
- string.Format("umbraco-{0}-{1}", "surface", meta.ControllerName),
- umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}",//url to match
- new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
- new[] { meta.ControllerNamespace }); //look in this namespace to create the controller
- route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "surface"); //ensure the umbraco token is set
- route.DataTokens.Add("UseNamespaceFallback", false); //Don't look anywhere else except this namespace!
- //make it use our custom/special SurfaceMvcHandler
- route.RouteHandler = new SurfaceRouteHandler();
- }
-
- ///
- /// Build the core container which contains all core things requird to build an app context
- ///
- internal override void ConfigureCoreServices(ServiceContainer container)
- {
- base.ConfigureCoreServices(container);
-
- // register model mappers
- container.RegisterFrom();
-
- // support web request scope
- // note: everything that is PerRequestLifeTime will be disposed by LightInject at the end of the request
- container.EnablePerWebRequestScope();
-
- // register the http context and umbraco context accessors
- // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when
- // we have no http context, eg when booting Umbraco or in background threads, so instead
- // let's use an hybrid accessor that can fall back to a ThreadStatic context.
- container.RegisterSingleton(); // replaces HttpContext.Current
- container.RegisterSingleton();
-
- // register a per-request HttpContextBase object
- // is per-request so only one wrapper is created per request
- container.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), new PerRequestLifeTime());
-
- // register the facade accessor - the "current" facade is in the umbraco context
- container.RegisterSingleton();
-
- // register the umbraco database accessor
- // have to use the hybrid thing...
- container.RegisterSingleton();
-
- // register the XML facade service
- //container.RegisterSingleton(factory => new PublishedCache.XmlPublishedCache.FacadeService(
- // factory.GetInstance(),
- // factory.GetInstance(),
- // factory.GetInstance().RequestCache,
- // factory.GetAllInstances(),
- // factory.GetInstance()));
-
- // register the NuCache facade service
- container.RegisterSingleton(factory => new PublishedCache.NuCache.FacadeService(
- new PublishedCache.NuCache.FacadeService.Options { FacadeCacheIsApplicationRequestCache = true },
- factory.GetInstance().MainDom,
- factory.GetInstance(),
- factory.GetInstance(),
- factory.GetInstance(),
- factory.GetInstance()));
-
- // register a per-request UmbracoContext object
- // no real need to be per request but assuming it is faster
- container.Register(factory => factory.GetInstance().UmbracoContext, new PerRequestLifeTime());
-
- // register the umbraco helper
- container.RegisterSingleton();
-
- // replace some services
- container.RegisterSingleton();
- container.RegisterSingleton();
- container.RegisterSingleton();
- container.RegisterSingleton();
-
- container.RegisterSingleton();
- }
-
- ///
- /// Called to customize the IoC container
- ///
- ///
- internal override void ConfigureApplicationServices(ServiceContainer container)
- {
- base.ConfigureApplicationServices(container);
-
- // IoC setup for LightInject for MVC/WebApi
- Container.EnableMvc();
- Container.RegisterMvcControllers(PluginManager, GetType().Assembly);
- container.EnableWebApi(GlobalConfiguration.Configuration);
- container.RegisterApiControllers(PluginManager, GetType().Assembly);
- }
-
- ///
- /// Initializes all web based and core resolves
- ///
- protected override void InitializeResolvers()
- {
- base.InitializeResolvers();
-
- XsltExtensionCollectionBuilder.Register(Container)
- .AddExtensionObjectProducer(() => PluginManager.ResolveXsltExtensions());
-
- EditorValidatorCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolveTypes());
-
- // set the default RenderMvcController
- Current.DefaultRenderMvcControllerType = typeof (RenderMvcController); // fixme WRONG!
-
- //Override the default server messenger, we need to check if the legacy dist calls is enabled, if that is the
- // case, then we'll set the default messenger to be the old one, otherwise we'll set it to the db messenger
- // which will always be on.
- if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled)
- {
- //set the legacy one by default - this maintains backwards compat
- Container.Register(_ => new BatchedWebServiceServerMessenger(() =>
- {
- //we should not proceed to change this if the app/database is not configured since there will
- // be no user, plus we don't need to have server messages sent if this is the case.
- if (ApplicationContext.IsConfigured && ApplicationContext.DatabaseContext.IsDatabaseConfigured)
- {
- //disable if they are not enabled
- if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled == false)
- {
- return null;
- }
-
- try
- {
- var user = ApplicationContext.Services.UserService.GetUserById(UmbracoConfig.For.UmbracoSettings().DistributedCall.UserId);
- return new Tuple(user.Username, user.RawPasswordValue);
- }
- catch (Exception e)
- {
- ProfilingLogger.Logger.Error("An error occurred trying to set the IServerMessenger during application startup", e);
- return null;
- }
- }
- ProfilingLogger.Logger.Warn("Could not initialize the DefaultServerMessenger, the application is not configured or the database is not configured");
- return null;
- }), new PerContainerLifetime());
- }
- else
- {
-
- Container.Register(_ => new BatchedDatabaseServerMessenger(
- ApplicationContext,
- true,
- //Default options for web including the required callbacks to build caches
- new DatabaseServerMessengerOptions
- {
- //These callbacks will be executed if the server has not been synced
- // (i.e. it is a new server or the lastsynced.txt file has been removed)
- InitializingCallbacks = new Action[]
- {
- //rebuild the xml cache file if the server is not synced
- () =>
- {
- // rebuild the facade caches entirely, if the server is not synced
- // this is equivalent to DistributedCache RefreshAllFacade but local only
- // (we really should have a way to reuse RefreshAllFacade... locally)
- // note: refresh all content & media caches does refresh content types too
- IFacadeService svc = Current.FacadeService;
- bool ignored1, ignored2;
- svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
- svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out ignored1, out ignored2);
- svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out ignored1);
- },
- //rebuild indexes if the server is not synced
- // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
- // indexes then they can adjust this logic themselves.
- () => RebuildIndexes(false)
- }
- }), new PerContainerLifetime());
- }
-
- ActionCollectionBuilder.Register(Container)
- .SetProducer(() => PluginManager.ResolveActions());
-
- var surfaceControllerTypes = new SurfaceControllerTypeCollection(PluginManager.ResolveSurfaceControllers());
- Container.RegisterInstance(surfaceControllerTypes);
-
- var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(PluginManager.ResolveUmbracoApiControllers());
- Container.RegisterInstance(umbracoApiControllerTypes);
-
- // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be
- // discovered when CoreBootManager configures the converters. We HAVE to remove one of them
- // here because there cannot be two converters for one property editor - and we want the full
- // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter.
- // (the limited one, defined in Core, is there for tests) - same for others
- Container.GetInstance()
- .Remove()
- .Remove()
- .Remove()
- .Remove();
-
- // add all known factories, devs can then modify this list on application
- // startup either by binding to events or in their own global.asax
- FilteredControllerFactoryCollectionBuilder.Register(Container)
- .Append();
-
- UrlProviderCollectionBuilder.Register(Container)
- //.Append() // not enabled by default
- .Append()
- .Append();
-
- Container.Register();
-
- ContentFinderCollectionBuilder.Register(Container)
- // all built-in finders in the correct order,
- // devs can then modify this list on application startup
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append();
-
- Container.Register();
-
- ThumbnailProviderCollectionBuilder.Register(Container)
- .Add(PluginManager.ResolveThumbnailProviders());
-
- ImageUrlProviderCollectionBuilder.Register(Container)
- .Append(PluginManager.ResolveImageUrlProviders());
-
- Container.RegisterSingleton();
-
- HealthCheckCollectionBuilder.Register(Container)
- .AddProducer(() => PluginManager.ResolveTypes())
- .Exclude(); // fixme must remove else NuCache dies!
- // but we should also have one for NuCache AND NuCache should be a component that does all this
- }
-
- ///
- /// Sets up MVC/WebApi services
- ///
- private void SetupMvcAndWebApi()
- {
- //don't output the MVC version header (security)
- MvcHandler.DisableMvcResponseHeader = true;
-
- // set master controller factory
- var controllerFactory = new MasterControllerFactory(() => Current.FilteredControllerFactories);
- ControllerBuilder.Current.SetControllerFactory(controllerFactory);
-
- // set the render & plugin view engines
- ViewEngines.Engines.Add(new RenderViewEngine());
- ViewEngines.Engines.Add(new PluginViewEngine());
-
- //set model binder
- ModelBinderProviders.BinderProviders.Add(new ContentModelBinder()); // is a provider
-
- ////add the profiling action filter
- //GlobalFilters.Filters.Add(new ProfilingActionFilter());
-
- GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
- new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));
- }
-
- protected virtual void RebuildIndexes(bool onlyEmptyIndexes)
- {
- if (ApplicationContext.IsConfigured == false || ApplicationContext.DatabaseContext.IsDatabaseConfigured == false)
- {
- return;
- }
-
- foreach (var indexer in ExamineManager.Instance.IndexProviders)
- {
- if (onlyEmptyIndexes == false || indexer.Value.IsIndexNew())
- {
- indexer.Value.RebuildIndex();
- }
- }
-
- }
-
- }
-}
-
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.Linq;
+using System.Web;
+using System.Web.Configuration;
+using System.Web.Http;
+using System.Web.Http.Dispatcher;
+using System.Web.Mvc;
+using System.Web.Routing;
+using ClientDependency.Core.Config;
+using Examine;
+using LightInject;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Dictionary;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Macros;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.PropertyEditors.ValueConverters;
+using Umbraco.Core.Sync;
+using Umbraco.Web.Dictionary;
+using Umbraco.Web.Install;
+using Umbraco.Web.Media;
+using Umbraco.Web.Media.ThumbnailProviders;
+using Umbraco.Web.Mvc;
+using Umbraco.Web.PublishedCache;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Security;
+using Umbraco.Web.UI.JavaScript;
+using Umbraco.Web.WebApi;
+using Umbraco.Core.Events;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Services;
+using Umbraco.Web.Services;
+using Umbraco.Web.Editors;
+using Umbraco.Core.DependencyInjection;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Services.Changes;
+using Umbraco.Web.Cache;
+using Umbraco.Web.DependencyInjection;
+using Umbraco.Web.HealthCheck;
+using Umbraco.Web.HealthCheck.Checks.DataIntegrity;
+using Umbraco.Web._Legacy.Actions;
+using UmbracoExamine;
+using Action = System.Action;
+using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
+using ProfilingViewEngine = Umbraco.Core.Profiling.ProfilingViewEngine;
+using TypeHelper = Umbraco.Core.Plugins.TypeHelper;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Represents the Web Umbraco runtime.
+ ///
+ /// On top of CoreRuntime, handles all of the web-related aspects of Umbraco (startup, etc).
+ public class WebRuntime : CoreRuntime
+ {
+ public override void Boot(ServiceContainer container)
+ {
+ base.Boot(container);
+ }
+
+ public override void Terminate()
+ {
+ base.Terminate();
+ }
+
+ protected override void Compose1(ServiceContainer container)
+ {
+ base.Compose1(container);
+
+ // register model mappers
+ container.RegisterFrom();
+
+ // support web request scope
+ // note: everything that is PerRequestLifeTime will be disposed by LightInject at the end of the request
+ // fixme - temp - moved to... find it
+ //container.EnablePerWebRequestScope();
+
+ // register the http context and umbraco context accessors
+ // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when
+ // we have no http context, eg when booting Umbraco or in background threads, so instead
+ // let's use an hybrid accessor that can fall back to a ThreadStatic context.
+ container.RegisterSingleton(); // replaces HttpContext.Current
+ container.RegisterSingleton();
+
+ // register a per-request HttpContextBase object
+ // is per-request so only one wrapper is created per request
+ container.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), new PerRequestLifeTime());
+
+ // register the facade accessor - the "current" facade is in the umbraco context
+ container.RegisterSingleton();
+
+ // register the umbraco database accessor
+ // have to use the hybrid thing...
+ container.RegisterSingleton();
+
+ // register the XML facade service
+ //container.RegisterSingleton(factory => new PublishedCache.XmlPublishedCache.FacadeService(
+ // factory.GetInstance(),
+ // factory.GetInstance(),
+ // factory.GetInstance().RequestCache,
+ // factory.GetAllInstances(),
+ // factory.GetInstance()));
+
+ // register the NuCache facade service
+ container.RegisterSingleton(factory => new PublishedCache.NuCache.FacadeService(
+ new PublishedCache.NuCache.FacadeService.Options { FacadeCacheIsApplicationRequestCache = true },
+ factory.GetInstance().MainDom,
+ factory.GetInstance(),
+ factory.GetInstance(),
+ factory.GetInstance(),
+ factory.GetInstance()));
+
+ // register a per-request UmbracoContext object
+ // no real need to be per request but assuming it is faster
+ container.Register(factory => factory.GetInstance().UmbracoContext, new PerRequestLifeTime());
+
+ // register the umbraco helper
+ container.RegisterSingleton();
+
+ // replace some services
+ container.RegisterSingleton();
+ container.RegisterSingleton();
+ container.RegisterSingleton();
+ container.RegisterSingleton();
+
+ container.RegisterSingleton();
+ }
+
+ protected override void Compose2(ServiceContainer container)
+ {
+ base.Compose2(container);
+
+ // fixme moved too wtf
+ //// IoC setup for LightInject for MVC/WebApi
+ //container.EnableMvc();
+ //container.RegisterMvcControllers(PluginManager, GetType().Assembly);
+ //container.EnableWebApi(GlobalConfiguration.Configuration);
+ //container.RegisterApiControllers(PluginManager, GetType().Assembly);
+
+ XsltExtensionCollectionBuilder.Register(container)
+ .AddExtensionObjectProducer(() => PluginManager.ResolveXsltExtensions());
+
+ EditorValidatorCollectionBuilder.Register(container)
+ .AddProducer(() => PluginManager.ResolveTypes());
+
+ // set the default RenderMvcController
+ Current.DefaultRenderMvcControllerType = typeof(RenderMvcController); // fixme WRONG!
+
+ //Override the default server messenger, we need to check if the legacy dist calls is enabled, if that is the
+ // case, then we'll set the default messenger to be the old one, otherwise we'll set it to the db messenger
+ // which will always be on.
+ if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled)
+ {
+ //set the legacy one by default - this maintains backwards compat
+ container.Register(_ => new BatchedWebServiceServerMessenger(() =>
+ {
+ //we should not proceed to change this if the app/database is not configured since there will
+ // be no user, plus we don't need to have server messages sent if this is the case.
+ if (ApplicationContext.IsConfigured && ApplicationContext.DatabaseContext.IsDatabaseConfigured)
+ {
+ //disable if they are not enabled
+ if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled == false)
+ {
+ return null;
+ }
+
+ try
+ {
+ var user = ApplicationContext.Services.UserService.GetUserById(UmbracoConfig.For.UmbracoSettings().DistributedCall.UserId);
+ return new Tuple(user.Username, user.RawPasswordValue);
+ }
+ catch (Exception e)
+ {
+ ProfilingLogger.Logger.Error("An error occurred trying to set the IServerMessenger during application startup", e);
+ return null;
+ }
+ }
+ ProfilingLogger.Logger.Warn("Could not initialize the DefaultServerMessenger, the application is not configured or the database is not configured");
+ return null;
+ }), new PerContainerLifetime());
+ }
+ else
+ {
+ container.Register(_ => new BatchedDatabaseServerMessenger(
+ ApplicationContext,
+ true,
+ //Default options for web including the required callbacks to build caches
+ new DatabaseServerMessengerOptions
+ {
+ //These callbacks will be executed if the server has not been synced
+ // (i.e. it is a new server or the lastsynced.txt file has been removed)
+ InitializingCallbacks = new Action[]
+ {
+ //rebuild the xml cache file if the server is not synced
+ () =>
+ {
+ // rebuild the facade caches entirely, if the server is not synced
+ // this is equivalent to DistributedCache RefreshAllFacade but local only
+ // (we really should have a way to reuse RefreshAllFacade... locally)
+ // note: refresh all content & media caches does refresh content types too
+ var svc = Current.FacadeService;
+ bool ignored1, ignored2;
+ svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
+ svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out ignored1, out ignored2);
+ svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out ignored1);
+ },
+ //rebuild indexes if the server is not synced
+ // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
+ // indexes then they can adjust this logic themselves.
+ () => RebuildIndexes(false)
+ }
+ }), new PerContainerLifetime());
+ }
+
+ ActionCollectionBuilder.Register(container)
+ .SetProducer(() => PluginManager.ResolveActions());
+
+ var surfaceControllerTypes = new SurfaceControllerTypeCollection(PluginManager.ResolveSurfaceControllers());
+ container.RegisterInstance(surfaceControllerTypes);
+
+ var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(PluginManager.ResolveUmbracoApiControllers());
+ container.RegisterInstance(umbracoApiControllerTypes);
+
+ // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be
+ // discovered when CoreBootManager configures the converters. We HAVE to remove one of them
+ // here because there cannot be two converters for one property editor - and we want the full
+ // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter.
+ // (the limited one, defined in Core, is there for tests) - same for others
+ container.GetInstance()
+ .Remove()
+ .Remove()
+ .Remove()
+ .Remove();
+
+ // add all known factories, devs can then modify this list on application
+ // startup either by binding to events or in their own global.asax
+ FilteredControllerFactoryCollectionBuilder.Register(container)
+ .Append();
+
+ UrlProviderCollectionBuilder.Register(container)
+ //.Append() // not enabled by default
+ .Append()
+ .Append();
+
+ Container.Register();
+
+ ContentFinderCollectionBuilder.Register(container)
+ // all built-in finders in the correct order,
+ // devs can then modify this list on application startup
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append();
+
+ container.Register();
+
+ ThumbnailProviderCollectionBuilder.Register(container)
+ .Add(PluginManager.ResolveThumbnailProviders());
+
+ ImageUrlProviderCollectionBuilder.Register(container)
+ .Append(PluginManager.ResolveImageUrlProviders());
+
+ container.RegisterSingleton();
+
+ HealthCheckCollectionBuilder.Register(container)
+ .AddProducer(() => PluginManager.ResolveTypes())
+ .Exclude(); // fixme must remove else NuCache dies!
+ // but we should also have one for NuCache AND NuCache should be a component that does all this
+ }
+
+ #region Getters
+
+ protected override IProfiler GetProfiler() => new WebProfiler();
+
+ protected override CacheHelper GetApplicationCache() => new CacheHelper(
+ // we need to have the dep clone runtime cache provider to ensure
+ // all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)),
+ new StaticCacheProvider(),
+ // we need request based cache when running in web-based context
+ new HttpRequestCacheProvider(),
+ new IsolatedRuntimeCache(type =>
+ // we need to have the dep clone runtime cache provider to ensure
+ // all entities are cached properly (cloned in and cloned out)
+ new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
+
+ #endregion
+
+ #region Web
+
+ internal static void ConfigureGlobalFilters()
+ {
+ GlobalFilters.Filters.Add(new EnsurePartialViewMacroViewContextFilterAttribute());
+ }
+
+ internal static void WrapViewEngines(IList viewEngines)
+ {
+ if (viewEngines == null || viewEngines.Count == 0) return;
+
+ var originalEngines = viewEngines.Select(e => e).ToArray();
+ viewEngines.Clear();
+ foreach (var engine in originalEngines)
+ {
+ var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine);
+ viewEngines.Add(wrappedEngine);
+ }
+ }
+
+ protected internal void CreateRoutes()
+ {
+ var umbracoPath = GlobalSettings.UmbracoMvcArea;
+
+ //Create the front-end route
+ var defaultRoute = RouteTable.Routes.MapRoute(
+ "Umbraco_default",
+ umbracoPath + "/RenderMvc/{action}/{id}",
+ new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional }
+ );
+ defaultRoute.RouteHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory());
+
+ //register install routes
+ RouteTable.Routes.RegisterArea();
+
+ //register all back office routes
+ RouteTable.Routes.RegisterArea();
+
+ //plugin controllers must come first because the next route will catch many things
+ RoutePluginControllers();
+ }
+
+ private static void RoutePluginControllers()
+ {
+ var umbracoPath = GlobalSettings.UmbracoMvcArea;
+
+ // need to find the plugin controllers and route them
+ var pluginControllers = Current.SurfaceControllerTypes
+ .Concat(Current.UmbracoApiControllerTypes)
+ .ToArray();
+
+ // local controllers do not contain the attribute
+ var localControllers = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace());
+ foreach (var s in localControllers)
+ {
+ if (TypeHelper.IsTypeAssignableFrom(s))
+ RouteLocalSurfaceController(s, umbracoPath);
+ else if (TypeHelper.IsTypeAssignableFrom(s))
+ RouteLocalApiController(s, umbracoPath);
+ }
+
+ // need to get the plugin controllers that are unique to each area (group by)
+ var pluginSurfaceControlleres = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace() == false);
+ var groupedAreas = pluginSurfaceControlleres.GroupBy(controller => PluginController.GetMetadata(controller).AreaName);
+ // loop through each area defined amongst the controllers
+ foreach (var g in groupedAreas)
+ {
+ // create & register an area for the controllers (this will throw an exception if all controllers are not in the same area)
+ var pluginControllerArea = new PluginControllerArea(g.Select(PluginController.GetMetadata));
+ RouteTable.Routes.RegisterArea(pluginControllerArea);
+ }
+ }
+
+ private static void RouteLocalApiController(Type controller, string umbracoPath)
+ {
+ var meta = PluginController.GetMetadata(controller);
+ var url = umbracoPath + (meta.IsBackOffice ? "/BackOffice" : "") + "/Api/" + meta.ControllerName + "/{action}/{id}";
+ var route = RouteTable.Routes.MapHttpRoute(
+ $"umbraco-api-{meta.ControllerName}",
+ url, // url to match
+ new { controller = meta.ControllerName, id = UrlParameter.Optional },
+ new[] { meta.ControllerNamespace });
+ if (route.DataTokens == null) // web api routes don't set the data tokens object
+ route.DataTokens = new RouteValueDictionary();
+ route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "api"); //ensure the umbraco token is set
+ }
+
+ private static void RouteLocalSurfaceController(Type controller, string umbracoPath)
+ {
+ var meta = PluginController.GetMetadata(controller);
+ var url = umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}";
+ var route = RouteTable.Routes.MapRoute(
+ $"umbraco-surface-{meta.ControllerName}",
+ url, // url to match
+ new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
+ new[] { meta.ControllerNamespace }); // look in this namespace to create the controller
+ route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "surface"); // ensure the umbraco token is set
+ route.DataTokens.Add("UseNamespaceFallback", false); // don't look anywhere else except this namespace!
+ // make it use our custom/special SurfaceMvcHandler
+ route.RouteHandler = new SurfaceRouteHandler();
+ }
+
+ #endregion
+
+ public WebRuntime(UmbracoApplicationBase umbracoApplication)
+ : base(umbracoApplication)
+ { }
+
+ ///
+ /// Constructor for unit tests, ensures some resolvers are not initialized
+ ///
+ ///
+ ///
+ ///
+ internal WebRuntime(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger, bool isForTesting)
+ : base(umbracoApplication, logger)
+ { }
+
+ ///
+ /// Initialize objects before anything during the boot cycle happens
+ ///
+ ///
+ public override IRuntime Initialize()
+ {
+ //TODO: Fix this - we need to manually perform re-indexing on startup when necessary Examine lib no longer does this
+ ////This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976
+ // // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
+ // // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
+ // // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
+ // // boot process has completed. It's a hack but it works.
+ //ExamineManager.Instance.BuildingEmptyIndexOnStartup += OnInstanceOnBuildingEmptyIndexOnStartup;
+
+ base.Initialize();
+
+ // fixme - only here so that everything above can have its OWN scope
+ // this is all completely fucked
+ Container.EnablePerWebRequestScope();
+ Container.EnableMvc();
+ Container.RegisterMvcControllers(PluginManager, GetType().Assembly);
+ Container.EnableWebApi(GlobalConfiguration.Configuration);
+ Container.RegisterApiControllers(PluginManager, GetType().Assembly);
+
+ //setup mvc and webapi services
+ SetupMvcAndWebApi();
+
+ // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK]
+ ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency";
+ ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings;
+
+ var section = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
+ if (section != null)
+ {
+ //set the max url length for CDF to be the smallest of the max query length, max request length
+ ClientDependency.Core.CompositeFiles.CompositeDependencyHandler.MaxHandlerUrlLength = Math.Min(section.MaxQueryStringLength, section.MaxRequestLength);
+ }
+
+ //Register a custom renderer - used to process property editor dependencies
+ var renderer = new DependencyPathRenderer();
+ renderer.Initialize("Umbraco.DependencyPathRenderer", new NameValueCollection
+ {
+ { "compositeFileHandlerPath", ClientDependencySettings.Instance.CompositeFileHandlerPath }
+ });
+ ClientDependencySettings.Instance.MvcRendererCollection.Add(renderer);
+
+ // Disable the X-AspNetMvc-Version HTTP Header
+ MvcHandler.DisableMvcResponseHeader = true;
+
+ InstallHelper.DeleteLegacyInstaller();
+
+ return this;
+ }
+
+ ///
+ /// Override this method in order to ensure that the UmbracoContext is also created, this can only be
+ /// created after resolution is frozen!
+ ///
+ protected override void FreezeResolution()
+ {
+ base.FreezeResolution();
+
+ //before we do anything, we'll ensure the umbraco context
+ //see: http://issues.umbraco.org/issue/U4-1717
+ var httpContext = new HttpContextWrapper(UmbracoApplication.Context);
+ UmbracoContext.EnsureContext(
+ httpContext, ApplicationContext,
+ Current.FacadeService,
+ new WebSecurity(httpContext, ApplicationContext),
+ UmbracoConfig.For.UmbracoSettings(),
+ Current.UrlProviders,
+ false);
+ }
+
+ ///
+ /// Ensure that the OnApplicationStarted methods of the IApplicationEvents are called
+ ///
+ ///
+ ///
+ public override IRuntime Complete(Action afterComplete)
+ {
+ //Wrap viewengines in the profiling engine
+ WrapViewEngines(ViewEngines.Engines);
+
+ //add global filters
+ ConfigureGlobalFilters();
+
+ //set routes
+ CreateRoutes();
+
+ base.Complete(afterComplete);
+
+ //rebuild any empty indexes
+ //TODO: Do we want to make this optional? otherwise the only way to disable this on startup
+ // would be to implement a custom WebBootManager and override this method
+ RebuildIndexes(true);
+
+ //Now ensure webapi is initialized after everything
+ GlobalConfiguration.Configuration.EnsureInitialized();
+
+ return this;
+ }
+
+
+ ///
+ /// Sets up MVC/WebApi services
+ ///
+ private void SetupMvcAndWebApi()
+ {
+ //don't output the MVC version header (security)
+ MvcHandler.DisableMvcResponseHeader = true;
+
+ // set master controller factory
+ var controllerFactory = new MasterControllerFactory(() => Current.FilteredControllerFactories);
+ ControllerBuilder.Current.SetControllerFactory(controllerFactory);
+
+ // set the render & plugin view engines
+ ViewEngines.Engines.Add(new RenderViewEngine());
+ ViewEngines.Engines.Add(new PluginViewEngine());
+
+ //set model binder
+ ModelBinderProviders.BinderProviders.Add(new ContentModelBinder()); // is a provider
+
+ ////add the profiling action filter
+ //GlobalFilters.Filters.Add(new ProfilingActionFilter());
+
+ GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
+ new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));
+ }
+
+ protected virtual void RebuildIndexes(bool onlyEmptyIndexes)
+ {
+ if (ApplicationContext.IsConfigured == false || ApplicationContext.DatabaseContext.IsDatabaseConfigured == false)
+ {
+ return;
+ }
+
+ foreach (var indexer in ExamineManager.Instance.IndexProviders)
+ {
+ if (onlyEmptyIndexes == false || indexer.Value.IsIndexNew())
+ {
+ indexer.Value.RebuildIndex();
+ }
+ }
+ }
+ }
+}
+