Resvolution - Booting

This commit is contained in:
Stephan
2016-08-25 15:09:51 +02:00
parent 9949f07a46
commit 9dcc6b285f
48 changed files with 2282 additions and 2753 deletions

View File

@@ -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

View 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();
}
}
}
}
}
}

View 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; }
}
}

View 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; }
}
}

View File

@@ -0,0 +1,11 @@
using LightInject;
namespace Umbraco.Core.Components
{
public interface IUmbracoComponent
{
void Compose(ServiceContainer container);
void Terminate();
}
}

View File

@@ -0,0 +1,5 @@
namespace Umbraco.Core.Components
{
public interface IUmbracoCoreComponent : IUmbracoComponent
{ }
}

View File

@@ -0,0 +1,5 @@
namespace Umbraco.Core.Components
{
public interface IUmbracoUserComponent : IUmbracoComponent
{ }
}

View 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; }
}
}

View 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()
{ }
}
}

View File

@@ -0,0 +1,5 @@
namespace Umbraco.Core.Components
{
public class UmbracoCoreComponent : UmbracoComponentBase
{ }
}

View File

@@ -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();
}
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -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);
}
}

View 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);
}
}

View File

@@ -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();
// }
// }
//}

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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" />

View File

@@ -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
}
}

View 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 { }
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
};
}
}

View File

@@ -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;

View File

@@ -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
}
}

View File

@@ -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();
}
}
}

View File

@@ -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
}
}

View File

@@ -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);

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}

View File

@@ -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[]
{

View File

@@ -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" />

View File

@@ -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
}
}

View 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;
}
}
}

View File

@@ -74,6 +74,5 @@ namespace Umbraco.Web.Strategies
content, ActionUnPublish.Instance, applicationContext));
}
}
}

View File

@@ -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" />

View File

@@ -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);
}
}
}