/// <summary> /// Finds a TypedEntity based on the Uri /// </summary> /// <param name="fullUrlIncludingDomain"></param> /// <param name="revisionStatusType"></param> /// <returns></returns> public EntityRouteResult FindEntityByUrl(Uri fullUrlIncludingDomain, RevisionStatusType revisionStatusType = null) { Mandate.ParameterNotNull(fullUrlIncludingDomain.Scheme, "Scheme"); var statusTypeCacheKey = (revisionStatusType != null) ? revisionStatusType.Alias : string.Empty; //cache key is full uri except query string and revision status var cacheKey = EntityMappedKey + "-" + fullUrlIncludingDomain.GetLeftPart(UriPartial.Path) + "-" + statusTypeCacheKey; //TODO: We need to change how the NiceUrls are cached because if we store them in one entry as a dictionary in cache, then // we can do a reverse lookup to see if we've already generated the URL for the entity which may match the fullUrlIncludingDomain, // if so, then all we have to do is return the entity with the cached ID. return(ApplicationContext.FrameworkContext.ApplicationCache .GetOrCreate(cacheKey, () => { using (DisposableTimer.Start(timer => LogHelper.TraceIfEnabled <DefaultRoutingEngine>("FindEntityByUrl (not from cache) for URL {0} took {1}ms", () => fullUrlIncludingDomain, () => timer))) { ReadonlyGroupUnitFactory <IContentStore> persistenceManager; using (DisposableTimer.TraceDuration <DefaultRoutingEngine>("FindEntityByUrl: Getting a reader", "FindEntityByUrl: Got a reader")) persistenceManager = ApplicationContext.Hive.GetReader <IContentStore>(fullUrlIncludingDomain); if (persistenceManager != null) { IReadonlyGroupUnit <IContentStore> readonlyGroupUnit; using (DisposableTimer.TraceDuration <DefaultRoutingEngine>("FindEntityByUrl: Opening a reader", "FindEntityByUrl: Opened a reader")) readonlyGroupUnit = persistenceManager.CreateReadonly(); using (var uow = readonlyGroupUnit) { //first, lets check if it's an ID URL var trimmedAppPath = _httpContext.Request.ApplicationPath.TrimEnd('/'); var appPathLength = trimmedAppPath.Length; // gate-check for the incoming url because some code (e.g. the alt-template checker) could be unaware of vdirs etc. var absolutePath = fullUrlIncludingDomain.AbsolutePath; var path = (absolutePath.Length < appPathLength) ? absolutePath : absolutePath.Substring(appPathLength, absolutePath.Length - appPathLength); var urlId = HiveId.TryParse(path.TrimStart('/')); if (urlId.Success && urlId.Result.ProviderGroupRoot != null) { LogHelper.TraceIfEnabled <DefaultRoutingEngine>("In FindEntityByUrl: Resolving entity by Id URL (Id: {0} ", () => urlId.Result.ToFriendlyString()); try { //var entityById = uow.Repositories.Revisions.GetLatestRevision<TypedEntity>(urlId.Result, revisionStatusType); var entityById = uow.Repositories.OfRevisionType(revisionStatusType).InIds(urlId.Result.AsEnumerableOfOne()).FirstOrDefault(); if (entityById == null) { LogHelper.Warn <DefaultRoutingEngine>("In FindEntityByUrl: Resolving entity by Id URL failed (Id: {0} ", urlId.Result.ToFriendlyString()); return null; } return new HttpRuntimeCacheParameters <EntityRouteResult>(new EntityRouteResult(entityById, EntityRouteStatus.SuccessById)); } catch (ArgumentException) { //this occurs if the Id parsed but 'not really' return null; } } TypedEntity lastItemFound; //is the current requesting hostname/port in our list ? if (DomainList.ContainsHostname(fullUrlIncludingDomain.Authority)) { //domain found so get the first item assigned to this domain LogHelper.TraceIfEnabled <DefaultRoutingEngine>("In FindEntityByUrl: Resolving entity by Domain URL {0}", () => fullUrlIncludingDomain.Authority); var hostnameEntry = DomainList[fullUrlIncludingDomain.Authority]; //lastRevisionFound = uow.Repositories.Revisions.GetLatestRevision<TypedEntity>(hostnameEntry.ContentId, revisionStatusType); lastItemFound = uow.Repositories.OfRevisionType(revisionStatusType).InIds(hostnameEntry.ContentId.AsEnumerableOfOne()).FirstOrDefault(); Mandate.That(lastItemFound != null, x => new InvalidOperationException("Could not find an entity with a revision status of '" + revisionStatusType.Alias + "', having a hostname '" + fullUrlIncludingDomain.Authority + "' and id: " + hostnameEntry.ContentId)); } else { //no domain found for the current request, so we need to find the first routable node that doesn't require a domain LogHelper.TraceIfEnabled <DefaultRoutingEngine>("In FindEntityByUrl: Resolving entity by Non-Domain URL"); //var root = uow.Repositories.Revisions.GetLatestRevision<TypedEntity>(FixedHiveIds.ContentVirtualRoot, revisionStatusType); //Mandate.That(root != null, x => new InvalidOperationException("Could not find the content root")); var domainListIds = DomainList.Select(d => d.ContentId).ToArray(); var firstLevelRelations = uow.Repositories.GetChildRelations(FixedHiveIds.ContentVirtualRoot, FixedRelationTypes.DefaultRelationType).OrderBy( x => x.Ordinal).ToArray(); //try to find a first level node that doesn't exist in our domain list var firstNonHostnameEntity = firstLevelRelations.FirstOrDefault(x => !domainListIds.Contains(x.DestinationId)); // Issue #U5-112 // If we have found no entities that are NOT assigned to a domain, given that we have already tried to find // content matching the current request's domain, we cannot route any content, therefore return null // Also return null if there is no content if (firstNonHostnameEntity == null || !firstLevelRelations.Any()) { return null; } var idToUse = firstNonHostnameEntity.DestinationId; using (DisposableTimer.TraceDuration <DefaultRoutingEngine>("FindEntityByUrl: Querying for " + idToUse, "FindEntityByUrl: Query")) lastItemFound = uow.Repositories.OfRevisionType(revisionStatusType).InIds(idToUse.AsEnumerableOfOne()).FirstOrDefault(); ////if there is no first level node anywhere, then there is no content. Show a friendly message //if (firstNonHostnameEntity == null && !firstLevelRelations.Any()) // return null; ////if we have a first level node not assigned to a domain, use the first, otherwise if all nodes are assigned to domains, then just use the first //var idToUse = firstNonHostnameEntity == null ? firstLevelRelations.First().DestinationId : firstNonHostnameEntity.DestinationId; //lastItemFound = uow.Repositories.OfRevisionType(revisionStatusType).InIds(idToUse.AsEnumerableOfOne()).FirstOrDefault(); } // Now we will have the path from the current application root like: // /this/is/a/path/to/a/document // Now we need to walk down the tree if (lastItemFound != null && !string.IsNullOrWhiteSpace(path) && path != "/") { using (DisposableTimer.TraceDuration <DefaultRoutingEngine>("FindEntityByUrl: Calling GetEntityByPath for " + lastItemFound.Id + " " + path, "FindEntityByUrl: GetEntityByPath")) lastItemFound = uow .Repositories .GetEntityByPath <TypedEntity>(lastItemFound.Id, path, revisionStatusType, true); } if (lastItemFound == null) { return new HttpRuntimeCacheParameters <EntityRouteResult>( new EntityRouteResult(null, EntityRouteStatus.FailedNotFoundByName)); } return new HttpRuntimeCacheParameters <EntityRouteResult>( new EntityRouteResult(lastItemFound, EntityRouteStatus.SuccessWithoutHostname)); } } return null; } })); }
/// <summary> /// Finds a TypedEntity based on the Uri /// </summary> /// <param name="fullUrlIncludingDomain"></param> /// <param name="revisionStatusType"></param> /// <returns></returns> public EntityRouteResult FindEntityByUrl(Uri fullUrlIncludingDomain, RevisionStatusType revisionStatusType) { Mandate.ParameterNotNull(fullUrlIncludingDomain.Scheme, "Scheme"); //cache key is full uri except query string and revision status var cacheKey = EntityMappedKey + "-" + fullUrlIncludingDomain.GetLeftPart(UriPartial.Path) + "-" + revisionStatusType; //TODO: We need to change how the NiceUrls are cached because if we store them in one entry as a dictionary in cache, then // we can do a reverse lookup to see if we've already generated the URL for the entity which may match the fullUrlIncludingDomain, // if so, then all we have to do is return the entity with the cached ID. return(ApplicationContext.FrameworkContext.ApplicationCache .GetOrCreate(cacheKey, () => { using (DisposableTimer.Start(timer => LogHelper.TraceIfEnabled <DefaultRoutingEngine>("FindEntityByUrl for URL {0} took {1}ms", () => fullUrlIncludingDomain, () => timer))) { var persistenceManager = ApplicationContext.Hive.GetReader <IContentStore>(fullUrlIncludingDomain); if (persistenceManager != null) { using (var uow = persistenceManager.CreateReadonly()) { //first, lets check if it's an ID URL var path = fullUrlIncludingDomain.AbsolutePath.Substring( _httpContext.Request.ApplicationPath.TrimEnd('/').Length, fullUrlIncludingDomain.AbsolutePath.Length - _httpContext.Request.ApplicationPath.TrimEnd('/').Length); var urlId = HiveId.TryParse(path.TrimStart('/')); if (urlId.Success && urlId.Result.ProviderGroupRoot != null) { LogHelper.TraceIfEnabled <DefaultRoutingEngine>("Resolving entity by Id URL (Id: {0} ", () => urlId.Result.ToFriendlyString()); try { var entityById = uow.Repositories.Revisions.GetLatestRevision <TypedEntity>(urlId.Result, revisionStatusType); if (entityById == null) { LogHelper.Warn <DefaultRoutingEngine>("Resolving entity by Id URL failed (Id: {0} ", urlId.Result.ToFriendlyString()); return null; } return new HttpRuntimeCacheParameters <EntityRouteResult>( new EntityRouteResult(entityById.Item, EntityRouteStatus.SuccessById)); } catch (ArgumentException) { //this occurs if the Id parsed but 'not really' return null; } } Revision <TypedEntity> lastRevisionFound; TypedEntity lastItemFound; //is the current requesting hostname/port in our list ? if (DomainList.ContainsHostname(fullUrlIncludingDomain.Authority)) { //domain found so get the first item assigned to this domain LogHelper.TraceIfEnabled <DefaultRoutingEngine>("Resolving entity by Domain URL"); var hostnameEntry = DomainList[fullUrlIncludingDomain.Authority]; lastRevisionFound = uow.Repositories.Revisions.GetLatestRevision <TypedEntity>(hostnameEntry.ContentId, revisionStatusType); Mandate.That(lastRevisionFound != null, x => new InvalidOperationException("Could not find an entity with a revision status of '" + revisionStatusType.Alias + "', having a hostname '" + fullUrlIncludingDomain.Authority + "' and id: " + hostnameEntry.ContentId)); lastItemFound = lastRevisionFound.Item; } else { //no domain found for the current request, so we need to find the first routable node that doesn't require a domain LogHelper.TraceIfEnabled <DefaultRoutingEngine>("Resolving entity by Non-Domain URL"); //var root = uow.Repositories.Revisions.GetLatestRevision<TypedEntity>(FixedHiveIds.ContentVirtualRoot, revisionStatusType); //Mandate.That(root != null, x => new InvalidOperationException("Could not find the content root")); var domainListIds = DomainList.Select(d => d.ContentId); var firstLevelRelations = uow.Repositories.GetChildRelations(FixedHiveIds.ContentVirtualRoot, FixedRelationTypes.DefaultRelationType).OrderBy( x => x.Ordinal).ToArray(); //try to find a first level node that doesn't exist in our domain list var firstNonHostnameEntity = firstLevelRelations.FirstOrDefault(x => !domainListIds.Contains(x.DestinationId)); //if there is no first level node anywhere, then there is no content. Show a friendly message if (firstNonHostnameEntity == null && firstLevelRelations.Count() == 0) { return null; } //if we have a first level node not assigned to a domain, use the first, otherwise if all nodes are assigned to domains, then just use the first lastRevisionFound = uow.Repositories.Revisions.GetLatestRevision <TypedEntity>( firstNonHostnameEntity == null ? firstLevelRelations.First().DestinationId : firstNonHostnameEntity.DestinationId, revisionStatusType); lastItemFound = lastRevisionFound != null ? lastRevisionFound.Item : null; } // Now we will have the path from the current application root like: // /this/is/a/path/to/a/document // Now we need to walk down the tree if (lastItemFound != null && !string.IsNullOrWhiteSpace(path) && path != "/") { lastItemFound = uow.Repositories.GetEntityByPath <TypedEntity>(lastItemFound.Id, path, revisionStatusType, true); } if (lastItemFound == null) { return new HttpRuntimeCacheParameters <EntityRouteResult>( new EntityRouteResult(null, EntityRouteStatus.FailedNotFoundByName)); } return new HttpRuntimeCacheParameters <EntityRouteResult>( new EntityRouteResult(lastItemFound, EntityRouteStatus.SuccessWithoutHostname)); } } return null; } })); }