diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index 7e736be161..bab1269932 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -166,9 +166,15 @@ namespace Umbraco.Core
///
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());
diff --git a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs
new file mode 100644
index 0000000000..8080ef05fa
--- /dev/null
+++ b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs
@@ -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
+{
+ ///
+ /// A registrar that uses the legacy xml configuration in umbracoSettings to get a list of defined server nodes
+ ///
+ internal class ConfigServerRegistrar : IServerRegistrar
+ {
+ private readonly XmlNode _xmlServers;
+
+ public ConfigServerRegistrar()
+ : this(UmbracoSettings.DistributionServers)
+ {
+
+ }
+
+ internal ConfigServerRegistrar(XmlNode xmlServers)
+ {
+ _xmlServers = xmlServers;
+ }
+
+ private List _addresses;
+
+ public IEnumerable Registrations
+ {
+ get
+ {
+ if (_addresses == null)
+ {
+ _addresses = new List();
+ var nodes = _xmlServers.SelectNodes("./server");
+ foreach (XmlNode n in nodes)
+ {
+ _addresses.Add(new ConfigServerRegistration(n));
+ }
+ }
+ return _addresses;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Sync/ConfigServerRegistration.cs b/src/Umbraco.Core/Sync/ConfigServerRegistration.cs
new file mode 100644
index 0000000000..c8865bd4a2
--- /dev/null
+++ b/src/Umbraco.Core/Sync/ConfigServerRegistration.cs
@@ -0,0 +1,29 @@
+using System.Xml;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.IO;
+
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// A server registration based on the legacy umbraco xml configuration in umbracoSettings
+ ///
+ 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; }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs
new file mode 100644
index 0000000000..cc6a428ee6
--- /dev/null
+++ b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs
@@ -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
+{
+ ///
+ /// The default server messenger that uses web services to keep servers in sync
+ ///
+ internal class DefaultServerMessenger : IServerMessenger
+ {
+ private readonly string _login;
+ private readonly string _password;
+ private readonly bool _useDistributedCalls;
+
+ ///
+ /// Without a username/password all distribuion will be disabled
+ ///
+ internal DefaultServerMessenger()
+ {
+ _useDistributedCalls = false;
+ }
+
+ ///
+ /// Distribution will be enabled based on the umbraco config setting.
+ ///
+ ///
+ ///
+ internal DefaultServerMessenger(string login, string password)
+ {
+ _useDistributedCalls = UmbracoSettings.UseDistributedCalls;
+ _login = login;
+ _password = password;
+ }
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher,Func getNumericId, params T[] instances)
+ {
+ if (servers == null) throw new ArgumentNullException("servers");
+ if (refresher == null) throw new ArgumentNullException("refresher");
+ throw new NotImplementedException();
+ }
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances)
+ {
+ if (servers == null) throw new ArgumentNullException("servers");
+ if (refresher == null) throw new ArgumentNullException("refresher");
+ throw new NotImplementedException();
+ }
+
+ public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func 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 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 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 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 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 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();
+
+ 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 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 GetAsyncResults(List asyncResultsList, out List waitHandlesList)
+ {
+ var asyncResults = asyncResultsList.ToArray();
+ waitHandlesList = new List();
+ foreach (var asyncResult in asyncResults)
+ {
+ waitHandlesList.Add(asyncResult.AsyncWaitHandle);
+ }
+ return asyncResults;
+ }
+
+ private void LogDispatchBatchError(Exception ee)
+ {
+ LogHelper.Error("Error refreshing distributed list", ee);
+ }
+
+ private void LogDispatchBatchResult(int errorCount)
+ {
+ LogHelper.Debug(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("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("Error refreshing a node in the distributed list, URI attempted: " + url, ex);
+ }
+
+ private void LogStartDispatch()
+ {
+ LogHelper.Info("Submitting calls to distributed servers");
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/IServerMessenger.cs b/src/Umbraco.Core/Sync/IServerMessenger.cs
new file mode 100644
index 0000000000..f2112ea22d
--- /dev/null
+++ b/src/Umbraco.Core/Sync/IServerMessenger.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using umbraco.interfaces;
+
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// Defines a server messenger for server sync and distrubuted cache
+ ///
+ internal interface IServerMessenger
+ {
+ ///
+ /// Performs a sync against all instance objects
+ ///
+ ///
+ /// The servers to sync against
+ ///
+ /// A delegate to return the Id for each instance to be used to sync to other servers
+ ///
+ void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances);
+
+ ///
+ /// Performs a sync against all instance objects
+ ///
+ ///
+ /// The servers to sync against
+ ///
+ /// A delegate to return the Id for each instance to be used to sync to other servers
+ ///
+ void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances);
+
+ ///
+ /// Removes the cache for the specified items
+ ///
+ ///
+ ///
+ ///
+ /// A delegate to return the Id for each instance to be used to sync to other servers
+ ///
+ void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances);
+
+ ///
+ /// Removes the cache for the specified items
+ ///
+ ///
+ ///
+ ///
+ void PerformRemove(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds);
+
+ ///
+ /// Performs a sync against all Ids
+ ///
+ /// The servers to sync against
+ ///
+ ///
+ void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds);
+
+ ///
+ /// Performs a sync against all Ids
+ ///
+ /// The servers to sync against
+ ///
+ ///
+ void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds);
+
+ ///
+ /// Performs entire cache refresh for a specified refresher
+ ///
+ ///
+ ///
+ void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher);
+ }
+
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/IServerRegistrar.cs b/src/Umbraco.Core/Sync/IServerRegistrar.cs
new file mode 100644
index 0000000000..b0f6cba20f
--- /dev/null
+++ b/src/Umbraco.Core/Sync/IServerRegistrar.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// An interface to expose a list of server registrations for server syncing
+ ///
+ internal interface IServerRegistrar
+ {
+ IEnumerable Registrations { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/IServerRegistration.cs b/src/Umbraco.Core/Sync/IServerRegistration.cs
new file mode 100644
index 0000000000..a5d80180b8
--- /dev/null
+++ b/src/Umbraco.Core/Sync/IServerRegistration.cs
@@ -0,0 +1,10 @@
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// An interface exposing a server address to use for server syncing
+ ///
+ internal interface IServerRegistration
+ {
+ string ServerAddress { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/MessageType.cs b/src/Umbraco.Core/Sync/MessageType.cs
new file mode 100644
index 0000000000..c4c66e99e2
--- /dev/null
+++ b/src/Umbraco.Core/Sync/MessageType.cs
@@ -0,0 +1,12 @@
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// The message type to be used for syncing across servers
+ ///
+ internal enum MessageType
+ {
+ RefreshAll,
+ RefreshById,
+ RemoveById
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/ServerMessengerResolver.cs b/src/Umbraco.Core/Sync/ServerMessengerResolver.cs
new file mode 100644
index 0000000000..b24001c877
--- /dev/null
+++ b/src/Umbraco.Core/Sync/ServerMessengerResolver.cs
@@ -0,0 +1,29 @@
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// A resolver to return the currently registered IServerMessenger object
+ ///
+ internal class ServerMessengerResolver : SingleObjectResolverBase
+ {
+ internal ServerMessengerResolver(IServerMessenger factory)
+ : base(factory)
+ {
+ }
+
+ ///
+ /// Can be used at runtime to set a custom IServerMessenger at app startup
+ ///
+ ///
+ public void SetServerMessenger(IServerMessenger serverMessenger)
+ {
+ Value = serverMessenger;
+ }
+
+ public IServerMessenger Messenger
+ {
+ get { return Value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs b/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs
new file mode 100644
index 0000000000..3da7747c33
--- /dev/null
+++ b/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs
@@ -0,0 +1,31 @@
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Core.Sync
+{
+ ///
+ /// The resolver to return the currently registered IServerRegistrar object
+ ///
+ internal class ServerRegistrarResolver : SingleObjectResolverBase
+ {
+
+ internal ServerRegistrarResolver(IServerRegistrar factory)
+ : base(factory)
+ {
+ }
+
+ ///
+ /// Can be used at runtime to set a custom IServerRegistrar at app startup
+ ///
+ ///
+ public void SetServerRegistrar(IServerRegistrar serverRegistrar)
+ {
+ Value = serverRegistrar;
+ }
+
+ public IServerRegistrar Registrar
+ {
+ get { return Value; }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sync/ServerSync.cs b/src/Umbraco.Core/Sync/ServerSync.cs
deleted file mode 100644
index 49337c17cf..0000000000
--- a/src/Umbraco.Core/Sync/ServerSync.cs
+++ /dev/null
@@ -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 _addresses;
-
- public IEnumerable Registrations
- {
- get
- {
- if (_addresses == null)
- {
- _addresses = new List();
- 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 Registrations { get; }
- }
-
- internal class ServerRegistrarResolver : SingleObjectResolverBase
- {
-
- internal ServerRegistrarResolver(IServerRegistrar factory)
- : base(factory)
- {
- }
-
- public IServerRegistrar Registrar
- {
- get { return Value; }
- }
-
- }
-}
diff --git a/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs
new file mode 100644
index 0000000000..4df81e8968
--- /dev/null
+++ b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs
@@ -0,0 +1,154 @@
+using System.Web.Services;
+using Umbraco.Core.IO;
+
+namespace Umbraco.Core.Sync
+{
+
+ ///
+ /// The client Soap service for making distrubuted cache calls between servers
+ ///
+ [WebServiceBinding(Name = "CacheRefresherSoap", Namespace = "http://umbraco.org/webservices/")]
+ internal class ServerSyncWebServiceClient : System.Web.Services.Protocols.SoapHttpClientProtocol
+ {
+
+ ///
+ 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";
+
+ }
+
+ ///
+ [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});
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public void EndRefreshAll(System.IAsyncResult asyncResult)
+ {
+ this.EndInvoke(asyncResult);
+ }
+
+ ///
+ [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});
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public void EndRefreshByGuid(System.IAsyncResult asyncResult)
+ {
+ this.EndInvoke(asyncResult);
+ }
+
+ ///
+ [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});
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public void EndRefreshById(System.IAsyncResult asyncResult)
+ {
+ this.EndInvoke(asyncResult);
+ }
+
+ ///
+ [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});
+ }
+
+ ///
+ 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);
+ }
+
+ ///
+ public void EndRemoveById(System.IAsyncResult asyncResult)
+ {
+ this.EndInvoke(asyncResult);
+ }
+
+ ///
+ [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]));
+ }
+
+ ///
+ public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
+ {
+ return this.BeginInvoke("GetRefreshers", new object[] {
+ Login,
+ Password}, callback, asyncState);
+ }
+
+ ///
+ public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
+ {
+ object[] results = this.EndInvoke(asyncResult);
+ return ((System.Xml.XmlNode)(results[0]));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 780239f295..7420e3018a 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -73,6 +73,7 @@
True
..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll
+
True
..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll
@@ -667,7 +668,18 @@
-
+
+
+ Component
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Tests/Sync/ConfigServerRegistrarTests.cs b/src/Umbraco.Tests/Sync/ConfigServerRegistrarTests.cs
new file mode 100644
index 0000000000..c537c19072
--- /dev/null
+++ b/src/Umbraco.Tests/Sync/ConfigServerRegistrarTests.cs
@@ -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("127.0.0.1", "http://127.0.0.1")]
+ [TestCase("www.somedomain.com", "https://www.somedomain.com")]
+ [TestCase("another.domain.com.au", "http://another.domain.com.au:888")]
+ [TestCase("another.domain.com.au", "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(@"
+ 127.0.0.1
+ www.somedomain.com
+ another.domain.com.au
+ another.domain.com.au
+");
+ var xNode = xDoc.FirstChild;
+ var cReg = new ConfigServerRegistrar(xNode);
+
+ Assert.AreEqual(4, cReg.Registrations.Count());
+ }
+
+ }
+}
diff --git a/src/Umbraco.Tests/Sync/DistributedCacheTests.cs b/src/Umbraco.Tests/Sync/DistributedCacheTests.cs
new file mode 100644
index 0000000000..b2a6cca09e
--- /dev/null
+++ b/src/Umbraco.Tests/Sync/DistributedCacheTests.cs
@@ -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
+{
+ ///
+ /// Ensures that calls to DistributedCache methods carry through to the IServerMessenger correctly
+ ///
+ [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 IntIdsRefreshed = new List();
+ public List GuidIdsRefreshed = new List();
+ public List IntIdsRemoved = new List();
+ public int CountOfFullRefreshes = 0;
+
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds)
+ {
+ IntIdsRemoved.AddRange(numericIds);
+ }
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds)
+ {
+ IntIdsRefreshed.AddRange(numericIds);
+ }
+
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds)
+ {
+ GuidIdsRefreshed.AddRange(guidIds);
+ }
+
+ public void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher)
+ {
+ CountOfFullRefreshes++;
+ }
+ }
+
+ internal class TestServerRegistrar : IServerRegistrar
+ {
+ public IEnumerable Registrations
+ {
+ get
+ {
+ return new List()
+ {
+ new TestServerRegistration("localhost")
+ };
+ }
+ }
+ }
+
+ public class TestServerRegistration : IServerRegistration
+ {
+ public TestServerRegistration(string address)
+ {
+ ServerAddress = address;
+ }
+ public string ServerAddress { get; private set; }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 96438c48b2..8ca2e35506 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -261,6 +261,8 @@
+
+
diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config
index 1fafe2f61b..e1e02de465 100644
--- a/src/Umbraco.Web.UI/config/umbracoSettings.config
+++ b/src/Umbraco.Web.UI/config/umbracoSettings.config
@@ -150,14 +150,16 @@
-
-
-
- 0
-
-
-
-
+
+
+
+ 0
+
+
+
+ umblb1.dev
+ localhost
+
diff --git a/src/Umbraco.Web/Cache/CacheRefresherClient.cs b/src/Umbraco.Web/Cache/CacheRefresherClient.cs
index a382b3fd26..2943c8579c 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherClient.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherClient.cs
@@ -1,155 +1,155 @@
-using Umbraco.Core.IO;
+//using Umbraco.Core.IO;
-namespace Umbraco.Web.Cache
-{
+//namespace Umbraco.Web.Cache
+//{
- ///
- /// The client Soap service for making distrubuted cache calls between servers
- ///
- [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
- {
+// ///
+// /// The client Soap service for making distrubuted cache calls between servers
+// ///
+// [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
+// {
- ///
- 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";
+// ///
+// 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";
- }
+// }
- ///
- [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});
- }
+// ///
+// [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});
+// }
- ///
- 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);
- }
+// ///
+// 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);
+// }
- ///
- public void EndRefreshAll(System.IAsyncResult asyncResult)
- {
- this.EndInvoke(asyncResult);
- }
+// ///
+// public void EndRefreshAll(System.IAsyncResult asyncResult)
+// {
+// this.EndInvoke(asyncResult);
+// }
- ///
- [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});
- }
+// ///
+// [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});
+// }
- ///
- 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);
- }
+// ///
+// 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);
+// }
- ///
- public void EndRefreshByGuid(System.IAsyncResult asyncResult)
- {
- this.EndInvoke(asyncResult);
- }
+// ///
+// public void EndRefreshByGuid(System.IAsyncResult asyncResult)
+// {
+// this.EndInvoke(asyncResult);
+// }
- ///
- [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});
- }
+// ///
+// [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});
+// }
- ///
- 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);
- }
+// ///
+// 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);
+// }
- ///
- public void EndRefreshById(System.IAsyncResult asyncResult)
- {
- this.EndInvoke(asyncResult);
- }
+// ///
+// public void EndRefreshById(System.IAsyncResult asyncResult)
+// {
+// this.EndInvoke(asyncResult);
+// }
- ///
- [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});
- }
+// ///
+// [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});
+// }
- ///
- 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);
- }
+// ///
+// 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);
+// }
- ///
- public void EndRemoveById(System.IAsyncResult asyncResult)
- {
- this.EndInvoke(asyncResult);
- }
+// ///
+// public void EndRemoveById(System.IAsyncResult asyncResult)
+// {
+// this.EndInvoke(asyncResult);
+// }
- ///
- [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]));
- }
+// ///
+// [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]));
+// }
- ///
- public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
- {
- return this.BeginInvoke("GetRefreshers", new object[] {
- Login,
- Password}, callback, asyncState);
- }
+// ///
+// public System.IAsyncResult BeginGetRefreshers(string Login, string Password, System.AsyncCallback callback, object asyncState)
+// {
+// return this.BeginInvoke("GetRefreshers", new object[] {
+// Login,
+// Password}, callback, asyncState);
+// }
- ///
- public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
- {
- object[] results = this.EndInvoke(asyncResult);
- return ((System.Xml.XmlNode)(results[0]));
- }
- }
-}
\ No newline at end of file
+// ///
+// public System.Xml.XmlNode EndGetRefreshers(System.IAsyncResult asyncResult)
+// {
+// object[] results = this.EndInvoke(asyncResult);
+// return ((System.Xml.XmlNode)(results[0]));
+// }
+// }
+//}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs
index 670592b325..a02f3bb637 100644
--- a/src/Umbraco.Web/Cache/DistributedCache.cs
+++ b/src/Umbraco.Web/Cache/DistributedCache.cs
@@ -16,17 +16,13 @@ using umbraco.interfaces;
namespace Umbraco.Web.Cache
{
///
- /// 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
///
///
/// 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
///
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();
///
/// Constructor
///
private DistributedCache()
- {
- _login = User.GetUser(UmbracoSettings.DistributedCallUser).LoginName;
- _password = User.GetUser(UmbracoSettings.DistributedCallUser).GetPassword();
- _webServicesUrl = IOHelper.ResolveUrl(SystemDirectories.WebServices);
+ {
}
///
@@ -76,7 +66,10 @@ namespace Umbraco.Web.Cache
/// The id of the node.
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);
}
///
@@ -87,7 +80,10 @@ namespace Umbraco.Web.Cache
/// The guid of the node.
public void Refresh(Guid factoryGuid, Guid id)
{
- InvokeDispatchMethod(DispatchType.RefreshByGuid, factoryGuid, 0, id);
+ ServerMessengerResolver.Current.Messenger.PerformRefresh(
+ ServerRegistrarResolver.Current.Registrar.Registrations,
+ GetRefresherById(factoryGuid),
+ id);
}
///
@@ -97,7 +93,9 @@ namespace Umbraco.Web.Cache
/// The unique identifier.
public void RefreshAll(Guid factoryGuid)
{
- InvokeDispatchMethod(DispatchType.RefreshAll, factoryGuid, 0, Guid.Empty);
+ ServerMessengerResolver.Current.Messenger.PerformRefreshAll(
+ ServerRegistrarResolver.Current.Registrar.Registrations,
+ GetRefresherById(factoryGuid));
}
///
@@ -108,192 +106,10 @@ namespace Umbraco.Web.Cache
/// The id.
public void Remove(Guid factoryGuid, int id)
{
- InvokeDispatchMethod(DispatchType.RemoveById, factoryGuid, id, Guid.Empty);
- }
-
- ///
- /// Used to invoke the method on an ICacheRefresher instance if we are not currently using distributed calls.
- ///
- ///
- ///
- ///
- ///
- 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;
- }
- }
-
- ///
- /// Invokes the relevant dispatch method.
- ///
- /// Type of the dispatch.
- /// The factory GUID.
- /// The numeric id.
- /// The GUID id.
- 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("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();
-
- 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 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("Error refreshing distributed list", ee);
- }
-
- private void LogDispatchBatchResult(int errorCount)
- {
- LogHelper.Debug(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("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("Error refreshing a node in the distributed list, URI attempted: " + url, ex);
- }
-
- ///
- /// Sets the web service URL for a CacheRefresher from an XmlNode.
- ///
- /// The CacheRefresher.
- /// The XmlNode.
- 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("Submitting calls to distributed servers");
- }
-
- ///
- /// Gets the node list of DistributionServers from config.
- ///
- ///
- private XmlNodeList GetDistributedNodes()
- {
- return UmbracoSettings.DistributionServers.SelectNodes("./server");
- }
-
- private IAsyncResult[] GetAsyncResults(List asyncResultsList,
- out List waitHandlesList)
- {
- IAsyncResult[] asyncResults = asyncResultsList.ToArray();
- waitHandlesList = new List();
- foreach (IAsyncResult asyncResult in asyncResults)
- {
- waitHandlesList.Add(asyncResult.AsyncWaitHandle);
- }
- return asyncResults;
- }
-
- #region Nested type: DispatchType
-
- private enum DispatchType
- {
- RefreshAll,
- RefreshByNumericId,
- RefreshByGuid,
- RemoveById
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index 907a8efdb8..25c6d6fc16 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -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("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();
diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs
index dc6c1c704f..fd9a1e8c16 100644
--- a/src/UmbracoExamine/BaseUmbracoIndexer.cs
+++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs
@@ -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
///
///
- 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
diff --git a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs b/src/UmbracoExamine/DataServices/UmbracoMediaService.cs
index a324903c41..c1bd2c71d3 100644
--- a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs
+++ b/src/UmbracoExamine/DataServices/UmbracoMediaService.cs
@@ -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;
diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs
index 1fb1a19c69..7d3f22a187 100644
--- a/src/UmbracoExamine/UmbracoContentIndexer.cs
+++ b/src/UmbracoExamine/UmbracoContentIndexer.cs
@@ -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;
}
diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs
index b0fc1a54e9..a123bb4160 100644
--- a/src/UmbracoExamine/UmbracoExamineSearcher.cs
+++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs
@@ -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
///
///
- 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