Fixed some med trust issues with UmbracoExamine. Created new framework for distributed cache providers with unit tests. Still uses the
old ICacheRefresher but now we can plugin 2 new providers - one for resolving a list of servers and the other to notify the servers of changes. By default we have the configuration based providers which uses the umbracoSettings.
This commit is contained in:
@@ -166,9 +166,15 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
protected virtual void InitializeResolvers()
|
||||
{
|
||||
|
||||
//by default we'll use the standard configuration based sync
|
||||
ServerRegistrarResolver.Current = new ServerRegistrarResolver(
|
||||
new ConfigServerRegistrar()); //by default we'll use the standard configuration based sync
|
||||
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());
|
||||
|
||||
RepositoryResolver.Current = new RepositoryResolver(
|
||||
new RepositoryFactory());
|
||||
|
||||
47
src/Umbraco.Core/Sync/ConfigServerRegistrar.cs
Normal file
47
src/Umbraco.Core/Sync/ConfigServerRegistrar.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A registrar that uses the legacy xml configuration in umbracoSettings to get a list of defined server nodes
|
||||
/// </summary>
|
||||
internal class ConfigServerRegistrar : IServerRegistrar
|
||||
{
|
||||
private readonly XmlNode _xmlServers;
|
||||
|
||||
public ConfigServerRegistrar()
|
||||
: this(UmbracoSettings.DistributionServers)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal ConfigServerRegistrar(XmlNode xmlServers)
|
||||
{
|
||||
_xmlServers = xmlServers;
|
||||
}
|
||||
|
||||
private List<IServerRegistration> _addresses;
|
||||
|
||||
public IEnumerable<IServerRegistration> Registrations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_addresses == null)
|
||||
{
|
||||
_addresses = new List<IServerRegistration>();
|
||||
var nodes = _xmlServers.SelectNodes("./server");
|
||||
foreach (XmlNode n in nodes)
|
||||
{
|
||||
_addresses.Add(new ConfigServerRegistration(n));
|
||||
}
|
||||
}
|
||||
return _addresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/Umbraco.Core/Sync/ConfigServerRegistration.cs
Normal file
29
src/Umbraco.Core/Sync/ConfigServerRegistration.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A server registration based on the legacy umbraco xml configuration in umbracoSettings
|
||||
/// </summary>
|
||||
internal class ConfigServerRegistration : IServerRegistration
|
||||
{
|
||||
|
||||
public ConfigServerRegistration(XmlNode n)
|
||||
{
|
||||
var webServicesUrl = IOHelper.ResolveUrl(SystemDirectories.WebServices);
|
||||
|
||||
var protocol = GlobalSettings.UseSSL ? "https" : "http";
|
||||
if (n.Attributes.GetNamedItem("forceProtocol") != null && !string.IsNullOrEmpty(n.Attributes.GetNamedItem("forceProtocol").Value))
|
||||
protocol = n.Attributes.GetNamedItem("forceProtocol").Value;
|
||||
var domain = XmlHelper.GetNodeValue(n);
|
||||
if (n.Attributes.GetNamedItem("forcePortnumber") != null && !string.IsNullOrEmpty(n.Attributes.GetNamedItem("forcePortnumber").Value))
|
||||
domain += string.Format(":{0}", n.Attributes.GetNamedItem("forcePortnumber").Value);
|
||||
ServerAddress = string.Format("{0}://{1}{2}/cacheRefresher.asmx", protocol, domain, webServicesUrl);
|
||||
}
|
||||
|
||||
public string ServerAddress { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
281
src/Umbraco.Core/Sync/DefaultServerMessenger.cs
Normal file
281
src/Umbraco.Core/Sync/DefaultServerMessenger.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// The default server messenger that uses web services to keep servers in sync
|
||||
/// </summary>
|
||||
internal class DefaultServerMessenger : IServerMessenger
|
||||
{
|
||||
private readonly string _login;
|
||||
private readonly string _password;
|
||||
private readonly bool _useDistributedCalls;
|
||||
|
||||
/// <summary>
|
||||
/// Without a username/password all distribuion will be disabled
|
||||
/// </summary>
|
||||
internal DefaultServerMessenger()
|
||||
{
|
||||
_useDistributedCalls = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distribution will be enabled based on the umbraco config setting.
|
||||
/// </summary>
|
||||
/// <param name="login"></param>
|
||||
/// <param name="password"></param>
|
||||
internal DefaultServerMessenger(string login, string password)
|
||||
{
|
||||
_useDistributedCalls = UmbracoSettings.UseDistributedCalls;
|
||||
_login = login;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
public void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher,Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRemove<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRemove(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
|
||||
numericIds.ForEach(x => InvokeDispatchMethod(servers, refresher, MessageType.RemoveById, x));
|
||||
}
|
||||
|
||||
public void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
|
||||
numericIds.ForEach(x => InvokeDispatchMethod(servers, refresher, MessageType.RefreshById, x));
|
||||
}
|
||||
|
||||
public void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params Guid[] guidIds)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
|
||||
guidIds.ForEach(x => InvokeDispatchMethod(servers, refresher, MessageType.RefreshById, x));
|
||||
|
||||
}
|
||||
|
||||
public void PerformRefreshAll(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher)
|
||||
{
|
||||
InvokeDispatchMethod(servers, refresher, MessageType.RefreshAll, null);
|
||||
}
|
||||
|
||||
private void InvokeMethodOnRefresherInstance(ICacheRefresher refresher, MessageType dispatchType, object id)
|
||||
{
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
switch (dispatchType)
|
||||
{
|
||||
case MessageType.RefreshAll:
|
||||
refresher.RefreshAll();
|
||||
break;
|
||||
case MessageType.RefreshById:
|
||||
if (id is int)
|
||||
{
|
||||
refresher.Refresh((int)id);
|
||||
}
|
||||
else if (id is Guid)
|
||||
{
|
||||
refresher.Refresh((Guid) id);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("The id must be either an int or a Guid");
|
||||
}
|
||||
|
||||
break;
|
||||
case MessageType.RemoveById:
|
||||
refresher.Remove((int)id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void InvokeDispatchMethod(
|
||||
IEnumerable<IServerRegistration> servers,
|
||||
ICacheRefresher refresher,
|
||||
MessageType dispatchType,
|
||||
object id)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
|
||||
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
|
||||
// can definitely not distribute.
|
||||
if (!_useDistributedCalls || !servers.Any())
|
||||
{
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
InvokeMethodOnRefresherInstance(refresher, dispatchType, id);
|
||||
return;
|
||||
}
|
||||
|
||||
//We are using distributed calls, so lets make them...
|
||||
try
|
||||
{
|
||||
using (var cacheRefresher = new ServerSyncWebServiceClient())
|
||||
{
|
||||
var asyncResultsList = new List<IAsyncResult>();
|
||||
|
||||
LogStartDispatch();
|
||||
|
||||
var nodes = servers;
|
||||
// Go through each configured node submitting a request asynchronously
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
//set the server address
|
||||
cacheRefresher.Url = n.ServerAddress;
|
||||
|
||||
// Add the returned WaitHandle to the list for later checking
|
||||
switch (dispatchType)
|
||||
{
|
||||
case MessageType.RefreshAll:
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginRefreshAll(
|
||||
refresher.UniqueIdentifier, _login, _password, null, null));
|
||||
break;
|
||||
case MessageType.RefreshById:
|
||||
IAsyncResult result;
|
||||
if (id is int)
|
||||
{
|
||||
result = cacheRefresher.BeginRefreshById(refresher.UniqueIdentifier, (int) id, _login, _password, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = cacheRefresher.BeginRefreshByGuid(refresher.UniqueIdentifier, (Guid)id, _login, _password, null, null);
|
||||
}
|
||||
asyncResultsList.Add(result);
|
||||
break;
|
||||
case MessageType.RemoveById:
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginRemoveById(
|
||||
refresher.UniqueIdentifier, (int)id, _login, _password, null, null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<WaitHandle> waitHandlesList;
|
||||
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
|
||||
|
||||
var errorCount = 0;
|
||||
|
||||
// Once for each WaitHandle that we have, wait for a response and log it
|
||||
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
|
||||
foreach (var t in asyncResults)
|
||||
{
|
||||
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
|
||||
|
||||
try
|
||||
{
|
||||
// Find out if the call succeeded
|
||||
switch (dispatchType)
|
||||
{
|
||||
case MessageType.RefreshAll:
|
||||
cacheRefresher.EndRefreshAll(t);
|
||||
break;
|
||||
case MessageType.RefreshById:
|
||||
if (id is int)
|
||||
{
|
||||
cacheRefresher.EndRefreshById(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheRefresher.EndRefreshByGuid(t);
|
||||
}
|
||||
break;
|
||||
case MessageType.RemoveById:
|
||||
cacheRefresher.EndRemoveById(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
|
||||
errorCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogDispatchBatchResult(errorCount);
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogDispatchBatchError(ee);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
|
||||
{
|
||||
var asyncResults = asyncResultsList.ToArray();
|
||||
waitHandlesList = new List<WaitHandle>();
|
||||
foreach (var asyncResult in asyncResults)
|
||||
{
|
||||
waitHandlesList.Add(asyncResult.AsyncWaitHandle);
|
||||
}
|
||||
return asyncResults;
|
||||
}
|
||||
|
||||
private void LogDispatchBatchError(Exception ee)
|
||||
{
|
||||
LogHelper.Error<DefaultServerMessenger>("Error refreshing distributed list", ee);
|
||||
}
|
||||
|
||||
private void LogDispatchBatchResult(int errorCount)
|
||||
{
|
||||
LogHelper.Debug<DefaultServerMessenger>(string.Format("Distributed server push completed with {0} nodes reporting an error", errorCount == 0 ? "no" : errorCount.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(Exception ex)
|
||||
{
|
||||
LogHelper.Error<DefaultServerMessenger>("Error refreshing a node in the distributed list", ex);
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(WebException ex)
|
||||
{
|
||||
string url = (ex.Response != null) ? ex.Response.ResponseUri.ToString() : "invalid url (responseUri null)";
|
||||
LogHelper.Error<DefaultServerMessenger>("Error refreshing a node in the distributed list, URI attempted: " + url, ex);
|
||||
}
|
||||
|
||||
private void LogStartDispatch()
|
||||
{
|
||||
LogHelper.Info<DefaultServerMessenger>("Submitting calls to distributed servers");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
74
src/Umbraco.Core/Sync/IServerMessenger.cs
Normal file
74
src/Umbraco.Core/Sync/IServerMessenger.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a server messenger for server sync and distrubuted cache
|
||||
/// </summary>
|
||||
internal interface IServerMessenger
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs a sync against all instance objects
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="servers">The servers to sync against</param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="getNumericId">A delegate to return the Id for each instance to be used to sync to other servers</param>
|
||||
/// <param name="instances"></param>
|
||||
void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sync against all instance objects
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="servers">The servers to sync against</param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="getGuidId">A delegate to return the Id for each instance to be used to sync to other servers</param>
|
||||
/// <param name="instances"></param>
|
||||
void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the cache for the specified items
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="servers"></param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="getNumericId">A delegate to return the Id for each instance to be used to sync to other servers</param>
|
||||
/// <param name="instances"></param>
|
||||
void PerformRemove<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the cache for the specified items
|
||||
/// </summary>
|
||||
/// <param name="servers"></param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="numericIds"></param>
|
||||
void PerformRemove(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sync against all Ids
|
||||
/// </summary>
|
||||
/// <param name="servers">The servers to sync against</param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="numericIds"></param>
|
||||
void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sync against all Ids
|
||||
/// </summary>
|
||||
/// <param name="servers">The servers to sync against</param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="guidIds"></param>
|
||||
void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params Guid[] guidIds);
|
||||
|
||||
/// <summary>
|
||||
/// Performs entire cache refresh for a specified refresher
|
||||
/// </summary>
|
||||
/// <param name="servers"></param>
|
||||
/// <param name="refresher"></param>
|
||||
void PerformRefreshAll(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher);
|
||||
}
|
||||
|
||||
}
|
||||
12
src/Umbraco.Core/Sync/IServerRegistrar.cs
Normal file
12
src/Umbraco.Core/Sync/IServerRegistrar.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface to expose a list of server registrations for server syncing
|
||||
/// </summary>
|
||||
internal interface IServerRegistrar
|
||||
{
|
||||
IEnumerable<IServerRegistration> Registrations { get; }
|
||||
}
|
||||
}
|
||||
10
src/Umbraco.Core/Sync/IServerRegistration.cs
Normal file
10
src/Umbraco.Core/Sync/IServerRegistration.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface exposing a server address to use for server syncing
|
||||
/// </summary>
|
||||
internal interface IServerRegistration
|
||||
{
|
||||
string ServerAddress { get; }
|
||||
}
|
||||
}
|
||||
12
src/Umbraco.Core/Sync/MessageType.cs
Normal file
12
src/Umbraco.Core/Sync/MessageType.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// The message type to be used for syncing across servers
|
||||
/// </summary>
|
||||
internal enum MessageType
|
||||
{
|
||||
RefreshAll,
|
||||
RefreshById,
|
||||
RemoveById
|
||||
}
|
||||
}
|
||||
29
src/Umbraco.Core/Sync/ServerMessengerResolver.cs
Normal file
29
src/Umbraco.Core/Sync/ServerMessengerResolver.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A resolver to return the currently registered IServerMessenger object
|
||||
/// </summary>
|
||||
internal class ServerMessengerResolver : SingleObjectResolverBase<ServerMessengerResolver, IServerMessenger>
|
||||
{
|
||||
internal ServerMessengerResolver(IServerMessenger factory)
|
||||
: base(factory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used at runtime to set a custom IServerMessenger at app startup
|
||||
/// </summary>
|
||||
/// <param name="serverMessenger"></param>
|
||||
public void SetServerMessenger(IServerMessenger serverMessenger)
|
||||
{
|
||||
Value = serverMessenger;
|
||||
}
|
||||
|
||||
public IServerMessenger Messenger
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Umbraco.Core/Sync/ServerRegistrarResolver.cs
Normal file
31
src/Umbraco.Core/Sync/ServerRegistrarResolver.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// The resolver to return the currently registered IServerRegistrar object
|
||||
/// </summary>
|
||||
internal class ServerRegistrarResolver : SingleObjectResolverBase<ServerRegistrarResolver, IServerRegistrar>
|
||||
{
|
||||
|
||||
internal ServerRegistrarResolver(IServerRegistrar factory)
|
||||
: base(factory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used at runtime to set a custom IServerRegistrar at app startup
|
||||
/// </summary>
|
||||
/// <param name="serverRegistrar"></param>
|
||||
public void SetServerRegistrar(IServerRegistrar serverRegistrar)
|
||||
{
|
||||
Value = serverRegistrar;
|
||||
}
|
||||
|
||||
public IServerRegistrar Registrar
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
|
||||
internal class ConfigServerRegistrar : IServerRegistrar
|
||||
{
|
||||
|
||||
private List<IServerRegistration> _addresses;
|
||||
|
||||
public IEnumerable<IServerRegistration> Registrations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_addresses == null)
|
||||
{
|
||||
_addresses = new List<IServerRegistration>();
|
||||
var nodes = UmbracoSettings.DistributionServers.SelectNodes("./server");
|
||||
foreach (XmlNode n in nodes)
|
||||
{
|
||||
_addresses.Add(new ConfigServerRegistration(n));
|
||||
}
|
||||
}
|
||||
return _addresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IServerRegistration
|
||||
{
|
||||
string ServerAddress { get; }
|
||||
}
|
||||
|
||||
internal class ConfigServerRegistration : IServerRegistration
|
||||
{
|
||||
|
||||
public ConfigServerRegistration(XmlNode n)
|
||||
{
|
||||
var protocol = GlobalSettings.UseSSL ? "https" : "http";
|
||||
if (n.Attributes.GetNamedItem("forceProtocol") != null && !string.IsNullOrEmpty(n.Attributes.GetNamedItem("forceProtocol").Value))
|
||||
protocol = n.Attributes.GetNamedItem("forceProtocol").Value;
|
||||
var domain = XmlHelper.GetNodeValue(n);
|
||||
if (n.Attributes.GetNamedItem("forcePortnumber") != null && !string.IsNullOrEmpty(n.Attributes.GetNamedItem("forcePortnumber").Value))
|
||||
domain += string.Format(":{0}", n.Attributes.GetNamedItem("forcePortnumber").Value);
|
||||
ServerAddress = string.Format("{0}://{1}{2}", protocol, domain);
|
||||
}
|
||||
|
||||
public string ServerAddress { get; private set; }
|
||||
|
||||
}
|
||||
|
||||
internal interface IServerRegistrar
|
||||
{
|
||||
IEnumerable<IServerRegistration> Registrations { get; }
|
||||
}
|
||||
|
||||
internal class ServerRegistrarResolver : SingleObjectResolverBase<ServerRegistrarResolver, IServerRegistrar>
|
||||
{
|
||||
|
||||
internal ServerRegistrarResolver(IServerRegistrar factory)
|
||||
: base(factory)
|
||||
{
|
||||
}
|
||||
|
||||
public IServerRegistrar Registrar
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
154
src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs
Normal file
154
src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Web.Services;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The client Soap service for making distrubuted cache calls between servers
|
||||
/// </summary>
|
||||
[WebServiceBinding(Name = "CacheRefresherSoap", Namespace = "http://umbraco.org/webservices/")]
|
||||
internal class ServerSyncWebServiceClient : System.Web.Services.Protocols.SoapHttpClientProtocol
|
||||
{
|
||||
|
||||
/// <remarks/>
|
||||
public ServerSyncWebServiceClient()
|
||||
{
|
||||
// only set the url if the httpcontext is present, else it's set by the cache dispatcher methods (when using distributed calls)
|
||||
if (System.Web.HttpContext.Current != null)
|
||||
this.Url = "http://" + System.Web.HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + IOHelper.ResolveUrl(SystemDirectories.WebServices) + "/cacheRefresher.asmx";
|
||||
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshAll(System.Guid uniqueIdentifier, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshAll", new object[] {
|
||||
uniqueIdentifier,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshAll(System.Guid uniqueIdentifier, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshAll", new object[] {
|
||||
uniqueIdentifier,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshAll(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshByGuid", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshByGuid", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshByGuid", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshByGuid(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshById(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RemoveById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RemoveById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RemoveById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRemoveById(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/GetRefreshers", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public System.Xml.XmlNode GetRefreshers(string Login, string Password)
|
||||
{
|
||||
object[] results = this.Invoke("GetRefreshers", new object[] {
|
||||
Login,
|
||||
Password});
|
||||
return ((System.Xml.XmlNode)(results[0]));
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("GetRefreshers", new object[] {
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
|
||||
{
|
||||
object[] results = this.EndInvoke(asyncResult);
|
||||
return ((System.Xml.XmlNode)(results[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Services" />
|
||||
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll</HintPath>
|
||||
@@ -667,7 +668,18 @@
|
||||
<Compile Include="Services\MediaService.cs" />
|
||||
<Compile Include="Services\ServiceContext.cs" />
|
||||
<Compile Include="Services\UserService.cs" />
|
||||
<Compile Include="Sync\ServerSync.cs" />
|
||||
<Compile Include="Sync\DefaultServerMessenger.cs" />
|
||||
<Compile Include="Sync\ServerSyncWebServiceClient.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Sync\ConfigServerRegistration.cs" />
|
||||
<Compile Include="Sync\IServerMessenger.cs" />
|
||||
<Compile Include="Sync\IServerRegistrar.cs" />
|
||||
<Compile Include="Sync\IServerRegistration.cs" />
|
||||
<Compile Include="Sync\MessageType.cs" />
|
||||
<Compile Include="Sync\ServerMessengerResolver.cs" />
|
||||
<Compile Include="Sync\ServerRegistrarResolver.cs" />
|
||||
<Compile Include="Sync\ConfigServerRegistrar.cs" />
|
||||
<Compile Include="TypeExtensions.cs" />
|
||||
<Compile Include="ReadLock.cs" />
|
||||
<Compile Include="TypeFinder.cs" />
|
||||
|
||||
45
src/Umbraco.Tests/Sync/ConfigServerRegistrarTests.cs
Normal file
45
src/Umbraco.Tests/Sync/ConfigServerRegistrarTests.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Tests.PartialTrust;
|
||||
|
||||
namespace Umbraco.Tests.Sync
|
||||
{
|
||||
[TestFixture]
|
||||
public class ConfigServerRegistrarTests
|
||||
{
|
||||
|
||||
[TestCase("<server>127.0.0.1</server>", "http://127.0.0.1")]
|
||||
[TestCase("<server forceProtocol='https'>www.somedomain.com</server>", "https://www.somedomain.com")]
|
||||
[TestCase("<server forcePortnumber='888'>another.domain.com.au</server>", "http://another.domain.com.au:888")]
|
||||
[TestCase("<server forcePortnumber='999' forceProtocol='https'>another.domain.com.au</server>", "https://another.domain.com.au:999")]
|
||||
public void Ensure_Correct_Format(string xml, string match)
|
||||
{
|
||||
var xDoc = new XmlDocument();
|
||||
xDoc.LoadXml(xml);
|
||||
var xNode = xDoc.FirstChild;
|
||||
var cReg = new ConfigServerRegistration(xNode);
|
||||
|
||||
Assert.AreEqual(match + "/umbraco/webservices/cacheRefresher.asmx", cReg.ServerAddress);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Parses_Config_Block()
|
||||
{
|
||||
var xDoc = new XmlDocument();
|
||||
xDoc.LoadXml(@"<servers>
|
||||
<server>127.0.0.1</server>
|
||||
<server forceProtocol='https'>www.somedomain.com</server>
|
||||
<server forcePortnumber='888'>another.domain.com.au</server>
|
||||
<server forcePortnumber='999' forceProtocol='https'>another.domain.com.au</server>
|
||||
</servers>");
|
||||
var xNode = xDoc.FirstChild;
|
||||
var cReg = new ConfigServerRegistrar(xNode);
|
||||
|
||||
Assert.AreEqual(4, cReg.Registrations.Count());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
180
src/Umbraco.Tests/Sync/DistributedCacheTests.cs
Normal file
180
src/Umbraco.Tests/Sync/DistributedCacheTests.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Web.Cache;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Tests.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures that calls to DistributedCache methods carry through to the IServerMessenger correctly
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class DistributedCacheTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ServerRegistrarResolver.Current = new ServerRegistrarResolver(
|
||||
new TestServerRegistrar());
|
||||
ServerMessengerResolver.Current = new ServerMessengerResolver(
|
||||
new TestServerMessenger());
|
||||
CacheRefreshersResolver.Current = new CacheRefreshersResolver(() => new[] { typeof(TestCacheRefresher) });
|
||||
Resolution.Freeze();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
ServerRegistrarResolver.Reset();
|
||||
ServerMessengerResolver.Reset();
|
||||
CacheRefreshersResolver.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RefreshIntId()
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
DistributedCache.Instance.Refresh(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i);
|
||||
}
|
||||
Assert.AreEqual(10, ((TestServerMessenger)ServerMessengerResolver.Current.Messenger).IntIdsRefreshed.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RefreshGuidId()
|
||||
{
|
||||
for (var i = 0; i < 11; i++)
|
||||
{
|
||||
DistributedCache.Instance.Refresh(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), Guid.NewGuid());
|
||||
}
|
||||
Assert.AreEqual(11, ((TestServerMessenger)ServerMessengerResolver.Current.Messenger).GuidIdsRefreshed.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveIds()
|
||||
{
|
||||
for (var i = 0; i < 12; i++)
|
||||
{
|
||||
DistributedCache.Instance.Remove(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i);
|
||||
}
|
||||
Assert.AreEqual(12, ((TestServerMessenger)ServerMessengerResolver.Current.Messenger).IntIdsRemoved.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FullRefreshes()
|
||||
{
|
||||
for (var i = 0; i < 13; i++)
|
||||
{
|
||||
DistributedCache.Instance.RefreshAll(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"));
|
||||
}
|
||||
Assert.AreEqual(13, ((TestServerMessenger)ServerMessengerResolver.Current.Messenger).CountOfFullRefreshes);
|
||||
}
|
||||
|
||||
#region internal test classes
|
||||
|
||||
internal class TestCacheRefresher : ICacheRefresher
|
||||
{
|
||||
public Guid UniqueIdentifier
|
||||
{
|
||||
get { return Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"); }
|
||||
}
|
||||
public string Name
|
||||
{
|
||||
get { return "Test"; }
|
||||
}
|
||||
public void RefreshAll()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Refresh(int id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Remove(int id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Refresh(Guid id)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestServerMessenger : IServerMessenger
|
||||
{
|
||||
//used for tests
|
||||
public List<int> IntIdsRefreshed = new List<int>();
|
||||
public List<Guid> GuidIdsRefreshed = new List<Guid>();
|
||||
public List<int> IntIdsRemoved = new List<int>();
|
||||
public int CountOfFullRefreshes = 0;
|
||||
|
||||
|
||||
public void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRefresh<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRemove<T>(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PerformRemove(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds)
|
||||
{
|
||||
IntIdsRemoved.AddRange(numericIds);
|
||||
}
|
||||
|
||||
public void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params int[] numericIds)
|
||||
{
|
||||
IntIdsRefreshed.AddRange(numericIds);
|
||||
}
|
||||
|
||||
public void PerformRefresh(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher, params Guid[] guidIds)
|
||||
{
|
||||
GuidIdsRefreshed.AddRange(guidIds);
|
||||
}
|
||||
|
||||
public void PerformRefreshAll(IEnumerable<IServerRegistration> servers, ICacheRefresher refresher)
|
||||
{
|
||||
CountOfFullRefreshes++;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestServerRegistrar : IServerRegistrar
|
||||
{
|
||||
public IEnumerable<IServerRegistration> Registrations
|
||||
{
|
||||
get
|
||||
{
|
||||
return new List<IServerRegistration>()
|
||||
{
|
||||
new TestServerRegistration("localhost")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TestServerRegistration : IServerRegistration
|
||||
{
|
||||
public TestServerRegistration(string address)
|
||||
{
|
||||
ServerAddress = address;
|
||||
}
|
||||
public string ServerAddress { get; private set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -261,6 +261,8 @@
|
||||
<Compile Include="Services\ContentTypeServiceTests.cs" />
|
||||
<Compile Include="Services\ThreadSafetyServiceTest.cs" />
|
||||
<Compile Include="Surface\PluginControllerAreaTests.cs" />
|
||||
<Compile Include="Sync\ConfigServerRegistrarTests.cs" />
|
||||
<Compile Include="Sync\DistributedCacheTests.cs" />
|
||||
<Compile Include="Templates\MasterPageHelperTests.cs" />
|
||||
<Compile Include="TestHelpers\BaseDatabaseFactoryTest.cs" />
|
||||
<Compile Include="TestHelpers\BaseRoutingTest.cs" />
|
||||
|
||||
@@ -150,14 +150,16 @@
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls make umbraco use webservices to handle cache refreshing -->
|
||||
<distributedCall enable="false">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
<servers>
|
||||
<!-- add ip number or hostname, make sure that it can be reached from all servers -->
|
||||
<!-- <server>127.0.0.1</server>-->
|
||||
</servers>
|
||||
<distributedCall enable="true">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
<servers>
|
||||
<!-- add ip number or hostname, make sure that it can be reached from all servers -->
|
||||
<!-- you can also add optional attributes to force a protocol or port number (see #2) -->
|
||||
<server>umblb1.dev</server>
|
||||
<server forcePortnumber="61638">localhost</server>
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
<!-- configuration for webservices -->
|
||||
|
||||
@@ -1,155 +1,155 @@
|
||||
using Umbraco.Core.IO;
|
||||
//using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
{
|
||||
//namespace Umbraco.Web.Cache
|
||||
//{
|
||||
|
||||
/// <summary>
|
||||
/// The client Soap service for making distrubuted cache calls between servers
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerStepThroughAttribute()]
|
||||
[System.ComponentModel.DesignerCategoryAttribute("code")]
|
||||
[System.Web.Services.WebServiceBindingAttribute(Name = "CacheRefresherSoap", Namespace = "http://umbraco.org/webservices/")]
|
||||
internal class CacheRefresherClient : System.Web.Services.Protocols.SoapHttpClientProtocol
|
||||
{
|
||||
// /// <summary>
|
||||
// /// The client Soap service for making distrubuted cache calls between servers
|
||||
// /// </summary>
|
||||
// [System.Diagnostics.DebuggerStepThroughAttribute()]
|
||||
// [System.ComponentModel.DesignerCategoryAttribute("code")]
|
||||
// [System.Web.Services.WebServiceBindingAttribute(Name = "CacheRefresherSoap", Namespace = "http://umbraco.org/webservices/")]
|
||||
// internal class CacheRefresherClient : System.Web.Services.Protocols.SoapHttpClientProtocol
|
||||
// {
|
||||
|
||||
/// <remarks/>
|
||||
public CacheRefresherClient()
|
||||
{
|
||||
// only set the url if the httpcontext is present, else it's set by the cache dispatcher methods (when using distributed calls)
|
||||
if (System.Web.HttpContext.Current != null)
|
||||
this.Url = "http://" + System.Web.HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + IOHelper.ResolveUrl(SystemDirectories.WebServices) + "/cacheRefresher.asmx";
|
||||
// /// <remarks/>
|
||||
// public CacheRefresherClient()
|
||||
// {
|
||||
// // only set the url if the httpcontext is present, else it's set by the cache dispatcher methods (when using distributed calls)
|
||||
// if (System.Web.HttpContext.Current != null)
|
||||
// this.Url = "http://" + System.Web.HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + IOHelper.ResolveUrl(SystemDirectories.WebServices) + "/cacheRefresher.asmx";
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshAll(System.Guid uniqueIdentifier, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshAll", new object[] {
|
||||
uniqueIdentifier,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
// /// <remarks/>
|
||||
// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
// public void RefreshAll(System.Guid uniqueIdentifier, string Login, string Password)
|
||||
// {
|
||||
// this.Invoke("RefreshAll", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Login,
|
||||
// Password});
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshAll(System.Guid uniqueIdentifier, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshAll", new object[] {
|
||||
uniqueIdentifier,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.IAsyncResult BeginRefreshAll(System.Guid uniqueIdentifier, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
// {
|
||||
// return this.BeginInvoke("RefreshAll", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Login,
|
||||
// Password}, callback, asyncState);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshAll(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public void EndRefreshAll(System.IAsyncResult asyncResult)
|
||||
// {
|
||||
// this.EndInvoke(asyncResult);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshByGuid", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshByGuid", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
// /// <remarks/>
|
||||
// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshByGuid", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
// public void RefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password)
|
||||
// {
|
||||
// this.Invoke("RefreshByGuid", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password});
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshByGuid", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.IAsyncResult BeginRefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
// {
|
||||
// return this.BeginInvoke("RefreshByGuid", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password}, callback, asyncState);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshByGuid(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public void EndRefreshByGuid(System.IAsyncResult asyncResult)
|
||||
// {
|
||||
// this.EndInvoke(asyncResult);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RefreshById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
// /// <remarks/>
|
||||
// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
// public void RefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
// {
|
||||
// this.Invoke("RefreshById", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password});
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RefreshById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.IAsyncResult BeginRefreshById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
// {
|
||||
// return this.BeginInvoke("RefreshById", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password}, callback, asyncState);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRefreshById(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public void EndRefreshById(System.IAsyncResult asyncResult)
|
||||
// {
|
||||
// this.EndInvoke(asyncResult);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RemoveById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RemoveById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
// /// <remarks/>
|
||||
// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RemoveById", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
// public void RemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
// {
|
||||
// this.Invoke("RemoveById", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password});
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RemoveById", new object[] {
|
||||
uniqueIdentifier,
|
||||
Id,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.IAsyncResult BeginRemoveById(System.Guid uniqueIdentifier, int Id, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
// {
|
||||
// return this.BeginInvoke("RemoveById", new object[] {
|
||||
// uniqueIdentifier,
|
||||
// Id,
|
||||
// Login,
|
||||
// Password}, callback, asyncState);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRemoveById(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public void EndRemoveById(System.IAsyncResult asyncResult)
|
||||
// {
|
||||
// this.EndInvoke(asyncResult);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/GetRefreshers", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public System.Xml.XmlNode GetRefreshers(string Login, string Password)
|
||||
{
|
||||
object[] results = this.Invoke("GetRefreshers", new object[] {
|
||||
Login,
|
||||
Password});
|
||||
return ((System.Xml.XmlNode)(results[0]));
|
||||
}
|
||||
// /// <remarks/>
|
||||
// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/GetRefreshers", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
// public System.Xml.XmlNode GetRefreshers(string Login, string Password)
|
||||
// {
|
||||
// object[] results = this.Invoke("GetRefreshers", new object[] {
|
||||
// Login,
|
||||
// Password});
|
||||
// return ((System.Xml.XmlNode)(results[0]));
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("GetRefreshers", new object[] {
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
// {
|
||||
// return this.BeginInvoke("GetRefreshers", new object[] {
|
||||
// Login,
|
||||
// Password}, callback, asyncState);
|
||||
// }
|
||||
|
||||
/// <remarks/>
|
||||
public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
|
||||
{
|
||||
object[] results = this.EndInvoke(asyncResult);
|
||||
return ((System.Xml.XmlNode)(results[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// <remarks/>
|
||||
// public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
|
||||
// {
|
||||
// object[] results = this.EndInvoke(asyncResult);
|
||||
// return ((System.Xml.XmlNode)(results[0]));
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -16,17 +16,13 @@ using umbraco.interfaces;
|
||||
namespace Umbraco.Web.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// DistrubutedCacheDispatcher is used to handle Umbraco's load balancing.
|
||||
/// DistributedCache is used to invalidate cache throughout the application which also takes in to account load balancing environments automatically
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Distributing calls to all registered load balanced servers, ensuring that content are synced and cached on all servers.
|
||||
/// Dispatcher is exendable, so 3rd party services can easily be integrated into the workflow, using the interfaces.ICacheRefresher interface.
|
||||
///
|
||||
/// Dispatcher can refresh/remove content, templates and macros.
|
||||
/// Load balanced servers are registered in umbracoSettings.config.
|
||||
///
|
||||
/// UPDATE 2010 02 - Alex Norcliffe - Refactored Dispatcher to support parallel dispatch threads, and preventing failure of whole dispatch
|
||||
/// if one node fails. Still needs more work to get it to Enterprise level though but this is for 4.1
|
||||
/// </remarks>
|
||||
public class DistributedCache
|
||||
{
|
||||
@@ -41,19 +37,13 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly string _login;
|
||||
private readonly string _password;
|
||||
private readonly string _webServicesUrl;
|
||||
private static readonly DistributedCache InstanceObject = new DistributedCache();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
private DistributedCache()
|
||||
{
|
||||
_login = User.GetUser(UmbracoSettings.DistributedCallUser).LoginName;
|
||||
_password = User.GetUser(UmbracoSettings.DistributedCallUser).GetPassword();
|
||||
_webServicesUrl = IOHelper.ResolveUrl(SystemDirectories.WebServices);
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -76,7 +66,10 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The id of the node.</param>
|
||||
public void Refresh(Guid factoryGuid, int id)
|
||||
{
|
||||
InvokeDispatchMethod(DispatchType.RefreshByNumericId, factoryGuid, id, Guid.Empty);
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,7 +80,10 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The guid of the node.</param>
|
||||
public void Refresh(Guid factoryGuid, Guid id)
|
||||
{
|
||||
InvokeDispatchMethod(DispatchType.RefreshByGuid, factoryGuid, 0, id);
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,7 +93,9 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="factoryGuid">The unique identifier.</param>
|
||||
public void RefreshAll(Guid factoryGuid)
|
||||
{
|
||||
InvokeDispatchMethod(DispatchType.RefreshAll, factoryGuid, 0, Guid.Empty);
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefreshAll(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -108,192 +106,10 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The id.</param>
|
||||
public void Remove(Guid factoryGuid, int id)
|
||||
{
|
||||
InvokeDispatchMethod(DispatchType.RemoveById, factoryGuid, id, Guid.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to invoke the method on an ICacheRefresher instance if we are not currently using distributed calls.
|
||||
/// </summary>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="dispatchType"></param>
|
||||
/// <param name="numericId"></param>
|
||||
/// <param name="guidId"></param>
|
||||
private void InvokeMethodOnRefresherInstance(ICacheRefresher refresher, DispatchType dispatchType, int numericId, Guid guidId)
|
||||
{
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
switch (dispatchType)
|
||||
{
|
||||
case DispatchType.RefreshAll:
|
||||
refresher.RefreshAll();
|
||||
break;
|
||||
case DispatchType.RefreshByNumericId:
|
||||
refresher.Refresh(numericId);
|
||||
break;
|
||||
case DispatchType.RefreshByGuid:
|
||||
refresher.Refresh(guidId);
|
||||
break;
|
||||
case DispatchType.RemoveById:
|
||||
refresher.Remove(numericId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the relevant dispatch method.
|
||||
/// </summary>
|
||||
/// <param name="dispatchType">Type of the dispatch.</param>
|
||||
/// <param name="factoryGuid">The factory GUID.</param>
|
||||
/// <param name="numericId">The numeric id.</param>
|
||||
/// <param name="guidId">The GUID id.</param>
|
||||
private void InvokeDispatchMethod(DispatchType dispatchType, Guid factoryGuid, int numericId, Guid guidId)
|
||||
{
|
||||
//get the refresher, it must be found or else we cannot continue
|
||||
var refresher = GetRefresherById(factoryGuid);
|
||||
if (refresher == null)
|
||||
{
|
||||
var ex = new InvalidOperationException(
|
||||
"Could not find an " + typeof(ICacheRefresher).Name + " with the Id " + guidId);
|
||||
LogHelper.Error<DistributedCache>("Could not continue with DistributedCache call", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
//Now, check if we are using Distrubuted calls
|
||||
if (!UmbracoSettings.UseDistributedCalls)
|
||||
{
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
InvokeMethodOnRefresherInstance(refresher, dispatchType, numericId, guidId);
|
||||
return;
|
||||
}
|
||||
|
||||
//We are using distributed calls, so lets make them...
|
||||
try
|
||||
{
|
||||
using (var cacheRefresher = new CacheRefresherClient())
|
||||
{
|
||||
var asyncResultsList = new List<IAsyncResult>();
|
||||
|
||||
LogStartDispatch();
|
||||
|
||||
//var nodes =
|
||||
|
||||
// Go through each configured node submitting a request asynchronously
|
||||
foreach (XmlNode n in GetDistributedNodes())
|
||||
{
|
||||
SetWebServiceUrlFromNode(cacheRefresher, n);
|
||||
|
||||
// Add the returned WaitHandle to the list for later checking
|
||||
switch (dispatchType)
|
||||
{
|
||||
case DispatchType.RefreshAll:
|
||||
asyncResultsList.Add(cacheRefresher.BeginRefreshAll(factoryGuid, _login, _password, null,
|
||||
null));
|
||||
break;
|
||||
case DispatchType.RefreshByGuid:
|
||||
asyncResultsList.Add(cacheRefresher.BeginRefreshByGuid(factoryGuid, guidId, _login,
|
||||
_password, null, null));
|
||||
break;
|
||||
case DispatchType.RefreshByNumericId:
|
||||
asyncResultsList.Add(cacheRefresher.BeginRefreshById(factoryGuid, numericId, _login,
|
||||
_password, null, null));
|
||||
break;
|
||||
case DispatchType.RemoveById:
|
||||
asyncResultsList.Add(cacheRefresher.BeginRemoveById(factoryGuid, numericId, _login,
|
||||
_password, null, null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<WaitHandle> waitHandlesList;
|
||||
IAsyncResult[] asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
|
||||
|
||||
int errorCount = 0;
|
||||
|
||||
// Once for each WaitHandle that we have, wait for a response and log it
|
||||
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
|
||||
for (int waitCalls = 0; waitCalls < asyncResults.Length; waitCalls++)
|
||||
{
|
||||
int handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
|
||||
|
||||
try
|
||||
{
|
||||
// Find out if the call succeeded
|
||||
switch (dispatchType)
|
||||
{
|
||||
case DispatchType.RefreshAll:
|
||||
cacheRefresher.EndRefreshAll(asyncResults[waitCalls]);
|
||||
break;
|
||||
case DispatchType.RefreshByGuid:
|
||||
cacheRefresher.EndRefreshByGuid(asyncResults[waitCalls]);
|
||||
break;
|
||||
case DispatchType.RefreshByNumericId:
|
||||
cacheRefresher.EndRefreshById(asyncResults[waitCalls]);
|
||||
break;
|
||||
case DispatchType.RemoveById:
|
||||
cacheRefresher.EndRemoveById(asyncResults[waitCalls]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
|
||||
errorCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogDispatchBatchResult(errorCount);
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogDispatchBatchError(ee);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDispatchBatchError(Exception ee)
|
||||
{
|
||||
LogHelper.Error<DistributedCache>("Error refreshing distributed list", ee);
|
||||
}
|
||||
|
||||
private void LogDispatchBatchResult(int errorCount)
|
||||
{
|
||||
LogHelper.Debug<DistributedCache>(string.Format("Distributed server push completed with {0} nodes reporting an error", errorCount == 0 ? "no" : errorCount.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(Exception ex)
|
||||
{
|
||||
LogHelper.Error<DistributedCache>("Error refreshing a node in the distributed list", ex);
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(WebException ex)
|
||||
{
|
||||
string url = (ex.Response != null) ? ex.Response.ResponseUri.ToString() : "invalid url (responseUri null)";
|
||||
LogHelper.Error<DistributedCache>("Error refreshing a node in the distributed list, URI attempted: " + url, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the web service URL for a CacheRefresher from an XmlNode.
|
||||
/// </summary>
|
||||
/// <param name="cr">The CacheRefresher.</param>
|
||||
/// <param name="n">The XmlNode.</param>
|
||||
private void SetWebServiceUrlFromNode(WebClientProtocol cr, XmlNode n)
|
||||
{
|
||||
//string protocol = GlobalSettings.UseSSL ? "https" : "http";
|
||||
//if (n.Attributes.GetNamedItem("forceProtocol") != null && !String.IsNullOrEmpty(n.Attributes.GetNamedItem("forceProtocol").Value))
|
||||
// protocol = n.Attributes.GetNamedItem("forceProtocol").Value;
|
||||
//string domain = XmlHelper.GetNodeValue(n);
|
||||
//if (n.Attributes.GetNamedItem("forcePortnumber") != null && !String.IsNullOrEmpty(n.Attributes.GetNamedItem("forcePortnumber").Value))
|
||||
// domain += string.Format(":{0}", n.Attributes.GetNamedItem("forcePortnumber").Value);
|
||||
//cr.Url = string.Format("{0}://{1}{2}/cacheRefresher.asmx", protocol, domain, _webServicesUrl);
|
||||
|
||||
//ServerRegistrarResolver.Current.Registrar.Registrations
|
||||
ServerMessengerResolver.Current.Messenger.PerformRemove(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
id);
|
||||
}
|
||||
|
||||
private static ICacheRefresher GetRefresherById(Guid uniqueIdentifier)
|
||||
@@ -301,42 +117,5 @@ namespace Umbraco.Web.Cache
|
||||
return CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
}
|
||||
|
||||
private void LogStartDispatch()
|
||||
{
|
||||
LogHelper.Info<DistributedCache>("Submitting calls to distributed servers");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the node list of DistributionServers from config.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private XmlNodeList GetDistributedNodes()
|
||||
{
|
||||
return UmbracoSettings.DistributionServers.SelectNodes("./server");
|
||||
}
|
||||
|
||||
private IAsyncResult[] GetAsyncResults(List<IAsyncResult> asyncResultsList,
|
||||
out List<WaitHandle> waitHandlesList)
|
||||
{
|
||||
IAsyncResult[] asyncResults = asyncResultsList.ToArray();
|
||||
waitHandlesList = new List<WaitHandle>();
|
||||
foreach (IAsyncResult asyncResult in asyncResults)
|
||||
{
|
||||
waitHandlesList.Add(asyncResult.AsyncWaitHandle);
|
||||
}
|
||||
return asyncResults;
|
||||
}
|
||||
|
||||
#region Nested type: DispatchType
|
||||
|
||||
private enum DispatchType
|
||||
{
|
||||
RefreshAll,
|
||||
RefreshByNumericId,
|
||||
RefreshByGuid,
|
||||
RemoveById
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Web.Dictionary;
|
||||
using Umbraco.Web.Media;
|
||||
using Umbraco.Web.Media.ThumbnailProviders;
|
||||
@@ -16,6 +18,7 @@ using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Web.Routing;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.businesslogic;
|
||||
using umbraco.cms.businesslogic;
|
||||
using umbraco.presentation.cache;
|
||||
@@ -173,6 +176,24 @@ namespace Umbraco.Web
|
||||
{
|
||||
base.InitializeResolvers();
|
||||
|
||||
//we should not proceed to change this if the app/database is not configured since there will
|
||||
// be no user, plus we don't need to have server messages sent if this is the case.
|
||||
if (ApplicationContext.IsConfigured && ApplicationContext.DatabaseContext.IsDatabaseConfigured)
|
||||
{
|
||||
var user = User.GetUser(UmbracoSettings.DistributedCallUser);
|
||||
try
|
||||
{
|
||||
//Override the ServerMessengerResolver to set a username/password for the distributed calls
|
||||
ServerMessengerResolver.Current.SetServerMessenger(new DefaultServerMessenger(
|
||||
user.LoginName,
|
||||
user.GetPassword()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<WebBootManager>("An error occurred trying to set the IServerMessenger during application startup", e);
|
||||
}
|
||||
}
|
||||
|
||||
//We are going to manually remove a few cache refreshers here because we've obsoleted them and we don't want them
|
||||
// to be registered more than once
|
||||
CacheRefreshersResolver.Current.RemoveType<pageRefresher>();
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace UmbracoExamine
|
||||
{
|
||||
|
||||
//We need to check if we actually can initialize, if not then don't continue
|
||||
if (!CanInitialized())
|
||||
if (!CanInitialize())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +173,8 @@ namespace UmbracoExamine
|
||||
/// Returns true if the Umbraco application is in a state that we can initialize the examine indexes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool CanInitialized()
|
||||
[SecuritySafeCritical]
|
||||
protected bool CanInitialize()
|
||||
{
|
||||
//We need to check if we actually can initialize, if not then don't continue
|
||||
if (ApplicationContext.Current == null
|
||||
|
||||
@@ -19,12 +19,14 @@ namespace UmbracoExamine.DataServices
|
||||
{
|
||||
private readonly ServiceContext _services;
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public UmbracoMediaService()
|
||||
: this(ApplicationContext.Current.Services)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public UmbracoMediaService(ServiceContext services)
|
||||
{
|
||||
_services = services;
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace UmbracoExamine
|
||||
{
|
||||
|
||||
//We need to check if we actually can initialize, if not then don't continue
|
||||
if (!CanInitialized())
|
||||
if (!CanInitialize())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace UmbracoExamine
|
||||
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
|
||||
{
|
||||
//We need to check if we actually can initialize, if not then don't continue
|
||||
if (!CanInitialized())
|
||||
if (!CanInitialize())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -70,7 +70,8 @@ namespace UmbracoExamine
|
||||
/// Returns true if the Umbraco application is in a state that we can initialize the examine indexes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool CanInitialized()
|
||||
[SecuritySafeCritical]
|
||||
protected bool CanInitialize()
|
||||
{
|
||||
//We need to check if we actually can initialize, if not then don't continue
|
||||
if (ApplicationContext.Current == null
|
||||
|
||||
Reference in New Issue
Block a user