Files
Umbraco-CMS/src/Umbraco.Core/CoreBootManager.cs

480 lines
21 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web;
using AutoMapper;
2013-08-09 11:37:57 +10:00
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix;
using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Profiling;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Publishing;
using Umbraco.Core.Macros;
using Umbraco.Core.Manifest;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Core.Strings;
using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero;
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
{
private ProfilingLogger _profilingLogger;
private DisposableTimer _timer;
private bool _isInitialized = false;
private bool _isStarted = false;
private bool _isComplete = false;
private readonly IServiceProvider _serviceProvider = new ActivatorServiceProvider();
private readonly UmbracoApplicationBase _umbracoApplication;
protected ApplicationContext ApplicationContext { get; set; }
protected CacheHelper ApplicationCache { get; set; }
protected PluginManager PluginManager { get; private set; }
2012-08-14 12:03:34 +06:00
protected UmbracoApplicationBase UmbracoApplication
{
get { return _umbracoApplication; }
}
protected IServiceProvider ServiceProvider
{
get { return _serviceProvider; }
}
public CoreBootManager(UmbracoApplicationBase umbracoApplication)
{
if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
_umbracoApplication = umbracoApplication;
}
public virtual IBootManager Initialize()
{
if (_isInitialized)
throw new InvalidOperationException("The boot manager has already been initialized");
InitializeLoggerResolver();
InitializeProfilerResolver();
_profilingLogger = new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler);
_timer = _profilingLogger.TraceDuration<CoreBootManager>(
Merge branch 7.2.7 into dev-v7 Conflicts: build/UmbracoVersion.txt src/SolutionInfo.cs src/Umbraco.Core/ApplicationContext.cs src/Umbraco.Core/AsyncLock.cs src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs src/Umbraco.Core/CoreBootManager.cs src/Umbraco.Core/Logging/LogHelper.cs src/Umbraco.Core/PluginManager.cs src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs src/Umbraco.Core/TypeFinder.cs src/Umbraco.Core/Umbraco.Core.csproj src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs src/Umbraco.Tests/ServerEnvironmentHelperTests.cs src/Umbraco.Web.UI.Client/bower.json src/Umbraco.Web.UI.Client/gruntFile.js src/Umbraco.Web.UI.Client/src/less/grid.less src/Umbraco.Web.UI.Client/src/less/panel.less src/Umbraco.Web.UI.Client/src/less/property-editors.less src/Umbraco.Web.UI.Client/src/loader.js src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js src/Umbraco.Web.UI/Umbraco.Web.UI.csproj src/Umbraco.Web.UI/config/ClientDependency.config src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs src/Umbraco.Web/Scheduling/KeepAlive.cs src/Umbraco.Web/Scheduling/LatchedBackgroundTaskBase.cs src/Umbraco.Web/Scheduling/LogScrubber.cs src/Umbraco.Web/Scheduling/RecurringTaskBase.cs src/Umbraco.Web/Scheduling/ScheduledPublishing.cs src/Umbraco.Web/Scheduling/ScheduledTasks.cs src/Umbraco.Web/Scheduling/Scheduler.cs src/Umbraco.Web/UI/JavaScript/JsInitialize.js src/Umbraco.Web/Umbraco.Web.csproj src/Umbraco.Web/umbraco.presentation/content.cs src/umbraco.cms/Actions/Action.cs
2015-07-13 22:45:37 +02:00
string.Format("Umbraco {0} application starting on {1}", UmbracoVersion.GetSemanticVersion().ToSemanticString(), NetworkHelper.MachineName),
"Umbraco application startup complete");
CreateApplicationCache();
//create and set the plugin manager (I'd much prefer to not use this singleton anymore but many things are using it unfortunately and
// the way that it is setup, there must only ever be one per app so without IoC it would be hard to make this not a singleton)
PluginManager = new PluginManager(ServiceProvider, ApplicationCache.RuntimeCache, _profilingLogger);
PluginManager.Current = PluginManager;
//Create the legacy prop-eds mapping
LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors();
LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors();
//create database and service contexts for the app context
var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, LoggerResolver.Current.Logger);
Database.Mapper = new PetaPocoMapper();
var dbContext = new DatabaseContext(
dbFactory,
LoggerResolver.Current.Logger,
SqlSyntaxProviders.CreateDefault(LoggerResolver.Current.Logger));
//initialize the DatabaseContext
dbContext.Initialize();
var serviceContext = new ServiceContext(
new RepositoryFactory(ApplicationCache, LoggerResolver.Current.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(dbFactory),
new FileUnitOfWorkProvider(),
new PublishingStrategy(),
ApplicationCache,
LoggerResolver.Current.Logger);
CreateApplicationContext(dbContext, serviceContext);
InitializeApplicationEventsResolver();
InitializeResolvers();
InitializeModelMappers();
2015-07-14 10:49:43 +02:00
using (_profilingLogger.DebugDuration<CoreBootManager>(
string.Format("Executing {0} IApplicationEventHandler.OnApplicationInitialized", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
"Finished executing IApplicationEventHandler.OnApplicationInitialized"))
2015-07-08 16:45:34 +02:00
{
//now we need to call the initialize methods
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
2015-07-08 16:27:06 +02:00
{
2015-07-08 16:45:34 +02:00
try
{
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
2015-07-14 10:49:43 +02:00
_profilingLogger.Logger.Error<CoreBootManager>("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
2015-07-08 16:45:34 +02:00
throw;
}
});
}
_isInitialized = true;
return this;
}
/// <summary>
/// Creates and assigns the application context singleton
/// </summary>
/// <param name="dbContext"></param>
/// <param name="serviceContext"></param>
protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext)
{
//create the ApplicationContext
ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext, ApplicationCache, _profilingLogger);
}
/// <summary>
/// Creates and assigns the ApplicationCache based on a new instance of System.Web.Caching.Cache
/// </summary>
protected virtual void CreateApplicationCache()
{
2013-08-09 11:37:57 +10:00
var cacheHelper = new CacheHelper(
new ObjectCacheRuntimeCacheProvider(),
new StaticCacheProvider(),
//we have no request based cache when not running in web-based context
2013-08-09 11:37:57 +10:00
new NullCacheProvider());
ApplicationCache = cacheHelper;
}
/// <summary>
/// This method allows for configuration of model mappers
/// </summary>
/// <remarks>
/// Model mappers MUST be defined on ApplicationEventHandler instances with the interface IMapperConfiguration.
/// This allows us to search for less types on startup.
/// </remarks>
protected void InitializeModelMappers()
{
Mapper.Initialize(configuration =>
{
foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType<IMapperConfiguration>())
{
m.ConfigureMappings(configuration, ApplicationContext);
}
});
}
/// <summary>
/// Special method to initialize the LoggerResolver
/// </summary>
protected virtual void InitializeLoggerResolver()
{
LoggerResolver.Current = new LoggerResolver(Logger.CreateWithDefaultLog4NetConfiguration())
{
//This is another special resolver that needs to be resolvable before resolution is frozen
//since it is used for profiling the application startup
CanResolveBeforeFrozen = true
};
}
/// <summary>
/// Special method to initialize the ProfilerResolver
/// </summary>
protected virtual void InitializeProfilerResolver()
{
//By default we'll initialize the Log profiler (in the web project, we'll override with the web profiler)
ProfilerResolver.Current = new ProfilerResolver(new LogProfiler(LoggerResolver.Current.Logger))
{
//This is another special resolver that needs to be resolvable before resolution is frozen
//since it is used for profiling the application startup
CanResolveBeforeFrozen = true
};
}
/// <summary>
/// Special method to initialize the ApplicationEventsResolver and any modifications required for it such
/// as adding custom types to the resolver.
/// </summary>
protected virtual void InitializeApplicationEventsResolver()
{
//find and initialize the application startup handlers, we need to initialize this resolver here because
//it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to
//events and to call their events during bootup.
//ApplicationStartupHandler.RegisterHandlers();
//... and set the special flag to let us resolve before frozen resolution
ApplicationEventsResolver.Current = new ApplicationEventsResolver(
ServiceProvider,
LoggerResolver.Current.Logger,
PluginManager.ResolveApplicationStartupHandlers())
{
CanResolveBeforeFrozen = true
};
}
/// <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)
{
2015-07-14 10:49:43 +02:00
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");
2015-07-14 10:49:43 +02:00
using (_profilingLogger.DebugDuration<CoreBootManager>(
string.Format("Executing {0} IApplicationEventHandler.OnApplicationStarting", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
"Finished executing IApplicationEventHandler.OnApplicationStarting"))
2015-07-08 16:45:34 +02:00
{
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
{
try
{
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
2015-07-14 10:49:43 +02:00
_profilingLogger.Logger.Error<CoreBootManager>("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
2015-07-08 16:45:34 +02:00
throw;
}
});
}
if (afterStartup != null)
2013-02-14 23:05:58 +06:00
{
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();
2015-07-14 10:49:43 +02:00
using (_profilingLogger.DebugDuration<CoreBootManager>(
string.Format("Executing {0} IApplicationEventHandler.OnApplicationStarted", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
"Finished executing IApplicationEventHandler.OnApplicationStarted"))
2015-07-08 16:45:34 +02:00
{
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
2015-07-08 16:27:06 +02:00
{
2015-07-08 16:45:34 +02:00
try
{
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
2015-07-14 10:49:43 +02:00
_profilingLogger.Logger.Error<CoreBootManager>("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex);
2015-07-08 16:45:34 +02:00
throw;
}
});
}
2013-02-14 23:05:58 +06:00
//Now, startup all of our legacy startup handler
2013-04-16 04:27:03 -02:00
ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers();
2013-02-14 23:05:58 +06:00
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;
var currentTry = 0;
while (currentTry < 5)
{
if (ApplicationContext.DatabaseContext.CanConnect)
break;
//wait and retry
Thread.Sleep(1000);
currentTry++;
}
if (currentTry == 5)
{
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
}
}
2013-03-12 00:21:10 +04:00
/// <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 builder = new ManifestBuilder(
ApplicationCache.RuntimeCache,
new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationCache.RuntimeCache));
2015-03-19 17:42:57 +11:00
PropertyEditorResolver.Current = new PropertyEditorResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolvePropertyEditors(), builder);
ParameterEditorResolver.Current = new ParameterEditorResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolveParameterEditors(), builder);
//setup the validators resolver with our predefined validators
ValidatorsResolver.Current = new ValidatorsResolver(
ServiceProvider, LoggerResolver.Current.Logger, new[]
{
new Lazy<Type>(() => typeof (RequiredManifestValueValidator)),
new Lazy<Type>(() => typeof (RegexValidator)),
new Lazy<Type>(() => typeof (DelimitedManifestValueValidator)),
new Lazy<Type>(() => typeof (EmailValidator)),
new Lazy<Type>(() => typeof (IntegerValidator)),
});
//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)
{
ServerRegistrarResolver.Current = new ServerRegistrarResolver(new ConfigServerRegistrar());
}
else
{
ServerRegistrarResolver.Current = new ServerRegistrarResolver(
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
ServerMessengerResolver.Current = new ServerMessengerResolver(
new DatabaseServerMessenger(ApplicationContext, true, new DatabaseServerMessengerOptions()));
MappingResolver.Current = new MappingResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolveAssignedMapperTypes());
//RepositoryResolver.Current = new RepositoryResolver(
// new RepositoryFactory(ApplicationCache));
CacheRefreshersResolver.Current = new CacheRefreshersResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolveCacheRefreshers());
DataTypesResolver.Current = new DataTypesResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolveDataTypes());
MacroFieldEditorsResolver.Current = new MacroFieldEditorsResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolveMacroRenderings());
PackageActionsResolver.Current = new PackageActionsResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolvePackageActions());
ActionsResolver.Current = new ActionsResolver(
ServiceProvider, LoggerResolver.Current.Logger,
() => PluginManager.ResolveActions());
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
LoggerResolver.Current.Logger,
() => PluginManager.ResolveTypes<IMigration>());
2013-09-23 16:55:59 +02:00
// todo: remove once we drop IPropertyEditorValueConverter support.
2013-09-05 17:47:13 +02:00
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
ServiceProvider, LoggerResolver.Current.Logger,
PluginManager.ResolvePropertyEditorValueConverters());
// need to filter out the ones we dont want!!
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
ServiceProvider, LoggerResolver.Current.Logger,
PluginManager.ResolveTypes<IPropertyValueConverter>());
// use the new DefaultShortStringHelper
ShortStringHelperResolver.Current = new ShortStringHelperResolver(
//new LegacyShortStringHelper());
new DefaultShortStringHelper(UmbracoConfig.For.UmbracoSettings()).WithDefaultConfig());
UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver(
ServiceProvider, LoggerResolver.Current.Logger,
typeof(DefaultUrlSegmentProvider));
2014-05-19 13:15:47 +02:00
// by default, no factory is activated
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
}
}
}