Beispiel #1
0
        /// <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;
                }
            }));
        }