using System; using System.Collections.Generic; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; 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.Services; using Umbraco.Core.Sync; using Umbraco.Core.Strings; using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero; namespace Umbraco.Core { /// /// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application /// /// /// This does not provide any startup functionality relating to web objects /// public class CoreBootManager : IBootManager { private DisposableTimer _timer; private bool _isInitialized = false; private bool _isStarted = false; private bool _isComplete = false; private readonly UmbracoApplicationBase _umbracoApplication; protected ApplicationContext ApplicationContext { get; private set; } protected UmbracoApplicationBase UmbracoApplication { get { return _umbracoApplication; } } 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"); InitializeProfilerResolver(); _timer = DisposableTimer.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); //create database and service contexts for the app context var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName); Database.Mapper = new PetaPocoMapper(); var dbContext = new DatabaseContext(dbFactory); var serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy()); CreateApplicationContext(dbContext, serviceContext); InitializeApplicationEventsResolver(); InitializeResolvers(); //initialize the DatabaseContext dbContext.Initialize(); //now we need to call the initialize methods ApplicationEventsResolver.Current.ApplicationEventHandlers .ForEach(x => x.OnApplicationInitialized(UmbracoApplication, ApplicationContext)); _isInitialized = true; return this; } /// /// Creates and assigns the application context singleton /// /// /// protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) { //create the ApplicationContext ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext); } /// /// Special method to initialize the ProfilerResolver /// 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()) { //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 }; } /// /// Special method to initialize the ApplicationEventsResolver and any modifications required for it such /// as adding custom types to the resolver. /// 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( PluginManager.Current.ResolveApplicationStartupHandlers()) { CanResolveBeforeFrozen = true }; // add custom types here that are internal, if needed //ApplicationEventsResolver.Current.AddType<>(); } /// /// Special method to extend the use of Umbraco by enabling the consumer to overwrite /// the absolute path to the root of an Umbraco site/solution, which is used for stuff /// like Umbraco.Core.IO.IOHelper.MapPath etc. /// /// Absolute protected virtual void InitializeApplicationRootPath(string rootPath) { Umbraco.Core.IO.IOHelper.SetRootDirectory(rootPath); } /// /// Fires after initialization and calls the callback to allow for customizations to occur & /// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called /// /// /// public virtual IBootManager Startup(Action afterStartup) { if (_isStarted) throw new InvalidOperationException("The boot manager has already been initialized"); //call OnApplicationStarting of each application events handler ApplicationEventsResolver.Current.ApplicationEventHandlers .ForEach(x => x.OnApplicationStarting(UmbracoApplication, ApplicationContext)); if (afterStartup != null) { afterStartup(ApplicationContext.Current); } _isStarted = true; return this; } /// /// Fires after startup and calls the callback once customizations are locked /// /// /// public virtual IBootManager Complete(Action afterComplete) { if (_isComplete) throw new InvalidOperationException("The boot manager has already been completed"); FreezeResolution(); //stop the timer and log the output _timer.Dispose(); //call OnApplicationStarting of each application events handler ApplicationEventsResolver.Current.ApplicationEventHandlers .ForEach(x => x.OnApplicationStarted(UmbracoApplication, ApplicationContext)); //Now, startup all of our legacy startup handler ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers(); if (afterComplete != null) { afterComplete(ApplicationContext.Current); } _isComplete = true; // we're ready to serve content! ApplicationContext.IsReady = true; return this; } /// /// Freeze resolution to not allow Resolvers to be modified /// protected virtual void FreezeResolution() { Resolution.Freeze(); } /// /// Create the resolvers /// protected virtual void InitializeResolvers() { //by default we'll use the standard configuration based sync ServerRegistrarResolver.Current = new ServerRegistrarResolver( new ConfigServerRegistrar()); //by default (outside of the web) we'll use the default server messenger without //supplying a username/password, this will automatically disable distributed calls // .. we'll override this in the WebBootManager ServerMessengerResolver.Current = new ServerMessengerResolver( new DefaultServerMessenger()); MappingResolver.Current = new MappingResolver( () => PluginManager.Current.ResolveAssignedMapperTypes()); RepositoryResolver.Current = new RepositoryResolver( new RepositoryFactory()); SqlSyntaxProvidersResolver.Current = new SqlSyntaxProvidersResolver( new[] { typeof(MySqlSyntaxProvider), typeof(SqlCeSyntaxProvider), typeof(SqlServerSyntaxProvider) }) { CanResolveBeforeFrozen = true }; CacheRefreshersResolver.Current = new CacheRefreshersResolver( () => PluginManager.Current.ResolveCacheRefreshers()); DataTypesResolver.Current = new DataTypesResolver( () => PluginManager.Current.ResolveDataTypes()); MacroFieldEditorsResolver.Current = new MacroFieldEditorsResolver( () => PluginManager.Current.ResolveMacroRenderings()); PackageActionsResolver.Current = new PackageActionsResolver( () => PluginManager.Current.ResolvePackageActions()); ActionsResolver.Current = new ActionsResolver( () => PluginManager.Current.ResolveActions()); //the database migration objects MigrationResolver.Current = new MigrationResolver( () => PluginManager.Current.ResolveMigrationTypes()); // todo: remove once we drop IPropertyEditorValueConverter support. PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver( PluginManager.Current.ResolvePropertyEditorValueConverters()); // initialize the new property value converters by discovering IPropertyValueConverter PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver( PluginManager.Current.ResolveTypes()); // use the new DefaultShortStringHelper but sort-of remain compatible // - use UmbracoSettings UrlReplaceCharacters // - allow underscores in terms, allow leading digits ShortStringHelperResolver.Current = new ShortStringHelperResolver( new DefaultShortStringHelper() .WithConfig(CleanStringType.Url, DefaultShortStringHelper.ApplyUrlReplaceCharacters, allowUnderscoreInTerm: true, allowLeadingDigits: true)); // that was the old one //ShortStringHelperResolver.Current = new ShortStringHelperResolver( // new LegacyShortStringHelper()); UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver( typeof (DefaultUrlSegmentProvider)); // keep it internal for now PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(); // new PublishedContentModelFactoryImpl()); } } }