Resvolution - Booting
This commit is contained in:
@@ -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
|
||||
|
||||
229
src/Umbraco.Core/Components/BootLoader.cs
Normal file
229
src/Umbraco.Core/Components/BootLoader.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BootLoader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="container">The application container.</param>
|
||||
public BootLoader(ServiceContainer container)
|
||||
{
|
||||
if (container == null) throw new ArgumentNullException(nameof(container));
|
||||
_container = container;
|
||||
_proflog = container.GetInstance<ProfilingLogger>();
|
||||
}
|
||||
|
||||
// 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<Type> componentTypes)
|
||||
{
|
||||
if (_booted) throw new InvalidOperationException("Can not boot, has already booted.");
|
||||
|
||||
using (_proflog.TraceDuration<BootLoader>($"Booting Umbraco {UmbracoVersion.GetSemanticVersion().ToSemanticString()} on {NetworkHelper.MachineName}.", "Booted."))
|
||||
{
|
||||
var orderedComponentTypes = PrepareComponentTypes(componentTypes);
|
||||
InstanciateComponents(orderedComponentTypes);
|
||||
ComposeComponents();
|
||||
InitializeComponents();
|
||||
}
|
||||
|
||||
// rejoice!
|
||||
_booted = true;
|
||||
}
|
||||
|
||||
private IEnumerable<Type> PrepareComponentTypes(IEnumerable<Type> componentTypes)
|
||||
{
|
||||
using (_proflog.DebugDuration<BootLoader>("Preparing component types.", "Prepared component types."))
|
||||
{
|
||||
return PrepareComponentTypes2(componentTypes);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> PrepareComponentTypes2(IEnumerable<Type> componentTypes)
|
||||
{
|
||||
var componentTypeList = componentTypes.ToList();
|
||||
|
||||
if (componentTypeList.Contains(typeof(UmbracoCoreComponent)) == false)
|
||||
componentTypeList.Add(typeof(UmbracoCoreComponent));
|
||||
|
||||
var enabled = new Dictionary<Type, EnableInfo>();
|
||||
|
||||
// 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<EnableComponentAttribute>())
|
||||
{
|
||||
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<DisableComponentAttribute>())
|
||||
{
|
||||
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<IUmbracoCoreComponent>()).ToArray();
|
||||
var items = new List<TopologicalSorter.DependencyField<Type>>();
|
||||
var temp = new List<Type>(); // reduce allocs
|
||||
foreach (var type in componentTypeList)
|
||||
{
|
||||
temp.Clear();
|
||||
if (type == typeof(UmbracoCoreComponent)) temp.AddRange(coreComponentTypes);
|
||||
if (type.Implements<IUmbracoUserComponent>()) temp.Add(typeof(UmbracoCoreComponent));
|
||||
temp.AddRange(type.GetCustomAttributes<RequireComponentAttribute>().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>(type.FullName, dependsOn.Select(x => x.FullName).ToArray(), new Lazy<Type>(() => type)));
|
||||
}
|
||||
return TopologicalSorter.GetSortedItems(items);
|
||||
}
|
||||
|
||||
private void InstanciateComponents(IEnumerable<Type> types)
|
||||
{
|
||||
using (_proflog.DebugDuration<BootLoader>("Instanciating components.", "Instanciated components."))
|
||||
{
|
||||
_components = types.Select(x => (IUmbracoComponent) Activator.CreateInstance(x)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void ComposeComponents()
|
||||
{
|
||||
using (_proflog.DebugDuration<BootLoader>($"Composing components. (log when >{LogThresholdMilliseconds}ms)", "Composed components."))
|
||||
{
|
||||
foreach (var component in _components)
|
||||
{
|
||||
var componentType = component.GetType();
|
||||
using (_proflog.DebugDuration<BootLoader>($"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<BootLoader>($"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<BootLoader>($"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<BootLoader>($"Terminating Umbraco. (log components when >{LogThresholdMilliseconds}ms)", "Terminated Umbraco."))
|
||||
{
|
||||
foreach (var component in _components)
|
||||
{
|
||||
var componentType = component.GetType();
|
||||
using (_proflog.DebugDuration<BootLoader>($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", LogThresholdMilliseconds))
|
||||
{
|
||||
component.Terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Umbraco.Core/Components/DisableComponentAttribute.cs
Normal file
18
src/Umbraco.Core/Components/DisableComponentAttribute.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
18
src/Umbraco.Core/Components/EnableComponentAttribute.cs
Normal file
18
src/Umbraco.Core/Components/EnableComponentAttribute.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Components/IUmbracoComponent.cs
Normal file
11
src/Umbraco.Core/Components/IUmbracoComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using LightInject;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
public interface IUmbracoComponent
|
||||
{
|
||||
void Compose(ServiceContainer container);
|
||||
|
||||
void Terminate();
|
||||
}
|
||||
}
|
||||
5
src/Umbraco.Core/Components/IUmbracoCoreComponent.cs
Normal file
5
src/Umbraco.Core/Components/IUmbracoCoreComponent.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
public interface IUmbracoCoreComponent : IUmbracoComponent
|
||||
{ }
|
||||
}
|
||||
5
src/Umbraco.Core/Components/IUmbracoUserComponent.cs
Normal file
5
src/Umbraco.Core/Components/IUmbracoUserComponent.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
public interface IUmbracoUserComponent : IUmbracoComponent
|
||||
{ }
|
||||
}
|
||||
15
src/Umbraco.Core/Components/RequireComponentAttribute.cs
Normal file
15
src/Umbraco.Core/Components/RequireComponentAttribute.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
13
src/Umbraco.Core/Components/UmbracoComponentBase.cs
Normal file
13
src/Umbraco.Core/Components/UmbracoComponentBase.cs
Normal file
@@ -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()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
5
src/Umbraco.Core/Components/UmbracoCoreComponent.cs
Normal file
5
src/Umbraco.Core/Components/UmbracoCoreComponent.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
public class UmbracoCoreComponent : UmbracoComponentBase
|
||||
{ }
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not provide any startup functionality relating to web objects
|
||||
/// </remarks>
|
||||
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<CoreBootManager>(
|
||||
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<ApplicationContext>();
|
||||
|
||||
//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<PerScopeLifetime>(PluginManager.ResolveApplicationStartupHandlers());
|
||||
|
||||
//build up standard IoC services
|
||||
ConfigureApplicationServices(Container);
|
||||
|
||||
InitializeResolvers();
|
||||
InitializeModelMappers();
|
||||
|
||||
//now we need to call the initialize methods
|
||||
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(
|
||||
$"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<CoreBootManager>("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the core container which contains all core things requird to build an app context
|
||||
/// </summary>
|
||||
internal virtual void ConfigureCoreServices(ServiceContainer container)
|
||||
{
|
||||
//Logging
|
||||
container.RegisterInstance(ProfilingLogger.Profiler);
|
||||
container.RegisterInstance(ProfilingLogger);
|
||||
|
||||
//Config
|
||||
container.RegisterFrom<ConfigurationCompositionRoot>();
|
||||
|
||||
//Cache
|
||||
container.RegisterInstance(ApplicationCache);
|
||||
container.RegisterInstance(ApplicationCache.RuntimeCache);
|
||||
|
||||
//Datalayer/Repositories/SQL/Database/etc...
|
||||
container.RegisterFrom<RepositoryCompositionRoot>();
|
||||
|
||||
//Data Services/ServiceContext/etc...
|
||||
container.RegisterFrom<ServicesCompositionRoot>();
|
||||
|
||||
//ModelMappers
|
||||
container.RegisterFrom<CoreModelMappersCompositionRoot>();
|
||||
|
||||
//TODO: Don't think we'll need this when the resolvers are all container resolvers
|
||||
container.RegisterSingleton<IServiceProvider, ActivatorServiceProvider>();
|
||||
container.RegisterInstance(PluginManager);
|
||||
|
||||
container.RegisterSingleton<ApplicationContext>();
|
||||
container.Register<MediaFileSystem>(factory => FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to customize the IoC container
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
internal virtual void ConfigureApplicationServices(ServiceContainer container)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ApplicationCache based on a new instance of System.Web.Caching.Cache
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method initializes all of the model mappers registered in the container
|
||||
/// </summary>
|
||||
protected void InitializeModelMappers()
|
||||
{
|
||||
Mapper.Initialize(configuration =>
|
||||
{
|
||||
//foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType<IMapperConfiguration>())
|
||||
foreach (var m in Container.GetAllInstances<ModelMapperConfiguration>())
|
||||
{
|
||||
m.ConfigureMappings(configuration, ApplicationContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the application's IProfiler
|
||||
/// </summary>
|
||||
protected virtual IProfiler CreateProfiler()
|
||||
{
|
||||
return new LogProfiler(ProfilingLogger.Logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="rootPath">Absolute</param>
|
||||
protected virtual void InitializeApplicationRootPath(string rootPath)
|
||||
{
|
||||
IO.IOHelper.SetRootDirectory(rootPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur &
|
||||
/// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
|
||||
/// </summary>
|
||||
/// <param name="afterStartup"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IBootManager Startup(Action<ApplicationContext> afterStartup)
|
||||
{
|
||||
if (_isStarted)
|
||||
throw new InvalidOperationException("The boot manager has already been initialized");
|
||||
|
||||
//call OnApplicationStarting of each application events handler
|
||||
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(
|
||||
$"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<CoreBootManager>("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
if (afterStartup != null)
|
||||
{
|
||||
afterStartup(ApplicationContext.Current);
|
||||
}
|
||||
|
||||
_isStarted = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires after startup and calls the callback once customizations are locked
|
||||
/// </summary>
|
||||
/// <param name="afterComplete"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IBootManager Complete(Action<ApplicationContext> 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<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(
|
||||
$"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<CoreBootManager>("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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We cannot continue if the db cannot be connected to
|
||||
/// </summary>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freeze resolution to not allow Resolvers to be modified
|
||||
/// </summary>
|
||||
protected virtual void FreezeResolution()
|
||||
{
|
||||
Resolution.Freeze();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the resolvers
|
||||
/// </summary>
|
||||
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<RequiredManifestValueValidator>()
|
||||
.Add<RegexValidator>()
|
||||
.Add<DelimitedManifestValueValidator>()
|
||||
.Add<EmailValidator>()
|
||||
.Add<IntegerValidator>()
|
||||
.Add<DecimalValidator>();
|
||||
|
||||
//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<IServerRegistrar>(_ => new ConfigServerRegistrar(UmbracoConfig.For.UmbracoSettings()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Container.RegisterSingleton<IServerRegistrar>(_ =>
|
||||
new DatabaseServerRegistrar(
|
||||
new Lazy<IServerRegistrationService>(() => 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<IServerMessenger>(
|
||||
_ => 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<PluginManager>().ResolvePackageActions());
|
||||
|
||||
//the database migration objects
|
||||
MigrationCollectionBuilder.Register(Container)
|
||||
.AddProducer(() => PluginManager.ResolveTypes<IMigration>());
|
||||
|
||||
// need to filter out the ones we dont want!!
|
||||
PropertyValueConverterCollectionBuilder.Register(Container)
|
||||
.Append(PluginManager.ResolveTypes<IPropertyValueConverter>());
|
||||
|
||||
// use the new DefaultShortStringHelper
|
||||
Container.RegisterSingleton<IShortStringHelper>(factory
|
||||
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance<IUmbracoSettingsSection>())));
|
||||
|
||||
UrlSegmentProviderCollectionBuilder.Register(Container)
|
||||
.Append<DefaultUrlSegmentProvider>();
|
||||
|
||||
// by default, no factory (ie, noop) is activated
|
||||
Container.RegisterSingleton<IPublishedContentModelFactory, NoopPublishedContentModelFactory>();
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Core Umbraco runtime.
|
||||
/// </summary>
|
||||
/// <remarks>Does not handle any of the web-related aspects of Umbraco (startup, etc). It
|
||||
/// should be possible to use this runtime in console apps.</remarks>
|
||||
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<ILogger>();
|
||||
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<ConfigurationCompositionRoot>(); // fixme - used to be before caches?
|
||||
container.RegisterFrom<RepositoryCompositionRoot>();
|
||||
container.RegisterFrom<ServicesCompositionRoot>();
|
||||
container.RegisterFrom<CoreModelMappersCompositionRoot>();
|
||||
}
|
||||
|
||||
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<IServiceProvider, ActivatorServiceProvider>();
|
||||
container.RegisterSingleton<ApplicationContext>();
|
||||
container.Register<MediaFileSystem>(factory => FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>());
|
||||
}
|
||||
|
||||
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<ILogger>(), new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), factory.GetInstance<IRuntimeCacheProvider>()));
|
||||
container.RegisterSingleton<ManifestBuilder>();
|
||||
|
||||
PropertyEditorCollectionBuilder.Register(container)
|
||||
.AddProducer(factory => factory.GetInstance<PluginManager>().ResolvePropertyEditors());
|
||||
|
||||
ParameterEditorCollectionBuilder.Register(container)
|
||||
.AddProducer(factory => factory.GetInstance<PluginManager>().ResolveParameterEditors());
|
||||
|
||||
// register our predefined validators
|
||||
ValidatorCollectionBuilder.Register(container)
|
||||
.Add<RequiredManifestValueValidator>()
|
||||
.Add<RegexValidator>()
|
||||
.Add<DelimitedManifestValueValidator>()
|
||||
.Add<EmailValidator>()
|
||||
.Add<IntegerValidator>()
|
||||
.Add<DecimalValidator>();
|
||||
|
||||
// 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<IServerRegistrationService>(() => 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<IServerMessenger>(_ => new DatabaseServerMessenger(ApplicationContext, true, new DatabaseServerMessengerOptions()));
|
||||
|
||||
CacheRefresherCollectionBuilder.Register(container)
|
||||
.AddProducer(factory => factory.GetInstance<PluginManager>().ResolveCacheRefreshers());
|
||||
|
||||
PackageActionCollectionBuilder.Register(container)
|
||||
.AddProducer(f => f.GetInstance<PluginManager>().ResolvePackageActions());
|
||||
|
||||
MigrationCollectionBuilder.Register(container)
|
||||
.AddProducer(factory => factory.GetInstance<PluginManager>().ResolveTypes<IMigration>());
|
||||
|
||||
// need to filter out the ones we dont want!! fixme - what does that mean?
|
||||
PropertyValueConverterCollectionBuilder.Register(container)
|
||||
.Append(factory => factory.GetInstance<PluginManager>().ResolveTypes<IPropertyValueConverter>());
|
||||
|
||||
container.RegisterSingleton<IShortStringHelper>(factory
|
||||
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance<IUmbracoSettingsSection>())));
|
||||
|
||||
UrlSegmentProviderCollectionBuilder.Register(container)
|
||||
.Append<DefaultUrlSegmentProvider>();
|
||||
|
||||
// by default, register a noop factory
|
||||
container.RegisterSingleton<IPublishedContentModelFactory, NoopPublishedContentModelFactory>();
|
||||
}
|
||||
|
||||
#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<Type> GetComponentTypes() => Current.PluginManager.ResolveTypes<IUmbracoComponent>();
|
||||
|
||||
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<IMapperConfiguration>())
|
||||
foreach (var m in Container.GetAllInstances<ModelMapperConfiguration>())
|
||||
{
|
||||
m.ConfigureMappings(configuration, ApplicationContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="rootPath">Absolute Umbraco root path.</param>
|
||||
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<CacheHelper>(); //GetApplicationCache();
|
||||
|
||||
_timer = ProfilingLogger.TraceDuration<CoreRuntime>(
|
||||
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<ApplicationContext>();
|
||||
|
||||
// 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<PerScopeLifetime>(factory => factory.GetInstance<PluginManager>().ResolveApplicationStartupHandlers());
|
||||
|
||||
// fixme - parallel? what about our dependencies?
|
||||
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreRuntime>(
|
||||
$"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<CoreRuntime>("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur &
|
||||
/// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
|
||||
/// </summary>
|
||||
/// <param name="afterStartup"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IRuntime Startup(Action<ApplicationContext> afterStartup)
|
||||
{
|
||||
if (_isStarted)
|
||||
throw new InvalidOperationException("The boot manager has already been initialized");
|
||||
|
||||
//call OnApplicationStarting of each application events handler
|
||||
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreRuntime>(
|
||||
$"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<CoreRuntime>("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
if (afterStartup != null)
|
||||
{
|
||||
afterStartup(ApplicationContext.Current);
|
||||
}
|
||||
|
||||
_isStarted = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires after startup and calls the callback once customizations are locked
|
||||
/// </summary>
|
||||
/// <param name="afterComplete"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IRuntime Complete(Action<ApplicationContext> 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<IApplicationEventHandler>(), x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ProfilingLogger.DebugDuration<CoreRuntime>(
|
||||
$"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<CoreRuntime>("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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Freeze resolution to not allow Resolvers to be modified
|
||||
/// </summary>
|
||||
protected virtual void FreezeResolution()
|
||||
{
|
||||
Resolution.Freeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PluginManager>();
|
||||
|
||||
public static UrlSegmentProviderCollection UrlSegmentProviders
|
||||
=> Container.GetInstance<UrlSegmentProviderCollection>();
|
||||
|
||||
@@ -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<ILogger>()
|
||||
@@ -108,6 +115,10 @@ namespace Umbraco.Core.DependencyInjection
|
||||
=> _profiler ?? (_profiler = _container?.TryGetInstance<IProfiler>()
|
||||
?? new LogProfiler(Logger));
|
||||
|
||||
public static ProfilingLogger ProfilingLogger
|
||||
=> _profilingLogger ?? (_profilingLogger = _container?.TryGetInstance<ProfilingLogger>())
|
||||
?? new ProfilingLogger(Logger, Profiler);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,8 @@ namespace Umbraco.Core.DependencyInjection
|
||||
return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService && x.ServiceName == name);
|
||||
}
|
||||
|
||||
// FIXME or just use names?!
|
||||
|
||||
/// <summary>
|
||||
/// In order for LightInject to deal with enumerables of the same type, each one needs to be registered as their explicit types
|
||||
/// </summary>
|
||||
@@ -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<TLifetime>(this IServiceContainer container, Func<IServiceFactory, IEnumerable<Type>> implementationTypes)
|
||||
where TLifetime : ILifetime, new()
|
||||
{
|
||||
foreach (var type in implementationTypes(container))
|
||||
container.Register(type, new TLifetime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,5 +195,11 @@ namespace Umbraco.Core.DependencyInjection
|
||||
container.Register(type);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterCollection(this IServiceContainer container, Func<IServiceFactory, IEnumerable<Type>> implementationTypes)
|
||||
{
|
||||
foreach (var type in implementationTypes(container))
|
||||
container.Register(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,27 @@ namespace Umbraco.Core.DependencyInjection
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends types to the collections.
|
||||
/// </summary>
|
||||
/// <param name="types">The types to append.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public TBuilder Append(Func<IServiceFactory, IEnumerable<Type>> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a type after another type.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A bootstrapper interface for the Umbraco application
|
||||
/// </summary>
|
||||
public interface IBootManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Fires first in the application startup process before any customizations can occur
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IBootManager Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur
|
||||
/// </summary>
|
||||
/// <param name="afterStartup"></param>
|
||||
/// <returns></returns>
|
||||
IBootManager Startup(Action<ApplicationContext> afterStartup);
|
||||
|
||||
/// <summary>
|
||||
/// Fires after startup and calls the callback once customizations are locked
|
||||
/// </summary>
|
||||
/// <param name="afterComplete"></param>
|
||||
/// <returns></returns>
|
||||
IBootManager Complete(Action<ApplicationContext> afterComplete);
|
||||
|
||||
}
|
||||
}
|
||||
62
src/Umbraco.Core/IRuntime.cs
Normal file
62
src/Umbraco.Core/IRuntime.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using LightInject;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
// fixme - move!
|
||||
public class RuntimeState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the application is running in debug mode.
|
||||
/// </summary>
|
||||
public bool Debug { get; }
|
||||
|
||||
public RuntimeSomething Something { get; }
|
||||
}
|
||||
|
||||
public enum RuntimeSomething
|
||||
{
|
||||
Boot,
|
||||
Run
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the Umbraco runtime.
|
||||
/// </summary>
|
||||
public interface IRuntime
|
||||
{
|
||||
/// <summary>
|
||||
/// Boots the runtime.
|
||||
/// </summary>
|
||||
/// <param name="container">The application service container.</param>
|
||||
void Boot(ServiceContainer container);
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the runtime.
|
||||
/// </summary>
|
||||
void Terminate();
|
||||
|
||||
// fixme - everything below is obsolete!
|
||||
|
||||
/// <summary>
|
||||
/// Fires first in the application startup process before any customizations can occur
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IRuntime Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur
|
||||
/// </summary>
|
||||
/// <param name="afterStartup"></param>
|
||||
/// <returns></returns>
|
||||
IRuntime Startup(Action<ApplicationContext> afterStartup);
|
||||
|
||||
/// <summary>
|
||||
/// Fires after startup and calls the callback once customizations are locked
|
||||
/// </summary>
|
||||
/// <param name="afterComplete"></param>
|
||||
/// <returns></returns>
|
||||
IRuntime Complete(Action<ApplicationContext> afterComplete);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// A resolver to return all IApplicationEvents objects
|
||||
// /// </summary>
|
||||
// /// <remarks>
|
||||
// /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
|
||||
// /// </remarks>
|
||||
// internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase<ApplicationEventsResolver, IApplicationEventHandler>, IDisposable
|
||||
// {
|
||||
// private IServiceContainer _container;
|
||||
// private readonly LegacyStartupHandlerResolver _legacyResolver;
|
||||
|
||||
// public ApplicationEventsResolver(IServiceContainer container, IEnumerable<Type> applicationEventHandlers)
|
||||
// : base(container.GetInstance<IServiceProvider>(), container.GetInstance<ILogger>(), applicationEventHandlers)
|
||||
// {
|
||||
// //create the legacy resolver and only include the legacy types
|
||||
// _legacyResolver = new LegacyStartupHandlerResolver(
|
||||
// container,
|
||||
// applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>(x) == false));
|
||||
// }
|
||||
|
||||
// ///// <summary>
|
||||
// ///// Constructor
|
||||
// ///// </summary>
|
||||
// ///// <param name="logger"></param>
|
||||
// ///// <param name="applicationEventHandlers"></param>
|
||||
// ///// <param name="parentContainer"></param>
|
||||
// ///// <param name="serviceProvider"></param>
|
||||
// //internal ApplicationEventsResolver(IServiceContainer parentContainer, IServiceProvider serviceProvider, ILogger logger, IEnumerable<Type> applicationEventHandlers)
|
||||
// // : base(serviceProvider, logger, applicationEventHandlers)
|
||||
// //{
|
||||
// // //create the legacy resolver and only include the legacy types
|
||||
// // _legacyResolver = new LegacyStartupHandlerResolver(
|
||||
// // applicationEventHandlers.Where(x => !TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>(x)));
|
||||
// //}
|
||||
|
||||
// /// <summary>
|
||||
// /// Override in order to only return types of IApplicationEventHandler and above,
|
||||
// /// do not include the legacy types of IApplicationStartupHandler
|
||||
// /// </summary>
|
||||
// protected override IEnumerable<Type> InstanceTypes
|
||||
// {
|
||||
// get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>); }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the <see cref="IApplicationEventHandler"/> implementations.
|
||||
// /// </summary>
|
||||
// public IEnumerable<IApplicationEventHandler> ApplicationEventHandlers
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
|
||||
// //return Values;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Create instances of all of the legacy startup handlers
|
||||
// /// </summary>
|
||||
// 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<ApplicationEventsResolver, IApplicationStartupHandler>, IDisposable
|
||||
// {
|
||||
// internal LegacyStartupHandlerResolver(IServiceContainer container, IEnumerable<Type> applicationEventHandlers)
|
||||
// : base(container.GetInstance<IServiceProvider>(), container.GetInstance<ILogger>(), applicationEventHandlers)
|
||||
// {
|
||||
// }
|
||||
|
||||
// //internal LegacyStartupHandlerResolver(IEnumerable<Type> legacyStartupHandlers)
|
||||
// // : base(legacyStartupHandlers)
|
||||
// //{
|
||||
|
||||
// //}
|
||||
|
||||
// public IEnumerable<IApplicationStartupHandler> LegacyStartupHandlers
|
||||
// {
|
||||
// get { return Values; }
|
||||
// }
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// ResetCollections();
|
||||
// }
|
||||
// }
|
||||
|
||||
// private bool _disposed;
|
||||
// private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets a value indicating whether this instance is disposed.
|
||||
// /// </summary>
|
||||
// /// <value>
|
||||
// /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
// /// </value>
|
||||
// public bool IsDisposed
|
||||
// {
|
||||
// get { return _disposed; }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
// /// </summary>
|
||||
// /// <filterpriority>2</filterpriority>
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Clear out all of the instances, we don't want them hanging around and cluttering up memory
|
||||
// /// </summary>
|
||||
// private void DisposeResources()
|
||||
// {
|
||||
// _legacyResolver.Dispose();
|
||||
// ResetCollections();
|
||||
// }
|
||||
|
||||
// }
|
||||
//}
|
||||
@@ -1,131 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using LightInject;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Create;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A single object resolver that can be configured to use IoC to instantiate and wire up the object
|
||||
/// </summary>
|
||||
/// <typeparam name="TResolver"></typeparam>
|
||||
/// <typeparam name="TResolved"></typeparam>
|
||||
public abstract class ContainerSingleObjectResolver<TResolver, TResolved> : SingleObjectResolverBase<TResolver, TResolved>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the resolver to use IoC, when using this contructor the type must be set manually
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
internal ContainerSingleObjectResolver(IServiceContainer container)
|
||||
{
|
||||
if (container == null) throw new ArgumentNullException(nameof(container));
|
||||
_container = container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the resolver to use IoC
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="implementationType"></param>
|
||||
internal ContainerSingleObjectResolver(IServiceContainer container, Func<IServiceFactory, TResolved> implementationType)
|
||||
{
|
||||
_container = container;
|
||||
_container.Register(implementationType, new PerContainerLifetime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resolved object instance.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
/// <exception cref="ArgumentNullException">value is set to null, but cannot be null (<c>CanBeNull</c> is <c>false</c>).</exception>
|
||||
/// <exception cref="InvalidOperationException">value is read and is null, but cannot be null (<c>CanBeNull</c> is <c>false</c>),
|
||||
/// or value is set (read) and resolution is (not) frozen.</exception>
|
||||
protected override TResolved Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _container == null
|
||||
? base.Value
|
||||
: _container.GetInstance<TResolved>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolved object instance is null.
|
||||
/// </summary>
|
||||
public override bool HasValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_container == null) return base.HasValue;
|
||||
return (_container.TryGetInstance<TResolved>() == null) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the lifetime scope of resolved objects.
|
||||
/// </summary>
|
||||
public enum ObjectLifetimeScope
|
||||
{
|
||||
/// <summary>
|
||||
/// A per-request object instance is created.
|
||||
/// </summary>
|
||||
HttpRequest,
|
||||
|
||||
/// <summary>
|
||||
/// A single application-wide object instance is created.
|
||||
/// </summary>
|
||||
Application,
|
||||
|
||||
/// <summary>
|
||||
/// A new object instance is created each time one is requested.
|
||||
/// </summary>
|
||||
Transient
|
||||
}
|
||||
}
|
||||
@@ -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?
|
||||
|
||||
/// <summary>
|
||||
/// Represents the status of objects resolution.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using LightInject;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// Base non-generic class for resolvers
|
||||
/// </summary>
|
||||
public abstract class ResolverBase
|
||||
{
|
||||
protected ResolverBase(Action resetAction)
|
||||
{
|
||||
//add itself to the internal collection
|
||||
ResolverCollection.Add(this, resetAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base class for all resolvers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResolver">The type of the concrete resolver class.</typeparam>
|
||||
/// <remarks>Provides singleton management to all resolvers.</remarks>
|
||||
public abstract class ResolverBase<TResolver> : ResolverBase
|
||||
where TResolver : ResolverBase
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The underlying singleton object instance
|
||||
/// </summary>
|
||||
static TResolver _resolver;
|
||||
|
||||
/// <summary>
|
||||
/// The lock for the singleton.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor set the reset action for the underlying object
|
||||
/// </summary>
|
||||
protected ResolverBase()
|
||||
: base(() => Reset())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resolver singleton instance.
|
||||
/// </summary>
|
||||
/// <remarks>The value can be set only once, and cannot be read before it has been set.</remarks>
|
||||
/// <exception cref="InvalidOperationException">value is read before it has been set, or value is set again once it has already been set.</exception>
|
||||
/// <exception cref="ArgumentNullException">value is <c>null</c>.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a the singleton instance has been set.
|
||||
/// </summary>
|
||||
public static bool HasCurrent
|
||||
{
|
||||
get
|
||||
{
|
||||
using (new ReadLock(ResolversLock))
|
||||
{
|
||||
return _resolver != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the resolver singleton instance to null.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To be used in unit tests. DO NOT USE THIS DURING PRODUCTION.
|
||||
/// </remarks>
|
||||
/// <param name="resetResolution">
|
||||
/// 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.
|
||||
/// </param>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// Simply used to track all ManyObjectsResolverBase instances so that we can
|
||||
/// reset them all at once really easily.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
internal static class ResolverCollection
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ResolverBase, Action> Resolvers = new ConcurrentDictionary<ResolverBase, Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of resolvers created
|
||||
/// </summary>
|
||||
internal static int Count
|
||||
{
|
||||
get { return Resolvers.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all resolvers
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is called when the static Reset method or a ResolverBase{T} is called.
|
||||
/// </summary>
|
||||
internal static void Remove(ResolverBase resolver)
|
||||
{
|
||||
if (resolver == null) return;
|
||||
Action action;
|
||||
Resolvers.TryRemove(resolver, out action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a resolver to the collection
|
||||
/// </summary>
|
||||
/// <param name="resolver"></param>
|
||||
/// <param name="resetAction"></param>
|
||||
/// <remarks>
|
||||
/// This is called when the creation of a ResolverBase occurs
|
||||
/// </remarks>
|
||||
internal static void Add(ResolverBase resolver, Action resetAction)
|
||||
{
|
||||
Resolvers.TryAdd(resolver, resetAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// The base class for all single-object resolvers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResolver">The type of the concrete resolver class.</typeparam>
|
||||
/// <typeparam name="TResolved">The type of the resolved object.</typeparam>
|
||||
/// <remarks>
|
||||
/// Resolves "single" objects ie objects for which there is only one application-wide instance, such as the MVC Controller factory.
|
||||
/// </remarks>
|
||||
public abstract class SingleObjectResolverBase<TResolver, TResolved> : ResolverBase<TResolver>
|
||||
where TResolved : class
|
||||
where TResolver : ResolverBase
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
private readonly bool _canBeNull;
|
||||
private TResolved _value;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="SingleObjectResolverBase{TResolver, TResolved}"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>By default <c>CanBeNull</c> is false, so <c>Value</c> has to be initialized before being read,
|
||||
/// otherwise an exception will be thrown when reading it.</remarks>
|
||||
protected SingleObjectResolverBase()
|
||||
: this(false)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="SingleObjectResolverBase{TResolver, TResolved}"/> class with an instance of the resolved object.
|
||||
/// </summary>
|
||||
/// <param name="value">An instance of the resolved object.</param>
|
||||
/// <remarks>By default <c>CanBeNull</c> is false, so <c>value</c> has to be non-null, or <c>Value</c> has to be
|
||||
/// initialized before being accessed, otherwise an exception will be thrown when reading it.</remarks>
|
||||
protected SingleObjectResolverBase(TResolved value)
|
||||
: this(false)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="SingleObjectResolverBase{TResolver, TResolved}"/> class with a value indicating whether the resolved object instance can be null.
|
||||
/// </summary>
|
||||
/// <param name="canBeNull">A value indicating whether the resolved object instance can be null.</param>
|
||||
/// <remarks>If <c>CanBeNull</c> is false, <c>Value</c> has to be initialized before being read,
|
||||
/// otherwise an exception will be thrown when reading it.</remarks>
|
||||
protected SingleObjectResolverBase(bool canBeNull)
|
||||
{
|
||||
_canBeNull = canBeNull;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="SingleObjectResolverBase{TResolver, TResolved}"/> class with an instance of the resolved object,
|
||||
/// and a value indicating whether that instance can be null.
|
||||
/// </summary>
|
||||
/// <param name="value">An instance of the resolved object.</param>
|
||||
/// <param name="canBeNull">A value indicating whether the resolved object instance can be null.</param>
|
||||
/// <remarks>If <c>CanBeNull</c> is false, <c>value</c> has to be non-null, or <c>Value</c> has to be initialized before being read,
|
||||
/// otherwise an exception will be thrown when reading it.</remarks>
|
||||
protected SingleObjectResolverBase(TResolved value, bool canBeNull)
|
||||
{
|
||||
_value = value;
|
||||
_canBeNull = canBeNull;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
|
||||
/// </summary>
|
||||
/// <remarks>This is false by default and is used for some special internal resolvers.</remarks>
|
||||
internal bool CanResolveBeforeFrozen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolved object instance can be null.
|
||||
/// </summary>
|
||||
public bool CanBeNull
|
||||
{
|
||||
get { return _canBeNull; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolved object instance is null.
|
||||
/// </summary>
|
||||
public virtual bool HasValue
|
||||
{
|
||||
get { return _value != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resolved object instance.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
/// <exception cref="ArgumentNullException">value is set to null, but cannot be null (<c>CanBeNull</c> is <c>false</c>).</exception>
|
||||
/// <exception cref="InvalidOperationException">value is read and is null, but cannot be null (<c>CanBeNull</c> is <c>false</c>),
|
||||
/// or value is set (read) and resolution is (not) frozen.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,8 @@ namespace Umbraco.Core.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
// fixme - somehow we NEED to get rid of this Current accessor
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current plugin manager.
|
||||
/// </summary>
|
||||
@@ -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)
|
||||
|
||||
@@ -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<T> GetSortedItems<T>(List<DependencyField<T>> fields) where T : class
|
||||
{
|
||||
int[] sortOrder = GetTopologicalSortOrder(fields);
|
||||
var sortOrder = GetTopologicalSortOrder(fields);
|
||||
var list = new List<T>();
|
||||
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<T>(List<DependencyField<T>> fields) where T : class
|
||||
{
|
||||
var g = new TopologicalSorter(fields.Count());
|
||||
var g = new TopologicalSorter(fields.Count);
|
||||
var indexes = new Dictionary<string, int>();
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,15 @@
|
||||
<Compile Include="Collections\DeepCloneableList.cs" />
|
||||
<Compile Include="Collections\ListCloneBehavior.cs" />
|
||||
<Compile Include="Collections\ConcurrentHashSet.cs" />
|
||||
<Compile Include="Components\BootLoader.cs" />
|
||||
<Compile Include="Components\DisableComponentAttribute.cs" />
|
||||
<Compile Include="Components\EnableComponentAttribute.cs" />
|
||||
<Compile Include="Components\IUmbracoComponent.cs" />
|
||||
<Compile Include="Components\IUmbracoCoreComponent.cs" />
|
||||
<Compile Include="Components\IUmbracoUserComponent.cs" />
|
||||
<Compile Include="Components\RequireComponentAttribute.cs" />
|
||||
<Compile Include="Components\UmbracoComponentBase.cs" />
|
||||
<Compile Include="Components\UmbracoCoreComponent.cs" />
|
||||
<Compile Include="Configuration\CaseInsensitiveEnumConfigConverter.cs" />
|
||||
<Compile Include="Configuration\ClientDependencyConfiguration.cs" />
|
||||
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
|
||||
@@ -208,7 +217,7 @@
|
||||
<Compile Include="Constants-DataTypes.cs" />
|
||||
<Compile Include="Constants-Examine.cs" />
|
||||
<Compile Include="Constants-Icons.cs" />
|
||||
<Compile Include="CoreBootManager.cs" />
|
||||
<Compile Include="CoreRuntime.cs" />
|
||||
<Compile Include="DependencyInjection\Current.cs" />
|
||||
<Compile Include="DatabaseContext.cs" />
|
||||
<Compile Include="DataTableExtensions.cs" />
|
||||
@@ -321,7 +330,6 @@
|
||||
<Compile Include="Models\PublicAccessEntry.cs" />
|
||||
<Compile Include="Models\PublicAccessRule.cs" />
|
||||
<Compile Include="Models\Rdbms\AccessDto.cs" />
|
||||
<Compile Include="ObjectResolution\ContainerSingleObjectResolver.cs" />
|
||||
<Compile Include="Persistence\Constants-DbProviderNames.cs" />
|
||||
<Compile Include="Persistence\Constants-Locks.cs" />
|
||||
<Compile Include="Persistence\FaultHandling\RetryDbConnection.cs">
|
||||
@@ -718,8 +726,6 @@
|
||||
<Compile Include="Models\UserExtensions.cs" />
|
||||
<Compile Include="NetworkHelper.cs" />
|
||||
<Compile Include="Models\Validation\RequiredForPersistenceAttribute.cs" />
|
||||
<Compile Include="ObjectResolution\ApplicationEventsResolver.cs" />
|
||||
<Compile Include="ObjectResolution\ResolverCollection.cs" />
|
||||
<Compile Include="Collections\ObservableDictionary.cs" />
|
||||
<Compile Include="Persistence\Factories\MacroFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\ModelFactoryConfiguration.cs" />
|
||||
@@ -1123,7 +1129,7 @@
|
||||
<Compile Include="DisposableObject.cs" />
|
||||
<Compile Include="DisposableTimer.cs" />
|
||||
<Compile Include="ExpressionHelper.cs" />
|
||||
<Compile Include="IBootManager.cs" />
|
||||
<Compile Include="IRuntime.cs" />
|
||||
<Compile Include="IntExtensions.cs" />
|
||||
<Compile Include="LambdaExpressionCacheKey.cs" />
|
||||
<Compile Include="Strings\DefaultShortStringHelperConfig.cs" />
|
||||
@@ -1137,10 +1143,7 @@
|
||||
<Compile Include="Logging\LoggingTaskExtension.cs" />
|
||||
<Compile Include="Logging\LogHelper.cs" />
|
||||
<Compile Include="ObjectExtensions.cs" />
|
||||
<Compile Include="ObjectResolution\ObjectLifetimeScope.cs" />
|
||||
<Compile Include="ObjectResolution\Resolution.cs" />
|
||||
<Compile Include="ObjectResolution\ResolverBase.cs" />
|
||||
<Compile Include="ObjectResolution\SingleObjectResolverBase.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\YesNoValueConverter.cs" />
|
||||
<Compile Include="Services\PublishStatus.cs" />
|
||||
<Compile Include="Services\PublishStatusType.cs" />
|
||||
|
||||
@@ -10,20 +10,21 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The abstract class for the Umbraco HttpApplication
|
||||
/// Provides an abstract base class for the Umbraco HttpApplication.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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?
|
||||
/// </remarks>
|
||||
public abstract class UmbracoApplicationBase : HttpApplication
|
||||
{
|
||||
private IRuntime _runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boot manager.
|
||||
/// Gets a runtime.
|
||||
/// </summary>
|
||||
protected abstract IBootManager GetBootManager();
|
||||
protected abstract IRuntime GetRuntime();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a logger.
|
||||
@@ -33,11 +34,17 @@ namespace Umbraco.Core
|
||||
return Logger.CreateWithDefaultLog4NetConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boots up the Umbraco application.
|
||||
/// </summary>
|
||||
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<UmbracoApplicationBase>(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;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the HttpApplication.Init() is fired, allows developers to subscribe to the HttpApplication events
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Needs to be static otherwise null refs occur - though I don't know why FIXME wtf?
|
||||
/// </remarks>
|
||||
public static event EventHandler ApplicationInit;
|
||||
public static event EventHandler ApplicationError;
|
||||
public static event EventHandler ApplicationEnd;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Umbraco application
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override init and raise the event
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
#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<UmbracoApplicationBase>("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<UmbracoApplicationBase>(shutdownMsg);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//if for some reason that fails, then log the normal output
|
||||
Current.Logger.Info<UmbracoApplicationBase>("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<UmbracoApplicationBase>("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
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Developers can override this method to modify objects on startup
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnApplicationStarting(object sender, EventArgs e)
|
||||
/// <param name="evargs"></param>
|
||||
protected virtual void OnApplicationStarting(object sender, EventArgs evargs)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationStarting?.Invoke(sender, e);
|
||||
ApplicationStarting?.Invoke(sender, evargs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationStarting event handler", ex);
|
||||
Current.Logger.Error<UmbracoApplicationBase>("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.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnApplicationStarted(object sender, EventArgs e)
|
||||
/// <param name="evargs"></param>
|
||||
protected virtual void OnApplicationStarted(object sender, EventArgs evargs)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationStarted?.Invoke(sender, e);
|
||||
ApplicationStarted?.Invoke(sender, evargs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationStarted event handler", ex);
|
||||
Current.Logger.Error<UmbracoApplicationBase>("An error occurred in an ApplicationStarted event handler", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to raise the ApplicationInit event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnApplicationInit(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationInit?.Invoke(sender, e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationInit event handler", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method that can be overridden to invoke code when the application has an error.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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<UmbracoApplicationBase>("An unhandled exception occurred", exc);
|
||||
|
||||
OnApplicationError(sender, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method that can be overridden to invoke code when the application shuts down.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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<UmbracoApplicationBase>("Application shutdown. Details: " + shutdownMsg);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//if for some reason that fails, then log the normal output
|
||||
Current.Logger.Info<UmbracoApplicationBase>("Application shutdown. Reason: " + HostingEnvironment.ShutdownReason);
|
||||
}
|
||||
}
|
||||
OnApplicationEnd(sender, e);
|
||||
|
||||
// last thing to do is shutdown log4net
|
||||
LogManager.Shutdown();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
153
src/Umbraco.Tests/Components/ComponentTests.cs
Normal file
153
src/Umbraco.Tests/Components/ComponentTests.cs
Normal file
@@ -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<Type> Composed = new List<Type>();
|
||||
private static readonly List<string> Initialized = new List<string>();
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Current.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Boot()
|
||||
{
|
||||
var container = new ServiceContainer();
|
||||
container.ConfigureUmbracoCore();
|
||||
|
||||
var logger = Mock.Of<ILogger>();
|
||||
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<ILogger>();
|
||||
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<ISomeResource, SomeResource>();
|
||||
|
||||
var logger = Mock.Of<ILogger>();
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<Assembly> AssembliesToScan
|
||||
{
|
||||
get
|
||||
protected virtual IEnumerable<Assembly> AssembliesToScan
|
||||
=> new[]
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
this.GetType().Assembly // this assembly only
|
||||
};
|
||||
}
|
||||
}
|
||||
GetType().Assembly // this assembly only
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<BaseResolver>
|
||||
{ }
|
||||
|
||||
class BaseResolver2 : ResolverBase<BaseResolver2>
|
||||
{ }
|
||||
|
||||
class BaseResolver3 : ResolverBase<BaseResolver3>
|
||||
{ }
|
||||
|
||||
#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<InvalidOperationException>(() =>
|
||||
{
|
||||
var c = BaseResolver.Current;
|
||||
});
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var c = BaseResolver2.Current;
|
||||
});
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var c = BaseResolver3.Current;
|
||||
});
|
||||
|
||||
//this should not error!
|
||||
BaseResolver.Current = new BaseResolver();
|
||||
BaseResolver2.Current = new BaseResolver2();
|
||||
BaseResolver3.Current = new BaseResolver3();
|
||||
|
||||
Assert.Pass();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<SingleResolver, Resolved>
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
SettingsForTests.UmbracoPath = "~/umbraco";
|
||||
|
||||
var webBoot = new WebBootManager(new UmbracoApplication(), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), true);
|
||||
var webBoot = new WebRuntime(new UmbracoApplication(), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), 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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// test application using a CoreBootManager instance to boot
|
||||
/// </summary>
|
||||
public class TestApp : UmbracoApplicationBase
|
||||
{
|
||||
private readonly ILogger _logger = Mock.Of<ILogger>();
|
||||
|
||||
protected override IBootManager GetBootManager()
|
||||
{
|
||||
return new TestBootManager(this, new ProfilingLogger(_logger, Mock.Of<IProfiler>()));
|
||||
}
|
||||
|
||||
protected override ILogger GetLogger()
|
||||
{
|
||||
return _logger;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test boot manager to add a custom application event handler
|
||||
/// </summary>
|
||||
public class TestBootManager : CoreBootManager
|
||||
{
|
||||
public TestBootManager(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
|
||||
: base(umbracoApplication, logger)
|
||||
{ }
|
||||
|
||||
internal override void ConfigureCoreServices(ServiceContainer container)
|
||||
{
|
||||
base.ConfigureCoreServices(container);
|
||||
|
||||
container.Register<IUmbracoSettingsSection>(factory => SettingsForTests.GetDefault());
|
||||
container.Register<DatabaseContext>(factory => new DatabaseContext(
|
||||
factory.GetInstance<IDatabaseFactory>(),
|
||||
factory.GetInstance<ILogger>()), new PerContainerLifetime());
|
||||
container.RegisterSingleton<IExamineIndexCollectionAccessor, TestIndexCollectionAccessor>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// test event handler
|
||||
/// </summary>
|
||||
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<ILogger>();
|
||||
|
||||
protected override IRuntime GetRuntime()
|
||||
{
|
||||
return new TestRuntime(this, new ProfilingLogger(_logger, Mock.Of<IProfiler>()));
|
||||
}
|
||||
|
||||
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<IUmbracoSettingsSection>(factory => SettingsForTests.GetDefault());
|
||||
container.Register<DatabaseContext>(factory => new DatabaseContext(
|
||||
factory.GetInstance<IDatabaseFactory>(),
|
||||
factory.GetInstance<ILogger>()), new PerContainerLifetime());
|
||||
container.RegisterSingleton<IExamineIndexCollectionAccessor, TestIndexCollectionAccessor>();
|
||||
}
|
||||
|
||||
protected override IEnumerable<Type> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ProfilingViewEngine>());
|
||||
@@ -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<RenderViewEngine>());
|
||||
@@ -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<IViewEngine> engines = null;
|
||||
Assert.DoesNotThrow(() => WebBootManager.WrapViewEngines(engines));
|
||||
Assert.DoesNotThrow(() => WebRuntime.WrapViewEngines(engines));
|
||||
Assert.That(engines, Is.Null);
|
||||
}
|
||||
|
||||
@@ -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[]
|
||||
{
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Cache\RefresherTests.cs" />
|
||||
<Compile Include="Cache\SnapDictionaryTests.cs" />
|
||||
<Compile Include="Components\ComponentTests.cs" />
|
||||
<Compile Include="CoreXml\RenamedRootNavigatorTests.cs" />
|
||||
<Compile Include="DependencyInjection\TempTests.cs" />
|
||||
<Compile Include="Integration\ContentEventsTests.cs" />
|
||||
@@ -270,7 +271,7 @@
|
||||
<Compile Include="Persistence\Repositories\PublicAccessRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskTypeRepositoryTest.cs" />
|
||||
<Compile Include="Resolvers\ResolverBaseTest.cs" />
|
||||
<Compile Include="DependencyInjection\ResolverBaseTest.cs" />
|
||||
<Compile Include="Routing\RoutesCacheTests.cs" />
|
||||
<Compile Include="Routing\UrlRoutingTestBase.cs" />
|
||||
<Compile Include="Security\BackOfficeCookieManagerTests.cs" />
|
||||
@@ -316,8 +317,8 @@
|
||||
<Compile Include="Web\Mvc\RenderModelBinderTests.cs" />
|
||||
<Compile Include="Web\Mvc\SurfaceControllerTests.cs" />
|
||||
<Compile Include="Web\Mvc\UmbracoViewPageTests.cs" />
|
||||
<Compile Include="BootManagers\CoreBootManagerTests.cs" />
|
||||
<Compile Include="BootManagers\WebBootManagerTests.cs" />
|
||||
<Compile Include="Runtimes\CoreRuntimeTests.cs" />
|
||||
<Compile Include="Runtimes\WebRuntimeTests.cs" />
|
||||
<Compile Include="Cache\ObjectCacheProviderTests.cs" />
|
||||
<Compile Include="Cache\CacheProviderTests.cs" />
|
||||
<Compile Include="Cache\HttpRequestCacheProviderTests.cs" />
|
||||
@@ -389,7 +390,7 @@
|
||||
<Compile Include="Services\LocalizedTextServiceTests.cs" />
|
||||
<Compile Include="Services\TagServiceTests.cs" />
|
||||
<Compile Include="Services\LocalizationServiceTests.cs" />
|
||||
<Compile Include="Resolvers\XsltExtensionsResolverTests.cs" />
|
||||
<Compile Include="DependencyInjection\XsltExtensionsResolverTests.cs" />
|
||||
<Compile Include="Services\MemberServiceTests.cs" />
|
||||
<Compile Include="Services\MediaServiceTests.cs" />
|
||||
<Compile Include="Services\MemberTypeServiceTests.cs" />
|
||||
@@ -478,7 +479,7 @@
|
||||
<Compile Include="Persistence\Repositories\StylesheetRepositoryTest.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentMoreTests.cs" />
|
||||
<Compile Include="Publishing\PublishingStrategyTests.cs" />
|
||||
<Compile Include="Resolvers\ActionCollectionTests.cs" />
|
||||
<Compile Include="DependencyInjection\ActionCollectionTests.cs" />
|
||||
<Compile Include="Logging\RingBufferTest.cs" />
|
||||
<Compile Include="TestHelpers\BaseUmbracoConfigurationTest.cs" />
|
||||
<Compile Include="TestHelpers\DatabaseTestBehaviorAttribute.cs" />
|
||||
@@ -496,10 +497,8 @@
|
||||
<Compile Include="TreesAndSections\ApplicationTreeTest.cs" />
|
||||
<Compile Include="ObjectExtensionsTests.cs" />
|
||||
<Compile Include="Cache\PublishedCache\PublishedContentCacheTests.cs" />
|
||||
<Compile Include="Resolvers\LazyCollectionBuilderTests.cs" />
|
||||
<Compile Include="Resolvers\CollectionBuildersTests.cs" />
|
||||
<Compile Include="Resolvers\SingleResolverTests.cs" />
|
||||
<Compile Include="Resolvers\ResolutionTests.cs" />
|
||||
<Compile Include="DependencyInjection\LazyCollectionBuilderTests.cs" />
|
||||
<Compile Include="DependencyInjection\CollectionBuildersTests.cs" />
|
||||
<Compile Include="Routing\ContentFinderByAliasWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\ContentFinderByNiceUrlWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\DomainsAndCulturesTests.cs" />
|
||||
@@ -561,7 +560,7 @@
|
||||
<Compile Include="UmbracoExamine\ExamineDemoDataContentService.cs" />
|
||||
<Compile Include="UriExtensionsTests.cs" />
|
||||
<Compile Include="UriUtilityTests.cs" />
|
||||
<Compile Include="Resolvers\PackageActionsResolverTests.cs" />
|
||||
<Compile Include="DependencyInjection\PackageActionCollectionTests.cs" />
|
||||
<Compile Include="Plugins\PluginManagerExtensions.cs" />
|
||||
<Compile Include="Plugins\PluginManagerTests.cs" />
|
||||
<Compile Include="TestHelpers\Stubs\FakeLastChanceFinder.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
|
||||
}
|
||||
}
|
||||
|
||||
44
src/Umbraco.Web/ManifestWatcherComponent.cs
Normal file
44
src/Umbraco.Web/ManifestWatcherComponent.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,5 @@ namespace Umbraco.Web.Strategies
|
||||
content, ActionUnPublish.Instance, applicationContext));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +210,7 @@
|
||||
<Compile Include="Macros\XsltMacroEngine.cs" />
|
||||
<Compile Include="HealthCheck\StatusResultType.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DataIntegrity\XmlDataIntegrityHealthCheck.cs" />
|
||||
<Compile Include="ManifestWatcherComponent.cs" />
|
||||
<Compile Include="Media\ImageUrlProviderCollection.cs" />
|
||||
<Compile Include="Media\ImageUrlProviderCollectionBuilder.cs" />
|
||||
<Compile Include="Media\ThumbnailProviders\ThumbnailProviderCollection.cs" />
|
||||
@@ -1087,7 +1088,7 @@
|
||||
<Compile Include="WebApi\UmbracoAuthorizedApiController.cs" />
|
||||
<Compile Include="WebApi\Filters\ValidationFilterAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\UmbracoUserTimeoutFilterAttribute.cs" />
|
||||
<Compile Include="WebBootManager.cs" />
|
||||
<Compile Include="WebRuntime.cs" />
|
||||
<Compile Include="Routing\LegacyRequestInitializer.cs" />
|
||||
<Compile Include="Mvc\ControllerExtensions.cs" />
|
||||
<Compile Include="Routing\ContentFinderByUrlAlias.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
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user