From 6c872e8a651eac478f338be7bfeffa3ca754fdd2 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 20 Jul 2012 23:10:43 +0600 Subject: [PATCH] Implements singly registered RoutesCache object and re-interfaces IRoutesCache which can now be set at runtime. Exposes UmbracoContext via DocumentRequest which simplifies some things. Removes dependency on UmbracoContext from DefaultRoutesCache, now InPreviewMode is checked before passing processing to the IRoutesCache provider. Changed NiceUrls to NiceUrlResolver. --- src/Umbraco.Web/ContentStore.cs | 9 +- .../{NiceUrls.cs => NiceUrlResolver.cs} | 24 +++- src/Umbraco.Web/Routing/DefaultRoutesCache.cs | 72 +++++++++++ src/Umbraco.Web/Routing/DocumentRequest.cs | 42 +++--- src/Umbraco.Web/Routing/IRoutesCache.cs | 11 ++ src/Umbraco.Web/Routing/LookupByPath.cs | 18 ++- .../Routing/LookupByPathWithTemplate.cs | 2 +- src/Umbraco.Web/Routing/LookupByProfile.cs | 2 +- src/Umbraco.Web/Routing/RoutesCache.cs | 120 +++++++----------- src/Umbraco.Web/Routing/RoutingEnvironment.cs | 17 +-- src/Umbraco.Web/Umbraco.Web.csproj | 4 +- src/Umbraco.Web/UmbracoContext.cs | 36 ++---- src/Umbraco.Web/UmbracoModule.cs | 16 ++- 13 files changed, 228 insertions(+), 145 deletions(-) rename src/Umbraco.Web/{NiceUrls.cs => NiceUrlResolver.cs} (86%) create mode 100644 src/Umbraco.Web/Routing/DefaultRoutesCache.cs create mode 100644 src/Umbraco.Web/Routing/IRoutesCache.cs diff --git a/src/Umbraco.Web/ContentStore.cs b/src/Umbraco.Web/ContentStore.cs index d123342f22..e13714c9ac 100644 --- a/src/Umbraco.Web/ContentStore.cs +++ b/src/Umbraco.Web/ContentStore.cs @@ -12,8 +12,15 @@ namespace Umbraco.Web /// internal class ContentStore { - private readonly UmbracoContext _umbracoContext; + /// + /// Delegate to return the current UmbracoContext + /// + private readonly UmbracoContext _umbracoContext; + /// + /// Constructor accepting a delegate to resolve the UmbracoContext + /// + /// public ContentStore(UmbracoContext umbracoContext) { _umbracoContext = umbracoContext; diff --git a/src/Umbraco.Web/NiceUrls.cs b/src/Umbraco.Web/NiceUrlResolver.cs similarity index 86% rename from src/Umbraco.Web/NiceUrls.cs rename to src/Umbraco.Web/NiceUrlResolver.cs index ae2ff6cf70..1e7da3b26e 100644 --- a/src/Umbraco.Web/NiceUrls.cs +++ b/src/Umbraco.Web/NiceUrlResolver.cs @@ -7,9 +7,12 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Web { - internal class NiceUrls + /// + /// Resolves NiceUrls for a given node id + /// + internal class NiceUrlResolver { - public NiceUrls(ContentStore contentStore, UmbracoContext umbracoContext, RoutesCache routesCache) + public NiceUrlResolver(ContentStore contentStore, UmbracoContext umbracoContext, IRoutesCache routesCache) { _umbracoContext = umbracoContext; _contentStore = contentStore; @@ -18,7 +21,7 @@ namespace Umbraco.Web private readonly UmbracoContext _umbracoContext; private readonly ContentStore _contentStore; - private readonly RoutesCache _routesCache; + private readonly IRoutesCache _routesCache; // note: this could be a parameter... const string UrlNameProperty = "@urlName"; @@ -34,10 +37,13 @@ namespace Umbraco.Web public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain) { - string route; - string path; + string path; + + // will not read cache if previewing! + var route = !_umbracoContext.InPreviewMode + ? _routesCache.GetRoute(nodeId) + : null; - route = _routesCache.GetRoute(nodeId); // will not read cache if previewing if (route != null) { int pos = route.IndexOf('/'); @@ -85,7 +91,11 @@ namespace Umbraco.Web parts.Reverse(); path = "/" + string.Join("/", parts); route = string.Format("{0}{1}", id, path); - _routesCache.Store(nodeId, route); // will not write if previewing + + if (!_umbracoContext.InPreviewMode) + { + _routesCache.Store(nodeId, route); // will not write if previewing + } return FormatUrl(domain, path); } diff --git a/src/Umbraco.Web/Routing/DefaultRoutesCache.cs b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs new file mode 100644 index 0000000000..a755e9c489 --- /dev/null +++ b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Web.Routing +{ + /// + /// The default implementation of IRoutesCache + /// + internal class DefaultRoutesCache : IRoutesCache + { + private readonly object _lock = new object(); + private Dictionary _routes; + private Dictionary _nodeIds; + + public DefaultRoutesCache() + { + Clear(); + + // here we should register handlers to clear the cache when content changes + // at the moment this is done by library, which clears everything when content changed + // + // but really, we should do some partial refreshes! + // otherwise, we could even cache 404 errors... + } + + public void Store(int nodeId, string route) + { + lock (_lock) + { + _routes[nodeId] = route; + _nodeIds[route] = nodeId; + } + } + + public string GetRoute(int nodeId) + { + lock (_lock) + { + return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; + } + } + + public int GetNodeId(string route) + { + lock (_lock) + { + return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; + } + } + + public void ClearNode(int nodeId) + { + lock (_lock) + { + if (_routes.ContainsKey(nodeId)) + { + _nodeIds.Remove(_routes[nodeId]); + _routes.Remove(nodeId); + } + } + } + + public void Clear() + { + lock (_lock) + { + _routes = new Dictionary(); + _nodeIds = new Dictionary(); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index efa44a1881..65844d84c8 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -16,12 +16,12 @@ namespace Umbraco.Web.Routing { static readonly TraceSource Trace = new TraceSource("DocumentRequest"); - public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrls niceUrls) + public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrlResolver niceUrlResolver) { // register lookups _environment = lookups; - _umbracoContext = umbracoContext; - _niceUrls = niceUrls; + UmbracoContext = umbracoContext; + _niceUrlResolver = niceUrlResolver; // prepare the host var host = uri.Host; @@ -55,13 +55,25 @@ namespace Umbraco.Web.Routing this.QueryString = uri.Query.TrimStart('?'); } + readonly RoutingEnvironment _environment; + private readonly NiceUrlResolver _niceUrlResolver; + + /// + /// the id of the requested node, if any, else zero. + /// + int _nodeId = 0; + + /// + /// the requested node, if any, else null. + /// + XmlNode _node = null; + #region Properties - // the id of the requested node, if any, else zero. - int _nodeId = 0; - - // the requested node, if any, else null. - XmlNode _node = null; + /// + /// Returns the current UmbracoContext + /// + public UmbracoContext UmbracoContext { get; private set; } /// /// Gets the request host name. @@ -176,15 +188,9 @@ namespace Umbraco.Web.Routing #region Resolve - readonly RoutingEnvironment _environment; - private readonly UmbracoContext _umbracoContext; - private readonly NiceUrls _niceUrls; - /// /// Determines the site root (if any) matching the http request. - /// - /// The host name part of the http request, eg. www.example.com. - /// The url part of the http request, starting with a slash, eg. /foo/bar. + /// /// A value indicating whether a domain was found. public bool ResolveSiteRoot() { @@ -432,9 +438,9 @@ namespace Umbraco.Web.Routing if (this.Node == null) throw new InvalidOperationException("There is no node."); - var templateAlias = _umbracoContext.HttpContext.Request.QueryString["altTemplate"]; + var templateAlias = UmbracoContext.HttpContext.Request.QueryString["altTemplate"]; if (string.IsNullOrWhiteSpace(templateAlias)) - templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"]; + templateAlias = UmbracoContext.HttpContext.Request.Form["altTemplate"]; // fixme - we might want to support cookies?!? NO but provide a hook to change the template @@ -488,7 +494,7 @@ namespace Umbraco.Web.Routing redirectId = -1; string redirectUrl = "#"; if (redirectId > 0) - redirectUrl = _niceUrls.GetNiceUrl(redirectId); + redirectUrl = _niceUrlResolver.GetNiceUrl(redirectId); if (redirectUrl != "#") this.RedirectUrl = redirectUrl; } diff --git a/src/Umbraco.Web/Routing/IRoutesCache.cs b/src/Umbraco.Web/Routing/IRoutesCache.cs new file mode 100644 index 0000000000..0f4a1044ec --- /dev/null +++ b/src/Umbraco.Web/Routing/IRoutesCache.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Web.Routing +{ + internal interface IRoutesCache + { + void Store(int nodeId, string route); + string GetRoute(int nodeId); + int GetNodeId(string route); + void ClearNode(int nodeId); + void Clear(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/LookupByPath.cs b/src/Umbraco.Web/Routing/LookupByPath.cs index d5e9dbba19..30dc31f546 100644 --- a/src/Umbraco.Web/Routing/LookupByPath.cs +++ b/src/Umbraco.Web/Routing/LookupByPath.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Routing [LookupWeight(10)] internal class LookupByPath : ILookup { - public LookupByPath(ContentStore contentStore, RoutesCache routesCache) + public LookupByPath(ContentStore contentStore, IRoutesCache routesCache) { ContentStore = contentStore; RoutesCache = routesCache; @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing static readonly TraceSource Trace = new TraceSource("LookupByPath"); protected ContentStore ContentStore; - protected RoutesCache RoutesCache; + protected IRoutesCache RoutesCache; public virtual bool LookupDocument(DocumentRequest docreq) { @@ -31,7 +31,12 @@ namespace Umbraco.Web.Routing { Trace.TraceInformation("Test route \"{0}\"", route); - var nodeId = RoutesCache.GetNodeId(route); + //return '0' if in preview mode! + var nodeId = !docreq.UmbracoContext.InPreviewMode + ? RoutesCache.GetNodeId(route) + : 0; + + XmlNode node = null; if (nodeId > 0) { @@ -55,7 +60,12 @@ namespace Umbraco.Web.Routing { docreq.Node = node; Trace.TraceInformation("Query matches, id={0}", docreq.NodeId); - RoutesCache.Store(docreq.NodeId, route); + + if (!docreq.UmbracoContext.InPreviewMode) + { + RoutesCache.Store(docreq.NodeId, route); // will not write if previewing + } + } else { diff --git a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs index 1b91be23aa..11454bcdb2 100644 --- a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs +++ b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing { static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate"); - public LookupByPathWithTemplate(ContentStore contentStore, RoutesCache routesCache) + public LookupByPathWithTemplate(ContentStore contentStore, IRoutesCache routesCache) : base(contentStore, routesCache) { } diff --git a/src/Umbraco.Web/Routing/LookupByProfile.cs b/src/Umbraco.Web/Routing/LookupByProfile.cs index a65eafac2e..c3044349e7 100644 --- a/src/Umbraco.Web/Routing/LookupByProfile.cs +++ b/src/Umbraco.Web/Routing/LookupByProfile.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing static readonly TraceSource Trace = new TraceSource("LookupByProfile"); - public LookupByProfile(ContentStore contentStore, RoutesCache routesCache, UmbracoContext umbracoContext) + public LookupByProfile(ContentStore contentStore, IRoutesCache routesCache, UmbracoContext umbracoContext) : base(contentStore, routesCache) { _umbracoContext = umbracoContext; diff --git a/src/Umbraco.Web/Routing/RoutesCache.cs b/src/Umbraco.Web/Routing/RoutesCache.cs index d366dc23f9..dcb7843e1b 100644 --- a/src/Umbraco.Web/Routing/RoutesCache.cs +++ b/src/Umbraco.Web/Routing/RoutesCache.cs @@ -1,81 +1,57 @@ -using System.Collections.Generic; +using System; namespace Umbraco.Web.Routing { - internal class RoutesCache - { - private readonly object _lock = new object(); - private Dictionary _routes; - private Dictionary _nodeIds; + /// + /// A singly registered object to assign and get the current IRoutesCache provider + /// + internal class RoutesCache + { + private static readonly RoutesCache Instance = new RoutesCache(); + private static IRoutesCache _provider; - private readonly UmbracoContext _umbracoContext; + /// + /// public contructor assigns the DefaultRoutesCache as the default provider + /// + public RoutesCache() + :this(null) + { + } - public RoutesCache(UmbracoContext umbracoContext) - { - _umbracoContext = umbracoContext; + /// + /// Constructor sets a custom provider if specified + /// + /// + internal RoutesCache(IRoutesCache provider) + { + _provider = provider ?? new DefaultRoutesCache(); + } - Clear(); + /// + /// Set the routes cache provider + /// + /// + public static void SetProvider(IRoutesCache provider) + { + if (provider == null) throw new ArgumentNullException("provider"); + _provider = provider; + } - // here we should register handlers to clear the cache when content changes - // at the moment this is done by library, which clears everything when content changed - // - // but really, we should do some partial refreshes! - // otherwise, we could even cache 404 errors... - } + /// + /// Singleton accessor + /// + public static RoutesCache Current + { + get { return Instance; } + } - public void Store(int nodeId, string route) - { - if (_umbracoContext.InPreviewMode) - return; - - lock (_lock) - { - _routes[nodeId] = route; - _nodeIds[route] = nodeId; - } - } - - public string GetRoute(int nodeId) - { - if (_umbracoContext.InPreviewMode) - return null; - - lock (_lock) - { - return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; - } - } - - public int GetNodeId(string route) - { - if (_umbracoContext.InPreviewMode) - return 0; - - lock (_lock) - { - return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; - } - } - - public void ClearNode(int nodeId) - { - lock (_lock) - { - if (_routes.ContainsKey(nodeId)) - { - _nodeIds.Remove(_routes[nodeId]); - _routes.Remove(nodeId); - } - } - } - - public void Clear() - { - lock (_lock) - { - _routes = new Dictionary(); - _nodeIds = new Dictionary(); - } - } - } + /// + /// Get the current provider + /// + /// + public IRoutesCache GetProvider() + { + return _provider; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutingEnvironment.cs b/src/Umbraco.Web/Routing/RoutingEnvironment.cs index cf848625be..0affeb6799 100644 --- a/src/Umbraco.Web/Routing/RoutingEnvironment.cs +++ b/src/Umbraco.Web/Routing/RoutingEnvironment.cs @@ -3,10 +3,11 @@ using System.Linq; namespace Umbraco.Web.Routing { - // represents a request for one specified Umbraco document to be rendered - // by one specified template, using one particular culture. - // - + + /// + /// represents a request for one specified Umbraco document to be rendered by one specified template, + /// using one particular culture. + /// internal class RoutingEnvironment { public RoutingEnvironment( @@ -28,13 +29,9 @@ namespace Umbraco.Web.Routing }).ToList(); } - public IEnumerable Lookups - { - get; - private set; - } + public IEnumerable Lookups { get; private set; } - public ILookupNotFound LookupNotFound { get; private set; } + public ILookupNotFound LookupNotFound { get; private set; } public ContentStore ContentStore { get; private set; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a9174e4cfb..b3ec0b677e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,11 +240,12 @@ - + + @@ -252,6 +253,7 @@ + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 4992fa3e07..6d6729b7c7 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web /// /// Gets the current Umbraco Context. /// - public static UmbracoContext Current + public static UmbracoContext Current { get { @@ -126,6 +126,16 @@ namespace Umbraco.Web /// internal DocumentRequest DocumentRequest { get; set; } + /// + /// Gets/sets the NiceUrlResolver object + /// + internal NiceUrlResolver NiceUrlResolver { get; set; } + + /// + /// Gets/sets the RoutingEnvironment object + /// + internal RoutingEnvironment RoutingEnvironment { get; set; } + /// /// Exposes the HttpContext for the current request /// @@ -193,29 +203,7 @@ namespace Umbraco.Web && !currentUrl.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)); // is not in admin UI } } - - /// - /// Gets the current Live Editing Context. - /// - public virtual ILiveEditingContext LiveEditingContext - { - get - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - var value = (ILiveEditingContext)HttpContext.Items["LiveEditingContext"]; - if (value == null) - { - LiveEditingContext = value = new DefaultLiveEditingContext(); - } - return value; - } - - set - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - HttpContext.Items["LiveEditingContext"] = value; - } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index d4b2ba0d35..1087e61fe3 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -37,11 +37,9 @@ namespace Umbraco.Web UmbracoContext.Current = umbracoContext; //create a content store - var contentStore = new ContentStore(umbracoContext); - //create the routes cache - var routesCache = new RoutesCache(umbracoContext); + var contentStore = new ContentStore(umbracoContext); //create the nice urls - var niceUrls = new NiceUrls(contentStore, umbracoContext, routesCache); + var niceUrls = new NiceUrlResolver(contentStore, umbracoContext, RoutesCache.Current.GetProvider()); //create the RoutingEnvironment (one per http request as it relies on the umbraco context!) var routingEnvironment = new RoutingEnvironment( ApplicationContext.Current.Plugins.ResolveLookups().ToArray(), @@ -49,9 +47,15 @@ namespace Umbraco.Web contentStore); // create the new document request which will cleanup the uri once and for all var docreq = new DocumentRequest(uri, routingEnvironment, umbracoContext, niceUrls); - - // initialize the document request on the UmbracoContext (this is circular dependency!!!) + + //NOTE: we are putting these objects on the UmbracoContext because these might be handy for developers in the future to + // access if we make them public. + // initialize the DocumentRequest on the UmbracoContext (this is circular dependency!!!) umbracoContext.DocumentRequest = docreq; + // initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!) + umbracoContext.RoutingEnvironment = routingEnvironment; + // initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!) + umbracoContext.NiceUrlResolver = niceUrls; //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!) var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext);