2012-07-20 01:04:35 +06:00
|
|
|
|
using System;
|
2012-07-26 21:12:54 +06:00
|
|
|
|
using System.Configuration;
|
2013-03-12 01:50:56 +04:00
|
|
|
|
using System.Threading;
|
2012-10-31 11:36:22 +06:00
|
|
|
|
using System.Web;
|
|
|
|
|
|
using System.Web.Caching;
|
2013-03-23 04:01:52 +06:00
|
|
|
|
using Umbraco.Core.Cache;
|
2012-08-01 22:06:15 +06:00
|
|
|
|
using Umbraco.Core.Configuration;
|
2013-09-13 14:06:36 +10:00
|
|
|
|
using Umbraco.Core.Configuration.UmbracoSettings;
|
2012-08-01 22:06:15 +06:00
|
|
|
|
using Umbraco.Core.Logging;
|
2013-03-12 01:50:56 +04:00
|
|
|
|
using Umbraco.Core.ObjectResolution;
|
2012-11-12 07:40:11 -01:00
|
|
|
|
using Umbraco.Core.Services;
|
2015-07-08 17:32:36 +02:00
|
|
|
|
using Umbraco.Core.Sync;
|
2012-07-26 21:12:54 +06:00
|
|
|
|
|
2012-07-20 01:04:35 +06:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core
|
2012-07-28 23:08:02 +06:00
|
|
|
|
{
|
2012-07-26 21:12:54 +06:00
|
|
|
|
/// <summary>
|
2012-07-20 01:04:35 +06:00
|
|
|
|
/// the Umbraco Application context
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// one per AppDomain, represents the global Umbraco application
|
|
|
|
|
|
/// </remarks>
|
2013-03-12 01:50:56 +04:00
|
|
|
|
public class ApplicationContext : IDisposable
|
2012-07-20 01:04:35 +06:00
|
|
|
|
{
|
2013-07-02 17:47:20 +10:00
|
|
|
|
|
2013-03-23 04:01:52 +06:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Constructor
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dbContext"></param>
|
|
|
|
|
|
/// <param name="serviceContext"></param>
|
2013-07-02 17:47:20 +10:00
|
|
|
|
/// <param name="cache"></param>
|
2013-10-09 13:41:14 +11:00
|
|
|
|
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
|
2013-03-23 04:01:52 +06:00
|
|
|
|
{
|
|
|
|
|
|
if (dbContext == null) throw new ArgumentNullException("dbContext");
|
|
|
|
|
|
if (serviceContext == null) throw new ArgumentNullException("serviceContext");
|
2013-07-02 17:47:20 +10:00
|
|
|
|
if (cache == null) throw new ArgumentNullException("cache");
|
2013-03-23 04:01:52 +06:00
|
|
|
|
_databaseContext = dbContext;
|
2013-07-02 17:47:20 +10:00
|
|
|
|
_services = serviceContext;
|
|
|
|
|
|
ApplicationCache = cache;
|
2015-07-09 14:30:32 +02:00
|
|
|
|
|
|
|
|
|
|
Init();
|
2013-03-23 04:01:52 +06:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-08-09 11:42:20 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates a basic app context
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="cache"></param>
|
2013-10-09 13:41:14 +11:00
|
|
|
|
public ApplicationContext(CacheHelper cache)
|
2013-08-09 11:42:20 +10:00
|
|
|
|
{
|
|
|
|
|
|
ApplicationCache = cache;
|
2015-07-09 14:30:32 +02:00
|
|
|
|
|
|
|
|
|
|
Init();
|
2013-08-09 11:42:20 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-09 13:17:19 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A method used to set and/or ensure that a global ApplicationContext singleton is created.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="appContext">
|
|
|
|
|
|
/// The instance to set on the global application singleton
|
|
|
|
|
|
/// </param>
|
|
|
|
|
|
/// <param name="replaceContext">If set to true and the singleton is already set, it will be replaced</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// This is NOT thread safe
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ApplicationContext.Current != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!replaceContext)
|
|
|
|
|
|
return ApplicationContext.Current;
|
|
|
|
|
|
}
|
|
|
|
|
|
ApplicationContext.Current = appContext;
|
|
|
|
|
|
return ApplicationContext.Current;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A method used to create and ensure that a global ApplicationContext singleton is created.
|
|
|
|
|
|
/// </summary>
|
2013-10-09 13:52:24 +11:00
|
|
|
|
/// <param name="cache"></param>
|
2013-10-09 13:17:19 +11:00
|
|
|
|
/// <param name="replaceContext">
|
|
|
|
|
|
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
|
|
|
|
|
/// startup if for some reason the boot manager is not the umbraco boot manager.
|
|
|
|
|
|
/// </param>
|
|
|
|
|
|
/// <param name="dbContext"></param>
|
|
|
|
|
|
/// <param name="serviceContext"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// This is NOT thread safe
|
|
|
|
|
|
/// </remarks>
|
2013-10-09 13:52:24 +11:00
|
|
|
|
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
|
2013-10-09 13:17:19 +11:00
|
|
|
|
{
|
|
|
|
|
|
if (ApplicationContext.Current != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!replaceContext)
|
|
|
|
|
|
return ApplicationContext.Current;
|
|
|
|
|
|
}
|
2013-10-09 13:52:24 +11:00
|
|
|
|
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
|
2013-10-09 13:17:19 +11:00
|
|
|
|
ApplicationContext.Current = ctx;
|
|
|
|
|
|
return ApplicationContext.Current;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2012-07-20 22:02:28 +06:00
|
|
|
|
/// Singleton accessor
|
|
|
|
|
|
/// </summary>
|
2012-07-20 23:42:55 +06:00
|
|
|
|
public static ApplicationContext Current { get; internal set; }
|
2012-07-20 01:04:35 +06:00
|
|
|
|
|
2012-10-31 11:36:22 +06:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns the application wide cache accessor
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Any caching that is done in the application (app wide) should be done through this property
|
|
|
|
|
|
/// </remarks>
|
2012-11-19 15:31:58 -01:00
|
|
|
|
public CacheHelper ApplicationCache { get; private set; }
|
2012-10-31 11:36:22 +06:00
|
|
|
|
|
2012-07-20 22:02:28 +06:00
|
|
|
|
// IsReady is set to true by the boot manager once it has successfully booted
|
2012-07-20 01:04:35 +06:00
|
|
|
|
// note - the original umbraco module checks on content.Instance in umbraco.dll
|
|
|
|
|
|
// now, the boot task that setup the content store ensures that it is ready
|
|
|
|
|
|
bool _isReady = false;
|
2013-09-25 19:23:41 +10:00
|
|
|
|
readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false);
|
2012-12-14 08:06:32 +05:00
|
|
|
|
private DatabaseContext _databaseContext;
|
|
|
|
|
|
private ServiceContext _services;
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsReady
|
2012-07-20 01:04:35 +06:00
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _isReady;
|
|
|
|
|
|
}
|
|
|
|
|
|
internal set
|
|
|
|
|
|
{
|
|
|
|
|
|
AssertIsNotReady();
|
|
|
|
|
|
_isReady = value;
|
2012-09-28 11:11:47 -02:00
|
|
|
|
_isReadyEvent.Set();
|
2012-07-20 01:04:35 +06:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-09-28 11:11:47 -02:00
|
|
|
|
public bool WaitForReady(int timeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _isReadyEvent.WaitHandle.WaitOne(timeout);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 01:04:35 +06:00
|
|
|
|
|
|
|
|
|
|
// notes
|
|
|
|
|
|
// GlobalSettings.ConfigurationStatus returns the value that's in the web.config, so it's the "configured version"
|
|
|
|
|
|
// GlobalSettings.CurrentVersion returns the hard-coded "current version"
|
|
|
|
|
|
// the system is configured if they match
|
|
|
|
|
|
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
|
2015-07-09 14:30:32 +02:00
|
|
|
|
|
2012-07-20 01:04:35 +06:00
|
|
|
|
public bool IsConfigured
|
|
|
|
|
|
{
|
2015-07-09 14:30:32 +02:00
|
|
|
|
get { return _configured.Value; }
|
2012-07-20 01:04:35 +06:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-09-13 14:06:36 +10:00
|
|
|
|
/// <summary>
|
2015-07-08 17:32:36 +02:00
|
|
|
|
/// The application url.
|
2013-04-05 23:28:43 +06:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
2015-07-08 17:32:36 +02:00
|
|
|
|
/// The application url is the url that should be used by services to talk to the application,
|
|
|
|
|
|
/// eg keep alive or scheduled publishing services. It must exist on a global context because
|
|
|
|
|
|
/// some of these services may not run within a web context.
|
|
|
|
|
|
/// The format of the application url is:
|
|
|
|
|
|
/// - has a scheme (http or https)
|
|
|
|
|
|
/// - has the SystemDirectories.Umbraco path
|
|
|
|
|
|
/// - does not end with a slash
|
|
|
|
|
|
/// It is initialized on the first request made to the server, by UmbracoModule.EnsureApplicationUrl:
|
|
|
|
|
|
/// - if umbracoSettings:settings/web.routing/@appUrl is set, use the value (new setting)
|
|
|
|
|
|
/// - if umbracoSettings:settings/scheduledTasks/@baseUrl is set, use the value (backward compatibility)
|
|
|
|
|
|
/// - otherwise, use the url of the (first) request.
|
|
|
|
|
|
/// Not locking, does not matter if several threads write to this.
|
|
|
|
|
|
/// See also issues:
|
|
|
|
|
|
/// - http://issues.umbraco.org/issue/U4-2059
|
|
|
|
|
|
/// - http://issues.umbraco.org/issue/U4-6788
|
|
|
|
|
|
/// - http://issues.umbraco.org/issue/U4-5728
|
|
|
|
|
|
/// - http://issues.umbraco.org/issue/U4-5391
|
2013-04-05 23:28:43 +06:00
|
|
|
|
/// </remarks>
|
2015-07-08 17:32:36 +02:00
|
|
|
|
internal string UmbracoApplicationUrl
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
// if initialized, return
|
|
|
|
|
|
if (_umbracoApplicationUrl != null) return _umbracoApplicationUrl;
|
|
|
|
|
|
|
|
|
|
|
|
// try settings
|
|
|
|
|
|
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(this, UmbracoConfig.For.UmbracoSettings());
|
|
|
|
|
|
|
|
|
|
|
|
// and return what we have, may be null
|
|
|
|
|
|
return _umbracoApplicationUrl;
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
_umbracoApplicationUrl = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-04-05 23:28:43 +06:00
|
|
|
|
|
2015-07-08 17:32:36 +02:00
|
|
|
|
internal string _umbracoApplicationUrl; // internal for tests
|
2015-05-15 22:20:09 +02:00
|
|
|
|
|
2015-07-09 14:30:32 +02:00
|
|
|
|
private Lazy<bool> _configured;
|
|
|
|
|
|
|
|
|
|
|
|
private void Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
//Create the lazy value to resolve whether or not the application is 'configured'
|
|
|
|
|
|
_configured = new Lazy<bool>(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var configStatus = ConfigurationStatus;
|
|
|
|
|
|
var currentVersion = UmbracoVersion.Current.ToString(3);
|
|
|
|
|
|
var ok = configStatus == currentVersion;
|
|
|
|
|
|
if (ok == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.Debug<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
|
|
|
|
|
|
}
|
|
|
|
|
|
return ok;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2012-11-19 15:31:58 -01:00
|
|
|
|
|
2012-07-26 21:12:54 +06:00
|
|
|
|
private string ConfigurationStatus
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return ConfigurationManager.AppSettings["umbracoConfigurationStatus"];
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return String.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 01:04:35 +06:00
|
|
|
|
private void AssertIsNotReady()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (this.IsReady)
|
|
|
|
|
|
throw new Exception("ApplicationContext has already been initialized.");
|
|
|
|
|
|
}
|
2012-11-06 10:47:14 -01:00
|
|
|
|
|
2012-12-14 08:06:32 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the current DatabaseContext
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Internal set is generally only used for unit tests
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
public DatabaseContext DatabaseContext
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_databaseContext == null)
|
|
|
|
|
|
throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext");
|
|
|
|
|
|
return _databaseContext;
|
|
|
|
|
|
}
|
|
|
|
|
|
internal set { _databaseContext = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the current ServiceContext
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Internal set is generally only used for unit tests
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
public ServiceContext Services
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_services == null)
|
|
|
|
|
|
throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext");
|
|
|
|
|
|
return _services;
|
|
|
|
|
|
}
|
|
|
|
|
|
internal set { _services = value; }
|
|
|
|
|
|
}
|
2013-03-12 01:50:56 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private volatile bool _disposed;
|
|
|
|
|
|
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This will dispose and reset all resources used to run the application
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// IMPORTANT: Never dispose this object if you require the Umbraco application to run, disposing this object
|
|
|
|
|
|
/// is generally used for unit testing and when your application is shutting down after you have booted Umbraco.
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
void IDisposable.Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Only operate if we haven't already disposed
|
|
|
|
|
|
if (_disposed) return;
|
|
|
|
|
|
|
|
|
|
|
|
using (new WriteLock(_disposalLocker))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check again now we're inside the lock
|
|
|
|
|
|
if (_disposed) return;
|
|
|
|
|
|
|
2013-03-12 22:58:21 +04:00
|
|
|
|
//clear the cache
|
2013-03-14 01:07:54 +04:00
|
|
|
|
if (ApplicationCache != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplicationCache.ClearAllCache();
|
|
|
|
|
|
}
|
2013-03-12 22:58:21 +04:00
|
|
|
|
//reset all resolvers
|
2013-03-12 01:50:56 +04:00
|
|
|
|
ResolverCollection.ResetAll();
|
2013-03-12 22:58:21 +04:00
|
|
|
|
//reset resolution itself (though this should be taken care of by resetting any of the resolvers above)
|
2013-03-12 01:50:56 +04:00
|
|
|
|
Resolution.Reset();
|
2013-03-13 18:31:07 +04:00
|
|
|
|
|
2013-03-12 22:58:21 +04:00
|
|
|
|
//reset the instance objects
|
|
|
|
|
|
this.ApplicationCache = null;
|
2013-03-14 01:07:54 +04:00
|
|
|
|
if (_databaseContext != null) //need to check the internal field here
|
2013-03-13 18:31:07 +04:00
|
|
|
|
{
|
2013-04-15 21:36:24 +06:00
|
|
|
|
if (DatabaseContext.IsDatabaseConfigured)
|
2013-03-14 01:07:54 +04:00
|
|
|
|
{
|
|
|
|
|
|
DatabaseContext.Database.Dispose();
|
|
|
|
|
|
}
|
2013-03-13 18:31:07 +04:00
|
|
|
|
}
|
2013-03-12 22:58:21 +04:00
|
|
|
|
this.DatabaseContext = null;
|
|
|
|
|
|
this.Services = null;
|
2013-03-13 18:31:07 +04:00
|
|
|
|
this._isReady = false; //set the internal field
|
2013-03-12 01:50:56 +04:00
|
|
|
|
|
|
|
|
|
|
// Indicate that the instance has been disposed.
|
|
|
|
|
|
_disposed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-07-20 01:04:35 +06:00
|
|
|
|
}
|
|
|
|
|
|
}
|