Adds DatabaseServerRegistrar and ServerRegistrationEventHandler, we can now ensure that all server add themselves to the

database table automatically.
This commit is contained in:
Shannon Deminick
2013-02-13 06:12:43 +06:00
parent 6423914f01
commit 8c97e367a2
12 changed files with 239 additions and 17 deletions

View File

@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Factories
public ServerRegistration BuildEntity(ServerRegistrationDto dto)
{
return new ServerRegistration(dto.Id, dto.ComputerName, dto.Address, dto.DateRegistered, dto.LastNotified);
return new ServerRegistration(dto.Id, dto.Address, dto.ComputerName, dto.DateRegistered, dto.LastNotified);
}
public ServerRegistrationDto BuildDto(ServerRegistration entity)

View File

@@ -20,6 +20,7 @@ namespace Umbraco.Core.Services
private Lazy<DataTypeService> _dataTypeService;
private Lazy<FileService> _fileService;
private Lazy<LocalizationService> _localizationService;
private Lazy<ServerRegistrationService> _serverRegistrationService;
/// <summary>
/// Constructor
@@ -47,6 +48,9 @@ namespace Umbraco.Core.Services
var provider = dbUnitOfWorkProvider;
var fileProvider = fileUnitOfWorkProvider;
if (_serverRegistrationService == null)
_serverRegistrationService = new Lazy<ServerRegistrationService>(() => new ServerRegistrationService(provider, repositoryFactory.Value));
if (_userService == null)
_userService = new Lazy<UserService>(() => new UserService(provider, repositoryFactory.Value));
@@ -72,6 +76,14 @@ namespace Umbraco.Core.Services
_localizationService = new Lazy<LocalizationService>(() => new LocalizationService(provider, repositoryFactory.Value));
}
/// <summary>
/// Gets the <see cref="ServerRegistrationService"/>
/// </summary>
internal ServerRegistrationService ServerRegistrationService
{
get { return _serverRegistrationService.Value; }
}
/// <summary>
/// Gets the <see cref="IContentService"/>
/// </summary>

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Xml;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
namespace Umbraco.Core.Sync
{

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
using Umbraco.Core.Services;
namespace Umbraco.Core.Sync
{
/// <summary>
/// A registrar that stores registered server nodes in a database
/// </summary>
internal class DatabaseServerRegistrar : IServerRegistrar
{
private readonly ServerRegistrationService _registrationService;
public DatabaseServerRegistrar(ServerRegistrationService registrationService)
{
_registrationService = registrationService;
}
public IEnumerable<IServerAddress> Registrations
{
get { return _registrationService.GetActiveServers(); }
}
}
}

View File

@@ -676,6 +676,7 @@
<Compile Include="Services\ServerRegistrationService.cs" />
<Compile Include="Services\ServiceContext.cs" />
<Compile Include="Services\UserService.cs" />
<Compile Include="Sync\DatabaseServerRegistrar.cs" />
<Compile Include="Sync\DefaultServerMessenger.cs" />
<Compile Include="Sync\ICacheRefresher.cs" />
<Compile Include="Sync\ServerSyncWebServiceClient.cs">

View File

@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="3" fileDependencyExtensions=".js,.css">
<clientDependency version="5" fileDependencyExtensions=".js,.css">
<fileRegistration defaultProvider="LoaderControlProvider">
<providers>

View File

@@ -0,0 +1,14 @@
namespace Umbraco.Web.Routing
{
/// <summary>
/// Reasons a request was not routable on the front-end
/// </summary>
internal enum EnsureRoutableOutcome
{
IsRoutable = 0,
NotDocumentRequest = 10,
NotReady = 11,
NotConfigured = 12,
NoContent = 13
}
}

View File

@@ -0,0 +1,18 @@
using System.Web;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Event args containing information about why the request was not routable, or if it is routable
/// </summary>
internal class RoutableAttemptEventArgs : UmbracoRequestEventArgs
{
public EnsureRoutableOutcome Outcome { get; private set; }
public RoutableAttemptEventArgs(EnsureRoutableOutcome reason, UmbracoContext umbracoContext, HttpContextBase httpContext)
: base(umbracoContext, httpContext)
{
Outcome = reason;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Web;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Event args used for event launched during a request (like in the UmbracoModule)
/// </summary>
internal class UmbracoRequestEventArgs : EventArgs
{
public UmbracoContext UmbracoContext { get; private set; }
public HttpContextBase HttpContext { get; private set; }
public UmbracoRequestEventArgs(UmbracoContext umbracoContext, HttpContextBase httpContext)
{
UmbracoContext = umbracoContext;
HttpContext = httpContext;
}
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Strategies
{
/// <summary>
/// This will ensure that the server is automatically registered in the database as an active node
/// on application startup and whenever a back office request occurs.
/// </summary>
/// <remarks>
/// We do this on app startup to ensure that the server is in the database but we also do it for the first 'x' times
/// a back office request is made so that we can tell if they are using https protocol which would update to that address
/// in the database. The first front-end request probably wouldn't be an https request.
///
/// For back office requests (so that we don't constantly make db calls), we'll only update the database when we detect at least
/// a timespan of 1 minute between requests.
/// </remarks>
public sealed class ServerRegistrationEventHandler : ApplicationEventHandler
{
private static bool _initUpdated = false;
private static DateTime _lastUpdated = DateTime.MinValue;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
/// <summary>
/// Update the database with this entry and bind to request events
/// </summary>
/// <param name="umbracoApplication"></param>
/// <param name="applicationContext"></param>
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//bind to event
UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt;
}
static void UmbracoModuleRouteAttempt(object sender, Routing.RoutableAttemptEventArgs e)
{
if (e.HttpContext.Request == null || e.HttpContext.Request.Url == null) return;
if (e.Outcome == EnsureRoutableOutcome.IsRoutable)
{
using (var lck = new UpgradeableReadLock(Locker))
{
//we only want to do the initial update once
if (!_initUpdated)
{
lck.UpgradeToWriteLock();
_initUpdated = true;
UpdateServerEntry(e.HttpContext, e.UmbracoContext.Application);
return;
}
}
}
//if it is not a document request, we'll check if it is a back end request
if (e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
{
var authority = e.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority);
var afterAuthority = e.HttpContext.Request.Url.GetLeftPart(UriPartial.Query)
.TrimStart(authority)
.TrimStart("/");
//check if this is in the umbraco back office
if (afterAuthority.InvariantStartsWith(GlobalSettings.Path.TrimStart("/")))
{
//yup it's a back office request!
using (var lck = new UpgradeableReadLock(Locker))
{
//we don't want to update if it's not been at least a minute since last time
var isItAMinute = DateTime.Now.Subtract(_lastUpdated).TotalSeconds >= 60;
if (isItAMinute)
{
lck.UpgradeToWriteLock();
_initUpdated = true;
_lastUpdated = DateTime.Now;
UpdateServerEntry(e.HttpContext, e.UmbracoContext.Application);
}
}
}
}
}
private static void UpdateServerEntry(HttpContextBase httpContext, ApplicationContext applicationContext)
{
try
{
var address = httpContext.Request.Url.GetLeftPart(UriPartial.Authority);
applicationContext.Services.ServerRegistrationService.EnsureActive(address);
}
catch (Exception e)
{
LogHelper.Error<ServerRegistrationEventHandler>("Failed to update server record in database.", e);
}
}
}
}

View File

@@ -351,7 +351,9 @@
<Compile Include="Routing\ContentFinderByPageIdQuery.cs" />
<Compile Include="Mvc\SurfaceControllerResolver.cs" />
<Compile Include="Routing\ContentFinderByNotFoundHandlers.cs" />
<Compile Include="Routing\RoutableAttemptEventArgs.cs" />
<Compile Include="Routing\NotFoundHandlerHelper.cs" />
<Compile Include="Routing\EnsureRoutableOutcome.cs" />
<Compile Include="Routing\PublishedContentRequestEngine.cs" />
<Compile Include="Search\ExamineEvents.cs" />
<Compile Include="Security\WebSecurity.cs" />
@@ -360,6 +362,7 @@
<Compile Include="Strategies\Publishing\UpdateCacheAfterPublish.cs" />
<Compile Include="Strategies\Publishing\UpdateCacheAfterUnPublish.cs" />
<Compile Include="Strategies\Migrations\EnsureAppsTreesUpdatedOnUpgrade.cs" />
<Compile Include="Strategies\ServerRegistrationEventHandler.cs" />
<Compile Include="Templates\TemplateRenderer.cs" />
<Compile Include="Templates\TemplateUtilities.cs" />
<Compile Include="Trees\PartialViewMacrosTree.cs" />
@@ -506,6 +509,7 @@
<Compile Include="umbraco.presentation\umbraco\nodeFactory\Node.cs" />
<Compile Include="umbraco.presentation\umbraco\nodeFactory\Nodes.cs" />
<Compile Include="umbraco.presentation\umbraco\nodeFactory\Property.cs" />
<Compile Include="Routing\UmbracoRequestEventArgs.cs" />
<Compile Include="UmbracoUserControl.cs">
<SubType>ASPXCodeBehind</SubType>
</Compile>

View File

@@ -94,8 +94,14 @@ namespace Umbraco.Web
}
// do not process if this request is not a front-end routable page
if (!EnsureUmbracoRoutablePage(umbracoContext, httpContext))
return;
var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext);
//raise event here
OnRouteAttempt(new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext, httpContext));
if (!isRoutableAttempt.Success)
{
return;
}
httpContext.Trace.Write("UmbracoModule", "Umbraco request confirmed");
@@ -148,7 +154,7 @@ namespace Umbraco.Web
#region Route helper methods
/// <summary>
/// This is a performance tweak to check if this is a .css, .js or .ico file request since
/// This is a performance tweak to check if this is a .css, .js or .ico, .jpg, .jpeg, .png, .gif file request since
/// .Net will pass these requests through to the module when in integrated mode.
/// We want to ignore all of these requests immediately.
/// </summary>
@@ -156,7 +162,7 @@ namespace Umbraco.Web
/// <returns></returns>
internal bool IsClientSideRequest(Uri url)
{
var toIgnore = new[] { ".js", ".css", ".ico" };
var toIgnore = new[] { ".js", ".css", ".ico", ".png", ".jpg", ".jpeg", ".gif" };
return toIgnore.Any(x => Path.GetExtension(url.LocalPath).InvariantEquals(x));
}
@@ -166,24 +172,34 @@ namespace Umbraco.Web
/// <param name="context"></param>
/// <param name="httpContext"></param>
/// <returns></returns>
internal bool EnsureUmbracoRoutablePage(UmbracoContext context, HttpContextBase httpContext)
internal Attempt<EnsureRoutableOutcome> EnsureUmbracoRoutablePage(UmbracoContext context, HttpContextBase httpContext)
{
var uri = context.OriginalRequestUrl;
var reason = EnsureRoutableOutcome.IsRoutable;;
// ensure this is a document request
if (!EnsureDocumentRequest(httpContext, uri))
return false;
{
reason = EnsureRoutableOutcome.NotDocumentRequest;
}
// ensure Umbraco is ready to serve documents
if (!EnsureIsReady(httpContext, uri))
return false;
else if (!EnsureIsReady(httpContext, uri))
{
reason = EnsureRoutableOutcome.NotReady;
}
// ensure Umbraco is properly configured to serve documents
if (!EnsureIsConfigured(httpContext, uri))
return false;
else if (!EnsureIsConfigured(httpContext, uri))
{
reason = EnsureRoutableOutcome.NotConfigured;
}
// ensure Umbraco has documents to serve
if (!EnsureHasContent(context, httpContext))
return false;
else if (!EnsureHasContent(context, httpContext))
{
reason = EnsureRoutableOutcome.NoContent;
}
return true;
return new Attempt<EnsureRoutableOutcome>(reason == EnsureRoutableOutcome.IsRoutable, reason);
}
/// <summary>
@@ -318,8 +334,7 @@ namespace Umbraco.Web
/// Rewrites to the correct Umbraco handler, either WebForms or Mvc
/// </summary>
/// <param name="context"></param>
/// <param name="currentQuery"></param>
/// <param name="engine"> </param>
/// <param name="pcr"> </param>
private void RewriteToUmbracoHandler(HttpContextBase context, PublishedContentRequest pcr)
{
// NOTE: we do not want to use TransferRequest even though many docs say it is better with IIS7, turns out this is
@@ -432,5 +447,14 @@ namespace Umbraco.Web
i.DisposeIfDisposable();
}
}
#region Events
internal static event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
private void OnRouteAttempt(RoutableAttemptEventArgs args)
{
if (RouteAttempt != null)
RouteAttempt(this, args);
}
#endregion
}
}