2016-08-25 15:09:51 +02:00
using System ;
using System.Collections.Generic ;
2018-11-19 22:02:51 +11:00
using System.Reflection ;
2016-08-25 15:09:51 +02:00
using System.Threading ;
2016-11-03 10:31:44 +01:00
using System.Web ;
2016-08-25 15:09:51 +02:00
using Umbraco.Core.Cache ;
using Umbraco.Core.Components ;
2017-05-30 15:46:25 +02:00
using Umbraco.Core.Composing ;
2017-12-28 09:27:57 +01:00
using Umbraco.Core.Configuration ;
2016-08-25 15:09:51 +02:00
using Umbraco.Core.Exceptions ;
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
2018-11-19 22:00:50 +11:00
using Umbraco.Core.Logging.Serilog ;
2017-12-26 11:35:21 +01:00
using Umbraco.Core.Migrations.Upgrade ;
2016-09-01 19:06:08 +02:00
using Umbraco.Core.Persistence ;
2016-10-17 15:16:07 +02:00
using Umbraco.Core.Persistence.Mappers ;
2018-10-17 15:09:59 +02:00
using Umbraco.Core.Services.Implement ;
2018-11-26 16:54:32 +01:00
using Umbraco.Core.Sync ;
2016-08-25 15:09:51 +02:00
2017-12-28 09:27:57 +01:00
namespace Umbraco.Core.Runtime
2016-08-25 15:09:51 +02:00
{
/// <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
{
2018-11-26 16:54:32 +01:00
private Components . Components _components ;
2018-11-28 12:59:40 +01:00
private IFactory _factory ;
2016-09-01 19:06:08 +02:00
private RuntimeState _state ;
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
/// <summary>
/// Gets the logger.
/// </summary>
protected ILogger Logger { get ; private set ; }
/// <summary>
/// Gets the profiling logger.
/// </summary>
protected IProfilingLogger ProfilingLogger { get ; private set ; }
2018-11-27 10:37:33 +01:00
/// <inheritdoc />
public IRuntimeState State = > _state ;
2016-08-31 16:48:57 +02:00
/// <inheritdoc/>
2018-11-29 10:35:16 +01:00
public virtual IFactory Boot ( IRegister register )
2016-08-25 15:09:51 +02:00
{
2018-11-26 16:54:32 +01:00
// create and register the essential services
// ie the bare minimum required to boot
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
// loggers
var logger = GetLogger ( ) ;
Logger = logger ;
var profiler = GetProfiler ( ) ;
var profilingLogger = new ProfilingLogger ( logger , profiler ) ;
ProfilingLogger = profilingLogger ;
// application environment
ConfigureUnhandledException ( ) ;
ConfigureAssemblyResolve ( ) ;
ConfigureApplicationRootPath ( ) ;
// application caches
var appCaches = GetAppCaches ( ) ;
var runtimeCache = appCaches . RuntimeCache ;
// database factory
2018-11-27 10:37:33 +01:00
var databaseFactory = GetDatabaseFactory ( ) ;
2018-11-26 16:54:32 +01:00
// type loader
2018-12-12 17:49:24 +01:00
var globalSettings = Current . Config . Global ( ) ;
2018-12-12 14:28:57 +01:00
var localTempStorage = globalSettings . LocalTempStorageLocation ;
var typeLoader = new TypeLoader ( runtimeCache , localTempStorage , profilingLogger ) ;
2018-11-26 16:54:32 +01:00
// runtime state
2018-11-28 12:59:40 +01:00
// beware! must use '() => _factory.GetInstance<T>()' and NOT '_factory.GetInstance<T>'
// as the second one captures the current value (null) and therefore fails
2018-11-26 16:54:32 +01:00
_state = new RuntimeState ( logger ,
2018-12-12 17:49:24 +01:00
Current . Config . Umbraco ( ) , Current . Config . Global ( ) ,
2018-11-28 12:59:40 +01:00
new Lazy < MainDom > ( ( ) = > _factory . GetInstance < MainDom > ( ) ) ,
new Lazy < IServerRegistrar > ( ( ) = > _factory . GetInstance < IServerRegistrar > ( ) ) )
2018-11-26 16:54:32 +01:00
{
Level = RuntimeLevel . Boot
} ;
2018-11-27 13:46:43 +01:00
2018-12-12 17:49:24 +01:00
// main dom
var mainDom = new MainDom ( logger ) ;
2018-11-27 13:46:43 +01:00
// create the composition
2018-12-12 14:28:57 +01:00
var composition = new Composition ( register , typeLoader , profilingLogger , _state ) ;
2018-12-12 17:49:24 +01:00
composition . RegisterEssentials ( logger , profiler , profilingLogger , mainDom , appCaches , databaseFactory , typeLoader , _state ) ;
2018-11-29 10:35:16 +01:00
2018-11-27 10:37:33 +01:00
// register runtime-level services
2018-12-12 14:28:57 +01:00
// there should be none, really - this is here "just in case"
2018-11-26 16:54:32 +01:00
Compose ( composition ) ;
2016-09-01 19:06:08 +02:00
2016-08-25 15:09:51 +02:00
// 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.
2016-09-11 19:57:33 +02:00
using ( var bootTimer = ProfilingLogger . TraceDuration < CoreRuntime > (
$"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()} on {NetworkHelper.MachineName}." ,
"Booted." ,
"Boot failed." ) )
2016-09-01 19:06:08 +02:00
{
2016-09-11 19:57:33 +02:00
try
{
2018-11-26 16:54:32 +01:00
// throws if not full-trust
new AspNetHostingPermission ( AspNetHostingPermissionLevel . Unrestricted ) . Demand ( ) ;
logger . Debug < CoreRuntime > ( "Runtime: {Runtime}" , GetType ( ) . FullName ) ;
2016-09-11 19:57:33 +02:00
2018-12-12 17:49:24 +01:00
AquireMainDom ( mainDom ) ;
2018-11-26 16:54:32 +01:00
DetermineRuntimeLevel ( databaseFactory ) ;
var componentTypes = ResolveComponentTypes ( typeLoader ) ;
_components = new Components . Components ( composition , componentTypes , profilingLogger ) ;
_components . Compose ( ) ;
2018-11-29 10:35:16 +01:00
_factory = Current . Factory = composition . CreateFactory ( ) ;
2018-11-26 16:54:32 +01:00
2018-11-28 12:59:40 +01:00
_components . Initialize ( _factory ) ;
2016-09-11 19:57:33 +02:00
}
catch ( Exception e )
{
_state . Level = RuntimeLevel . BootFailed ;
var bfe = e as BootFailedException ? ? new BootFailedException ( "Boot failed." , e ) ;
2017-05-31 15:25:07 +02:00
_state . BootFailedException = bfe ;
2016-09-11 19:57:33 +02:00
bootTimer . Fail ( exception : bfe ) ; // be sure to log the exception - even if we repeat ourselves
2018-11-29 12:27:16 +01:00
// if something goes wrong above, we may end up with no factory
// meaning nothing can get the runtime state, etc - so let's try
// to make sure we have a factory
if ( _factory = = null )
{
try
{
_factory = Current . Factory = composition . CreateFactory ( ) ;
}
catch { /* yea */ }
}
2016-09-11 19:57:33 +02:00
// throwing here can cause w3wp to hard-crash and we want to avoid it.
// instead, we're logging the exception and setting level to BootFailed.
// various parts of Umbraco such as UmbracoModule and UmbracoDefaultOwinStartup
// understand this and will nullify themselves, while UmbracoModule will
// throw a BootFailedException for every requests.
}
}
2018-11-29 10:35:16 +01:00
return _factory ;
2016-09-11 19:57:33 +02:00
}
2016-09-01 19:06:08 +02:00
2018-11-26 16:54:32 +01:00
protected virtual void ConfigureUnhandledException ( )
2018-11-19 22:02:51 +11:00
{
//take care of unhandled exceptions - there is nothing we can do to
// prevent the launch process to go down but at least we can try
// and log the exception
AppDomain . CurrentDomain . UnhandledException + = ( _ , args ) = >
{
var exception = ( Exception ) args . ExceptionObject ;
var isTerminating = args . IsTerminating ; // always true?
var msg = "Unhandled exception in AppDomain" ;
if ( isTerminating ) msg + = " (terminating)" ;
msg + = "." ;
2018-11-26 16:54:32 +01:00
Logger . Error < CoreRuntime > ( exception , msg ) ;
2018-11-19 22:02:51 +11:00
} ;
}
2018-11-26 16:54:32 +01:00
protected virtual void ConfigureAssemblyResolve ( )
2018-11-19 22:02:51 +11:00
{
// When an assembly can't be resolved. In here we can do magic with the assembly name and try loading another.
// This is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code.
AppDomain . CurrentDomain . AssemblyResolve + = ( sender , args ) = >
{
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
2018-11-26 16:54:32 +01:00
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stack overflow
2018-11-19 22:02:51 +11:00
if ( args . Name . StartsWith ( "AutoMapper" ) & & args . Name . EndsWith ( "PublicKeyToken=null" ) )
return Assembly . Load ( args . Name . Replace ( ", PublicKeyToken=null" , ", PublicKeyToken=be96cd2c38ef1005" ) ) ;
return null ;
} ;
}
2018-11-26 16:54:32 +01:00
protected virtual void ConfigureApplicationRootPath ( )
{
var path = GetApplicationRootPath ( ) ;
if ( string . IsNullOrWhiteSpace ( path ) = = false )
IOHelper . SetRootDirectory ( path ) ;
}
2018-11-19 22:02:51 +11:00
2018-12-12 17:49:24 +01:00
private void AquireMainDom ( MainDom mainDom )
2016-09-11 19:57:33 +02:00
{
2018-11-26 16:54:32 +01:00
using ( var timer = ProfilingLogger . DebugDuration < CoreRuntime > ( "Acquiring MainDom." , "Acquired." ) )
2016-09-11 19:57:33 +02:00
{
try
2016-09-01 19:06:08 +02:00
{
mainDom . Acquire ( ) ;
}
2016-09-11 19:57:33 +02:00
catch
{
timer . Fail ( ) ;
throw ;
}
}
}
2016-09-01 19:06:08 +02:00
2016-10-13 21:08:07 +02:00
// internal for tests
2018-11-26 16:54:32 +01:00
internal void DetermineRuntimeLevel ( IUmbracoDatabaseFactory databaseFactory )
2016-09-11 19:57:33 +02:00
{
using ( var timer = ProfilingLogger . DebugDuration < CoreRuntime > ( "Determining runtime level." , "Determined." ) )
{
try
2016-09-01 19:06:08 +02:00
{
2018-11-26 16:54:32 +01:00
_state . Level = DetermineRuntimeLevel2 ( databaseFactory ) ;
2018-03-30 10:16:21 +02:00
2018-11-26 16:54:32 +01:00
ProfilingLogger . Debug < CoreRuntime > ( "Runtime level: {RuntimeLevel}" , _state . Level ) ;
2018-03-30 10:16:21 +02:00
if ( _state . Level = = RuntimeLevel . Upgrade )
{
2018-11-26 16:54:32 +01:00
ProfilingLogger . Debug < CoreRuntime > ( "Configure database factory for upgrades." ) ;
databaseFactory . ConfigureForUpgrade ( ) ;
2018-03-30 10:16:21 +02:00
}
2016-09-01 19:06:08 +02:00
}
2016-09-11 19:57:33 +02:00
catch
2016-09-01 19:06:08 +02:00
{
2018-11-26 16:54:32 +01:00
_state . Level = RuntimeLevel . BootFailed ;
2016-09-11 19:57:33 +02:00
timer . Fail ( ) ;
throw ;
2016-09-01 19:06:08 +02:00
}
2016-09-11 19:57:33 +02:00
}
}
2016-09-01 19:06:08 +02:00
2018-11-26 16:54:32 +01:00
private IEnumerable < Type > ResolveComponentTypes ( TypeLoader typeLoader )
2016-09-11 19:57:33 +02:00
{
using ( var timer = ProfilingLogger . TraceDuration < CoreRuntime > ( "Resolving component types." , "Resolved." ) )
{
try
{
2018-11-26 16:54:32 +01:00
return GetComponentTypes ( typeLoader ) ;
2016-09-11 19:57:33 +02:00
}
catch
{
timer . Fail ( ) ;
throw ;
}
2016-09-01 19:06:08 +02:00
}
2016-08-25 15:09:51 +02:00
}
2016-09-01 19:06:08 +02:00
/// <inheritdoc/>
2016-08-25 15:09:51 +02:00
public virtual void Terminate ( )
{
2016-09-08 18:43:58 +02:00
using ( ProfilingLogger . DebugDuration < CoreRuntime > ( "Terminating Umbraco." , "Terminated." ) )
{
2018-11-26 16:54:32 +01:00
_components ? . Terminate ( ) ;
2016-09-08 18:43:58 +02:00
}
2016-08-25 15:09:51 +02:00
}
2017-12-26 11:35:21 +01:00
/// <summary>
/// Composes the runtime.
/// </summary>
2018-11-26 16:54:32 +01:00
public virtual void Compose ( Composition composition )
2016-08-25 15:09:51 +02:00
{
2018-11-27 10:37:33 +01:00
// nothing
2016-08-25 15:09:51 +02:00
}
2018-11-26 16:54:32 +01:00
private RuntimeLevel DetermineRuntimeLevel2 ( IUmbracoDatabaseFactory databaseFactory )
2016-09-01 19:06:08 +02:00
{
2018-11-15 08:47:47 +01:00
var localVersion = UmbracoVersion . LocalVersion ; // the local, files, version
2018-03-29 11:31:33 +02:00
var codeVersion = _state . SemanticVersion ; // the executing code version
2016-09-01 19:06:08 +02:00
var connect = false ;
2018-03-27 10:04:07 +02:00
if ( localVersion = = null )
2016-09-01 19:06:08 +02:00
{
// there is no local version, we are not installed
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "No local version, need to install Umbraco." ) ;
return RuntimeLevel . Install ;
2016-09-01 19:06:08 +02:00
}
2018-11-26 16:54:32 +01:00
if ( localVersion < codeVersion )
2016-09-01 19:06:08 +02:00
{
// there *is* a local version, but it does not match the code version
// need to upgrade
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco." , localVersion , codeVersion ) ;
2016-09-01 19:06:08 +02:00
}
2018-06-12 11:03:18 +02:00
else if ( localVersion > codeVersion )
{
2018-11-26 16:54:32 +01:00
Logger . Warn < CoreRuntime > ( "Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported." , localVersion , codeVersion ) ;
2018-06-12 11:03:18 +02:00
// in fact, this is bad enough that we want to throw
throw new BootFailedException ( $"Local version \" { localVersion } \ " > code version \"{codeVersion}\", downgrading is not supported." ) ;
}
2016-09-01 19:06:08 +02:00
else if ( databaseFactory . Configured = = false )
{
// local version *does* match code version, but the database is not configured
// install (again? this is a weird situation...)
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Database is not configured, need to install Umbraco." ) ;
return RuntimeLevel . Install ;
2016-09-01 19:06:08 +02:00
}
2016-09-08 18:43:58 +02:00
// else, keep going,
2016-09-01 19:06:08 +02:00
// anything other than install wants a database - see if we can connect
2017-09-15 18:22:19 +02:00
// (since this is an already existing database, assume localdb is ready)
2016-09-08 18:43:58 +02:00
for ( var i = 0 ; i < 5 ; i + + )
2016-09-01 19:06:08 +02:00
{
2016-09-08 18:43:58 +02:00
connect = databaseFactory . CanConnect ;
if ( connect ) break ;
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Could not immediately connect to database, trying again." ) ;
2016-09-08 18:43:58 +02:00
Thread . Sleep ( 1000 ) ;
}
2016-09-01 19:06:08 +02:00
2016-09-08 18:43:58 +02:00
if ( connect = = false )
{
// cannot connect to configured database, this is bad, fail
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Could not connect to database." ) ;
2016-09-01 19:06:08 +02:00
2016-09-08 18:43:58 +02:00
// in fact, this is bad enough that we want to throw
throw new BootFailedException ( "A connection string is configured but Umbraco could not connect to the database." ) ;
2016-09-01 19:06:08 +02:00
}
2018-10-22 10:34:04 +02:00
// if we already know we want to upgrade,
// still run EnsureUmbracoUpgradeState to get the states
// (v7 will just get a null state, that's ok)
2016-09-01 19:06:08 +02:00
// else
// look for a matching migration entry - bypassing services entirely - they are not 'up' yet
// fixme - in a LB scenario, ensure that the DB gets upgraded only once!
2018-10-17 15:09:59 +02:00
bool noUpgrade ;
2016-09-01 19:06:08 +02:00
try
{
2018-11-26 16:54:32 +01:00
noUpgrade = EnsureUmbracoUpgradeState ( databaseFactory ) ;
2016-09-01 19:06:08 +02:00
}
2018-03-29 11:31:33 +02:00
catch ( Exception e )
2016-09-01 19:06:08 +02:00
{
2018-10-17 15:09:59 +02:00
// can connect to the database but cannot check the upgrade state... oops
2018-11-26 16:54:32 +01:00
Logger . Warn < CoreRuntime > ( e , "Could not check the upgrade state." ) ;
2018-10-17 15:09:59 +02:00
throw new BootFailedException ( "Could not check the upgrade state." , e ) ;
2016-09-01 19:06:08 +02:00
}
2018-10-17 15:09:59 +02:00
if ( noUpgrade )
2016-09-01 19:06:08 +02:00
{
// the database version matches the code & files version, all clear, can run
2018-11-26 16:54:32 +01:00
return RuntimeLevel . Run ;
2016-09-01 19:06:08 +02:00
}
// the db version does not match... but we do have a migration table
// so, at least one valid table, so we quite probably are installed & need to upgrade
// although the files version matches the code version, the database version does not
// which means the local files have been upgraded but not the database - need to upgrade
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Has not reached the final upgrade step, need to upgrade Umbraco." ) ;
return RuntimeLevel . Upgrade ;
2016-09-01 19:06:08 +02:00
}
2018-11-26 16:54:32 +01:00
protected virtual bool EnsureUmbracoUpgradeState ( IUmbracoDatabaseFactory databaseFactory )
2016-09-08 18:43:58 +02:00
{
2018-03-21 11:32:07 +01:00
var umbracoPlan = new UmbracoPlan ( ) ;
var stateValueKey = Upgrader . GetStateValueKey ( umbracoPlan ) ;
2018-10-17 15:09:59 +02:00
// no scope, no service - just directly accessing the database
2017-12-26 11:35:21 +01:00
using ( var database = databaseFactory . CreateDatabase ( ) )
2016-12-14 19:21:50 +01:00
{
2018-10-17 15:09:59 +02:00
_state . CurrentMigrationState = KeyValueService . GetValue ( database , stateValueKey ) ;
_state . FinalMigrationState = umbracoPlan . FinalState ;
2016-12-14 19:21:50 +01:00
}
2017-12-26 11:35:21 +01:00
2018-11-26 16:54:32 +01:00
Logger . Debug < CoreRuntime > ( "Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}" , _state . FinalMigrationState , _state . CurrentMigrationState ? ? "<null>" ) ;
2017-12-26 11:35:21 +01:00
2018-10-17 15:09:59 +02:00
return _state . CurrentMigrationState = = _state . FinalMigrationState ;
2016-09-08 18:43:58 +02:00
}
2018-11-26 16:54:32 +01:00
#region Getters
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
// getters can be implemented by runtimes inheriting from CoreRuntime
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
/// <summary>
/// Gets all component types.
/// </summary>
protected virtual IEnumerable < Type > GetComponentTypes ( TypeLoader typeLoader )
= > typeLoader . GetTypes < IUmbracoComponent > ( ) ;
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
/// <summary>
/// Gets a logger.
/// </summary>
protected virtual ILogger GetLogger ( )
= > SerilogLogger . CreateWithDefaultConfiguration ( ) ;
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
/// <summary>
/// Gets a profiler.
/// </summary>
protected virtual IProfiler GetProfiler ( )
= > new LogProfiler ( ProfilingLogger ) ;
2016-08-25 15:09:51 +02:00
2018-11-26 16:54:32 +01:00
/// <summary>
/// Gets the application caches.
/// </summary>
protected virtual CacheHelper GetAppCaches ( )
{
// need the deep clone runtime cache provider to ensure entities are cached properly, ie
// are cloned in and cloned out - no request-based cache here since no web-based context,
// is overriden by the web runtime
2016-08-31 16:48:57 +02:00
2018-11-26 16:54:32 +01:00
return new CacheHelper (
new DeepCloneRuntimeCacheProvider ( new ObjectCacheRuntimeCacheProvider ( ) ) ,
new StaticCacheProvider ( ) ,
NullCacheProvider . Instance ,
new IsolatedRuntimeCache ( type = > new DeepCloneRuntimeCacheProvider ( new ObjectCacheRuntimeCacheProvider ( ) ) ) ) ;
}
2016-08-25 15:09:51 +02:00
2016-09-08 18:43:58 +02:00
// by default, returns null, meaning that Umbraco should auto-detect the application root path.
// override and return the absolute path to the Umbraco site/solution, if needed
2018-11-26 16:54:32 +01:00
protected virtual string GetApplicationRootPath ( )
= > null ;
2016-08-25 15:09:51 +02:00
2018-11-27 10:37:33 +01:00
/// <summary>
/// Gets the database factory.
/// </summary>
/// <remarks>This is strictly internal, for tests only.</remarks>
protected internal virtual IUmbracoDatabaseFactory GetDatabaseFactory ( )
2018-11-28 12:59:40 +01:00
= > new UmbracoDatabaseFactory ( Logger , new Lazy < IMapperCollection > ( ( ) = > _factory . GetInstance < IMapperCollection > ( ) ) ) ;
2018-11-27 10:37:33 +01:00
2016-08-25 15:09:51 +02:00
#endregion
}
}