2012-08-01 22:06:15 +06:00
|
|
|
|
using System;
|
2013-10-02 11:07:04 +02:00
|
|
|
|
using System.Linq;
|
2013-01-16 13:31:04 -01:00
|
|
|
|
using System.Threading;
|
2013-10-02 11:07:04 +02:00
|
|
|
|
using Umbraco.Core.Logging;
|
2012-08-01 22:06:15 +06:00
|
|
|
|
|
2012-08-10 13:18:13 +06:00
|
|
|
|
namespace Umbraco.Core.ObjectResolution
|
2012-08-01 22:06:15 +06:00
|
|
|
|
{
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represents the status of objects resolution.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
2013-01-16 13:31:04 -01:00
|
|
|
|
/// <para>Before resolution is frozen it is possible to access its configuration, but not to get values.</para>
|
|
|
|
|
|
/// <para>Once resolution is frozen, it is not possible to access its configuration anymore, but it is possible to get values.</para>
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// </remarks>
|
2013-01-16 13:31:04 -01:00
|
|
|
|
internal static class Resolution
|
2012-08-01 22:06:15 +06:00
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
private static readonly ReaderWriterLockSlim ConfigurationLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
|
|
|
|
|
private volatile static bool _isFrozen;
|
2013-01-16 13:10:34 -01:00
|
|
|
|
|
2013-10-02 11:07:04 +02:00
|
|
|
|
/// <summary>
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// Occurs when resolution is frozen.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>Occurs only once, since resolution can be frozen only once.</remarks>
|
2012-08-01 22:06:15 +06:00
|
|
|
|
public static event EventHandler Frozen;
|
|
|
|
|
|
|
2012-08-01 22:46:13 +06:00
|
|
|
|
/// <summary>
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// Gets or sets a value indicating whether resolution of objects is frozen.
|
2012-08-01 22:46:13 +06:00
|
|
|
|
/// </summary>
|
2013-10-02 11:07:04 +02:00
|
|
|
|
// internal for unit tests, use ReadFrozen if you want to be sure
|
|
|
|
|
|
internal static bool IsFrozen
|
2013-01-16 13:31:04 -01:00
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
using (new ReadLock(ConfigurationLock))
|
|
|
|
|
|
{
|
|
|
|
|
|
return _isFrozen;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-01-16 13:31:04 -01:00
|
|
|
|
}
|
2012-08-01 22:06:15 +06:00
|
|
|
|
|
2013-10-02 11:07:04 +02:00
|
|
|
|
public static IDisposable Reader(bool canReadUnfrozen = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
IDisposable l = new ReadLock(ConfigurationLock);
|
|
|
|
|
|
if (canReadUnfrozen || _isFrozen) return l;
|
|
|
|
|
|
|
|
|
|
|
|
l.Dispose();
|
|
|
|
|
|
throw new InvalidOperationException("Resolution is not frozen, it is not yet possible to get values from it.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// <summary>
|
2013-01-16 13:31:04 -01:00
|
|
|
|
/// Returns a disposable object that represents safe access to unfrozen resolution configuration.
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// </summary>
|
2013-01-16 13:31:04 -01:00
|
|
|
|
/// <remarks>Should be used in a <c>using(Resolution.Configuration) { ... }</c> mode.</remarks>
|
|
|
|
|
|
public static IDisposable Configuration
|
2012-08-01 22:06:15 +06:00
|
|
|
|
{
|
2013-01-16 13:31:04 -01:00
|
|
|
|
get
|
|
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
IDisposable l = new WriteLock(ConfigurationLock);
|
|
|
|
|
|
if (_isFrozen == false) return l;
|
|
|
|
|
|
|
|
|
|
|
|
l.Dispose();
|
|
|
|
|
|
throw new InvalidOperationException("Resolution is frozen, it is not possible to configure it anymore.");
|
2013-01-16 13:31:04 -01:00
|
|
|
|
}
|
2012-08-01 22:06:15 +06:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-16 16:48:31 +02:00
|
|
|
|
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
|
|
|
|
|
|
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
|
|
|
|
|
|
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
|
|
|
|
|
|
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
|
|
|
|
|
|
|
2013-01-23 14:08:13 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// <para>Should not be used.</para>
|
|
|
|
|
|
/// <para>Should be used in a <c>using(Resolution.DirtyBackdoorToConfiguration) { ... }</c> mode.</para>
|
|
|
|
|
|
/// <para>Because we just lift the frozen state, and we don't actually re-freeze, the <c>Frozen</c> event does not trigger.</para>
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
internal static IDisposable DirtyBackdoorToConfiguration
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return new DirtyBackdoor(); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// keep the class here because it needs write-access to Resolution.IsFrozen
|
|
|
|
|
|
private class DirtyBackdoor : IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2013-10-02 11:07:04 +02:00
|
|
|
|
private readonly IDisposable _lock;
|
|
|
|
|
|
private readonly bool _frozen;
|
2013-01-23 14:08:13 -01:00
|
|
|
|
|
|
|
|
|
|
public DirtyBackdoor()
|
|
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
LogHelper.Debug(typeof(DirtyBackdoor), "Creating back door for resolution");
|
|
|
|
|
|
|
|
|
|
|
|
_lock = new WriteLock(ConfigurationLock);
|
|
|
|
|
|
_frozen = _isFrozen;
|
|
|
|
|
|
_isFrozen = false;
|
2013-01-23 14:08:13 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
LogHelper.Debug(typeof(DirtyBackdoor), "Disposing back door for resolution");
|
|
|
|
|
|
|
|
|
|
|
|
_isFrozen = _frozen;
|
2013-01-23 14:08:13 -01:00
|
|
|
|
_lock.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-01-16 13:10:34 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Freezes resolution.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <exception cref="InvalidOperationException">resolution is already frozen.</exception>
|
2012-08-01 22:06:15 +06:00
|
|
|
|
public static void Freeze()
|
|
|
|
|
|
{
|
2015-07-08 16:28:35 +02:00
|
|
|
|
LogHelper.Debug(typeof (Resolution), "Freezing resolution");
|
2012-08-01 22:06:15 +06:00
|
|
|
|
|
2013-10-02 11:07:04 +02:00
|
|
|
|
using (new WriteLock(ConfigurationLock))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isFrozen)
|
|
|
|
|
|
throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again.");
|
|
|
|
|
|
|
|
|
|
|
|
_isFrozen = true;
|
|
|
|
|
|
}
|
2015-07-08 16:28:35 +02:00
|
|
|
|
|
|
|
|
|
|
LogHelper.Debug(typeof(Resolution), "Resolution is frozen");
|
|
|
|
|
|
|
|
|
|
|
|
if (Frozen == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Frozen(null, null);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.Error(typeof (Resolution), "Exception in Frozen event handler.", e);
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
2012-08-01 22:06:15 +06:00
|
|
|
|
}
|
2013-01-23 09:41:01 -01:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Resets resolution, ie unfreezes it and clears Frozen event.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>To be used in unit tests.</remarks>
|
|
|
|
|
|
internal static void Reset()
|
|
|
|
|
|
{
|
2013-10-02 11:07:04 +02:00
|
|
|
|
LogHelper.Debug(typeof(Resolution), "Resetting resolution");
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
var trace = new System.Diagnostics.StackTrace();
|
|
|
|
|
|
var testing = trace.GetFrames().Any(frame =>
|
|
|
|
|
|
frame.GetMethod().DeclaringType.FullName.StartsWith("Umbraco.Tests"));
|
|
|
|
|
|
if (testing == false)
|
|
|
|
|
|
throw new InvalidOperationException("Only unit tests can reset configuration.");
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
using (new WriteLock(ConfigurationLock))
|
|
|
|
|
|
{
|
|
|
|
|
|
_isFrozen = false;
|
|
|
|
|
|
}
|
2013-01-23 09:41:01 -01:00
|
|
|
|
Frozen = null;
|
|
|
|
|
|
}
|
2012-08-01 22:06:15 +06:00
|
|
|
|
}
|
|
|
|
|
|
}
|