2015-03-04 12:16:28 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2015-07-15 17:27:01 +02:00
|
|
|
using System.Linq;
|
2015-03-04 12:16:28 +01:00
|
|
|
using System.Web;
|
2015-07-15 17:27:01 +02:00
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using umbraco.interfaces;
|
2015-03-04 12:16:28 +01:00
|
|
|
using Umbraco.Core;
|
|
|
|
|
using Umbraco.Core.Logging;
|
2015-07-15 17:27:01 +02:00
|
|
|
using Umbraco.Core.Models.Rdbms;
|
2015-03-04 12:16:28 +01:00
|
|
|
using Umbraco.Core.Sync;
|
|
|
|
|
using Umbraco.Web.Routing;
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web
|
|
|
|
|
{
|
2015-03-27 10:25:25 +11:00
|
|
|
/// <summary>
|
|
|
|
|
/// An <see cref="IServerMessenger"/> implementation that works by storing messages in the database.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This binds to appropriate umbraco events in order to trigger the Boot(), Sync() & FlushBatch() calls
|
|
|
|
|
/// </remarks>
|
2015-07-15 17:27:01 +02:00
|
|
|
public class BatchedDatabaseServerMessenger : Core.Sync.DatabaseServerMessenger
|
2015-03-04 12:16:28 +01:00
|
|
|
{
|
|
|
|
|
public BatchedDatabaseServerMessenger(ApplicationContext appContext, bool enableDistCalls, DatabaseServerMessengerOptions options)
|
2015-03-27 10:16:49 +11:00
|
|
|
: base(appContext, enableDistCalls, options)
|
2015-03-04 12:16:28 +01:00
|
|
|
{
|
|
|
|
|
UmbracoApplicationBase.ApplicationStarted += Application_Started;
|
|
|
|
|
UmbracoModule.EndRequest += UmbracoModule_EndRequest;
|
|
|
|
|
UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Application_Started(object sender, EventArgs eventArgs)
|
|
|
|
|
{
|
2015-07-16 15:52:22 +02:00
|
|
|
if (ApplicationContext.IsConfigured == false
|
|
|
|
|
|| ApplicationContext.DatabaseContext.IsDatabaseConfigured == false
|
2015-03-04 12:16:28 +01:00
|
|
|
|| ApplicationContext.DatabaseContext.CanConnect == false)
|
2015-07-16 15:52:22 +02:00
|
|
|
{
|
2015-03-04 12:16:28 +01:00
|
|
|
|
2015-07-16 15:52:22 +02:00
|
|
|
LogHelper.Warn<BatchedDatabaseServerMessenger>("The app is not configured or cannot connect to the database, this server cannot be initialized with "
|
|
|
|
|
+ typeof (BatchedDatabaseServerMessenger) + ", distributed calls will not be enabled for this server");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Boot();
|
|
|
|
|
}
|
2015-03-04 12:16:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UmbracoModule_RouteAttempt(object sender, RoutableAttemptEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
switch (e.Outcome)
|
|
|
|
|
{
|
|
|
|
|
case EnsureRoutableOutcome.IsRoutable:
|
|
|
|
|
Sync();
|
|
|
|
|
break;
|
|
|
|
|
case EnsureRoutableOutcome.NotDocumentRequest:
|
|
|
|
|
//so it's not a document request, we'll check if it's a back office request
|
|
|
|
|
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
|
|
|
|
|
{
|
|
|
|
|
//it's a back office request, we should sync!
|
|
|
|
|
Sync();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
//case EnsureRoutableOutcome.NotReady:
|
|
|
|
|
//case EnsureRoutableOutcome.NotConfigured:
|
|
|
|
|
//case EnsureRoutableOutcome.NoContent:
|
|
|
|
|
//default:
|
|
|
|
|
// break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UmbracoModule_EndRequest(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
// will clear the batch - will remain in HttpContext though - that's ok
|
|
|
|
|
FlushBatch();
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-15 17:27:01 +02:00
|
|
|
protected override void DeliverRemote(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, MessageType messageType, IEnumerable<object> ids = null, string json = null)
|
|
|
|
|
{
|
|
|
|
|
var idsA = ids == null ? null : ids.ToArray();
|
|
|
|
|
|
|
|
|
|
Type arrayType;
|
|
|
|
|
if (GetArrayType(idsA, out arrayType) == false)
|
|
|
|
|
throw new ArgumentException("All items must be of the same type, either int or Guid.", "ids");
|
|
|
|
|
|
|
|
|
|
BatchMessage(servers, refresher, messageType, idsA, arrayType, json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void FlushBatch()
|
|
|
|
|
{
|
|
|
|
|
var batch = GetBatch(false);
|
|
|
|
|
if (batch == null) return;
|
|
|
|
|
|
|
|
|
|
var instructions = batch.SelectMany(x => x.Instructions).ToArray();
|
|
|
|
|
batch.Clear();
|
|
|
|
|
if (instructions.Length == 0) return;
|
|
|
|
|
|
|
|
|
|
var dto = new CacheInstructionDto
|
|
|
|
|
{
|
|
|
|
|
UtcStamp = DateTime.UtcNow,
|
|
|
|
|
Instructions = JsonConvert.SerializeObject(instructions, Formatting.None),
|
|
|
|
|
OriginIdentity = LocalIdentity
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ApplicationContext.DatabaseContext.Database.Insert(dto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected ICollection<RefreshInstructionEnvelope> GetBatch(bool ensureHttpContext)
|
2015-03-04 12:16:28 +01:00
|
|
|
{
|
|
|
|
|
var httpContext = UmbracoContext.Current == null ? null : UmbracoContext.Current.HttpContext;
|
|
|
|
|
if (httpContext == null)
|
2015-03-05 10:45:43 +01:00
|
|
|
{
|
2015-03-27 10:16:49 +11:00
|
|
|
if (ensureHttpContext)
|
2015-03-05 10:45:43 +01:00
|
|
|
throw new NotSupportedException("Cannot execute without a valid/current UmbracoContext with an HttpContext assigned.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2015-03-04 12:16:28 +01:00
|
|
|
|
|
|
|
|
var key = typeof (BatchedDatabaseServerMessenger).Name;
|
|
|
|
|
|
|
|
|
|
// no thread-safety here because it'll run in only 1 thread (request) at a time
|
|
|
|
|
var batch = (ICollection<RefreshInstructionEnvelope>)httpContext.Items[key];
|
2015-03-27 10:16:49 +11:00
|
|
|
if (batch == null && ensureHttpContext)
|
2015-03-04 12:16:28 +01:00
|
|
|
httpContext.Items[key] = batch = new List<RefreshInstructionEnvelope>();
|
|
|
|
|
return batch;
|
|
|
|
|
}
|
2015-07-15 17:27:01 +02:00
|
|
|
|
|
|
|
|
protected void BatchMessage(
|
|
|
|
|
IEnumerable<IServerAddress> servers,
|
|
|
|
|
ICacheRefresher refresher,
|
|
|
|
|
MessageType messageType,
|
|
|
|
|
IEnumerable<object> ids = null,
|
|
|
|
|
Type idType = null,
|
|
|
|
|
string json = null)
|
|
|
|
|
{
|
|
|
|
|
var batch = GetBatch(true);
|
|
|
|
|
if (batch == null)
|
|
|
|
|
throw new Exception("Failed to get a batch.");
|
|
|
|
|
|
|
|
|
|
batch.Add(new RefreshInstructionEnvelope(servers, refresher,
|
|
|
|
|
RefreshInstruction.GetInstructions(refresher, messageType, ids, idType, json)));
|
|
|
|
|
}
|
2015-03-04 12:16:28 +01:00
|
|
|
}
|
|
|
|
|
}
|