Example #1
0
        public List <ContentAddress> Search(string search, List <FieldSearch> nonTextualMatches)
        {
            string    indexFileLocation = GetIndexFilePath();
            Directory dir =
                Lucene.Net.Store.FSDirectory.GetDirectory(indexFileLocation);
            Analyzer      analyzer = new StandardAnalyzer();
            IndexSearcher searcher = new IndexSearcher(dir);

            try
            {
                QueryParser  parser = new QueryParser("_GLOM_", analyzer);
                BooleanQuery query  = new BooleanQuery();
                if (!string.IsNullOrEmpty(search))
                {
                    Query textQuery = parser.Parse(search);
                    query.Add(textQuery, BooleanClause.Occur.MUST);
                }
                foreach (var match in nonTextualMatches)
                {
                    query.Add(new TermQuery(new Term(match.FieldName, match.Value)), match.Operator == FieldSearchOperator.ANDNOT ? BooleanClause.Occur.MUST_NOT : BooleanClause.Occur.MUST);
                }
                TopDocs hits = searcher.Search(query, 1000);
                List <ContentAddress> addrs =
                    Enumerable.Range(0, hits.totalHits)
                    .Select(n => ContentAddress.FromString(searcher.Doc(hits.scoreDocs[n].doc).Get("_CONTENTADDRESS_")))
                    .ToList();
                return(addrs);
            }
            finally
            {
                searcher.Close();
                analyzer.Close();
                dir.Close();
            }
        }
Example #2
0
        public string GetUrl(ContentAddress ca)
        {
            RouteValueDictionary rvs = new RouteValueDictionary();

            rvs.Add("controller", ca.Controller);
            rvs.Add("action", ca.Action);
            ControllerInfo cInfo = GetController((string)rvs["controller"]);;

            if (cInfo == null)
            {
                throw new MissingControllerException("Can't find controller " + ca.Controller);
            }

            for (int i = 0; i < Math.Min(cInfo.SignificantRouteKeys.Count, ca.Subindexes.Count); i++)
            {
                rvs.Add(cInfo.SignificantRouteKeys[i], ca.Subindexes[i]);
            }

            UrlPattern patt = cInfo.UrlPatterns.FirstOrDefault(up => up.Matches(rvs));

            if (patt == null)
            {
                throw new StructureException("No matching pattern found on controller " + ca.Controller);
            }
            return(patt.BuildUrl(rvs));
        }
Example #3
0
        public List <ContentAddress> Search(string textualSearch, string nonTextualSearch)
        {
            string    indexFileLocation = GetIndexFilePath();
            Directory dir =
                Lucene.Net.Store.FSDirectory.GetDirectory(indexFileLocation);
            IndexSearcher searcher       = new IndexSearcher(dir);
            Analyzer      textualAnal    = null;
            Analyzer      nonTextualAnal = null;

            try
            {
                Query textualQuery    = null;
                Query nonTextualQuery = null;

                if (!string.IsNullOrEmpty(textualSearch))
                {
                    textualAnal = new StandardAnalyzer();
                    QueryParser parser = new QueryParser("_GLOM_", textualAnal);
                    textualQuery = parser.Parse(textualSearch);
                }
                if (!string.IsNullOrEmpty(nonTextualSearch))
                {
                    nonTextualAnal = new KeywordAnalyzer();
                    QueryParser parser = new QueryParser("", nonTextualAnal);
                    nonTextualQuery = parser.Parse(nonTextualSearch);
                }

                Query query = textualQuery;
                if (query == null)
                {
                    query = nonTextualQuery;
                }
                else if (nonTextualQuery != null)
                {
                    query = new BooleanQuery();
                    (query as BooleanQuery).Add(textualQuery, BooleanClause.Occur.MUST);
                    (query as BooleanQuery).Add(nonTextualQuery, BooleanClause.Occur.MUST);
                }

                TopDocs hits = searcher.Search(query, 1000);
                List <ContentAddress> addrs =
                    Enumerable.Range(0, hits.totalHits)
                    .Select(n => ContentAddress.FromString(searcher.Doc(hits.scoreDocs[n].doc).Get("_CONTENTADDRESS_")))
                    .ToList();
                return(addrs);
            }
            finally
            {
                searcher.Close();
                if (textualAnal != null)
                {
                    textualAnal.Close();
                }
                if (nonTextualAnal != null)
                {
                    nonTextualAnal.Close();
                }
                dir.Close();
            }
        }
Example #4
0
 public static ContentItem GetContentForAddress(ContentAddress ca)
 {
     ContentItem contentItem = RequestContent;
     if (contentItem != null && contentItem.ContentAddress == ca)
         return contentItem;
     else
         return null;
 }
        public async Task RunnerActivity(
            [ActivityTrigger] ContextDTO contextDTO,
            ILogger log)
        {
            Resource       resource = contextDTO.resource;
            ContentAddress source   = contextDTO.source;
            ContentAddress target   = contextDTO.target;

            try
            {
                string q1      = $"SELECT * FROM c WHERE c.id='{resource.Id}'";
                var    resTemp = await repoResource.GetAsync(sqlQuery : q1);

                var res = resTemp.Items.FirstOrDefault();

                if (!string.IsNullOrEmpty(res.Status))
                {
                    return;
                }

                resource.Status = "processing";
                await repoResource.UpsertAsync(resource.Id, resource);

                if (source != null && target != null)
                {
                    await _storageHandler.CopyData(source.ContainerName, source.BlobName, target.ContainerName, target.BlobName);
                }

                resource.Status        = "publish";
                resource.ProcessedDate = DateTime.UtcNow;

                //await UpdateOtherData(resource, log);
                await repoResource.UpsertAsync(resource.Id, resource);
            }
            catch (PendingException ex)
            {
                resource.Status            = "pending";
                resource.StatusDescription = $"{ex.Message}\r\n{ex.InnerException?.Message}";
                resource.ProcessedDate     = DateTime.UtcNow;

                //await UpdateOtherData(resource, log);
                await repoResource.UpsertAsync(resource.Id, resource);
            }
            catch (Exception ex)
            {
                resource.Status            = "error";
                resource.StatusDescription = $"{ex.Message}\r\n{ex.InnerException?.Message}";
                resource.ProcessedDate     = DateTime.UtcNow;
                resource.ContentAddress    = null;

                //await UpdateOtherData(resource, log);
                await repoResource.UpsertAsync(resource.Id, resource);

                throw ex;
            }
        }
        public virtual ContentItem GetContent(ContentAddress primaryAddress)
        {
            ContentItem contentItem = ContentRepository.Instance.GetContentItem(primaryAddress);

            if (contentItem != null)
            {
                Type contentType = L24Manager.ControllerAssembly.GetType(contentItem.Type);
                contentItem = GetContent(primaryAddress, contentItem, contentType);
            }
            return(contentItem);
        }
        public async Task UpdatePublishResource(Resource resource)
        {
            var ca = new ContentAddress(resource.ContentAddress);

            if (await _storageHandler.FileIsExists(ca.ContainerName, ca.BlobName))
            {
                resource.Status            = "publish";
                resource.StatusDescription = null;

                //await UpdateOtherData(resource);
                await repoResource.UpsertAsync(resource.Id, resource);
            }
        }
Example #8
0
        public static ContentItem GetContentForAddress(ContentAddress ca)
        {
            ContentItem contentItem = RequestContent;

            if (contentItem != null && contentItem.ContentAddress == ca)
            {
                return(contentItem);
            }
            else
            {
                return(null);
            }
        }
Example #9
0
        public async Task SaveVideo(Models.Resource resource, Uri uri)
        {
            try
            {
                var client = await GetClient();

                var credential = _mediaCredential.Value;

                var contentAddress = new ContentAddress(resource.ContentAddress);
                var sourceName     = $"raw-{contentAddress.ContainerName}";

                Asset asset = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, sourceName);

                if (asset != null)
                {
                    await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, sourceName);

                    await Task.Delay(TimeSpan.FromSeconds(60));
                }
                //do
                //{
                //    asset = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, sourceName);
                //} while (asset != null);

                asset = await client.Assets.CreateOrUpdateAsync(credential.ResourceGroup, credential.AccountName,
                                                                sourceName, new Asset(name : sourceName, container : sourceName));

                var response = await client.Assets.ListContainerSasAsync(
                    credential.ResourceGroup,
                    credential.AccountName,
                    asset.Name,
                    permissions : AssetContainerPermission.ReadWrite,
                    expiryTime : DateTime.UtcNow.AddHours(4).ToUniversalTime());

                var sasUri = new Uri(response.AssetContainerSasUrls.First());
                CloudBlobContainer container = new CloudBlobContainer(sasUri);
                var blob = container.GetBlockBlobReference(Path.GetFileName(resource.Filename));

                // Use Strorage API to upload the file into the container in storage.
                await blob.StartCopyAsync(uri);
            }
            catch (Exception ex)
            {
                if (ex.Message.ToLower().Contains("pending"))
                {
                    throw new PendingException($"Pending copying {uri}");
                }
                throw ex;
            }
        }
Example #10
0
        public async Task <Job> GetJob(Models.Resource resource)
        {
            var client = await GetClient();

            var credential = _mediaCredential.Value;

            var    contentAddress = new ContentAddress(resource.ContentAddress);
            string jobName        = $"job-{contentAddress.ContainerName}";

            var job = await client.Jobs.GetAsync(
                credential.ResourceGroup,
                credential.AccountName,
                TRANSFORM_NAME,
                jobName);

            return(job);
        }
        protected virtual ContentItem GetContent(ContentAddress primaryAddress, ContentItem primaryItem, Type contentType)
        {
            var rpsAttributes = contentType
                                .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false)
                                .Cast <RedirectPropertySourceAttribute>()
                                .ToList();
            List <ContentAddress> addresses = new List <ContentAddress>();

            if (primaryItem == null)
            {
                addresses.Add(primaryAddress);
            }
            addresses.AddRange(rpsAttributes
                               .Select(a => primaryAddress.Redirect(a.SourceDescriptor))
                               .Distinct());
            List <ContentItem> items = ContentRepository.Instance.GetContentItems(addresses);

            if (primaryItem == null)
            {
                primaryItem = items.First(ci => ci.ContentAddress == primaryAddress);
            }
            if (rpsAttributes.Any())
            {
                primaryItem.JObjectContent = JObject.Parse(primaryItem.Content);
                foreach (var rpsAttribute in rpsAttributes)
                {
                    ContentAddress refdAddress = primaryAddress.Redirect(rpsAttribute.SourceDescriptor);
                    if (refdAddress == primaryAddress) // redirected to itself
                    {
                        continue;
                    }
                    ContentItem refdItem = items.FirstOrDefault(ci => ci.ContentAddress == refdAddress);
                    if (refdItem != null)
                    {
                        foreach (string path in rpsAttribute.Paths)
                        {
                            UpdateJObjectForPathSource(primaryItem.JObjectContent, path, refdItem);
                        }
                    }
                }
            }

            return(primaryItem);
        }
        public virtual void SetContent <T>(ContentItem contentItem, T content) where T : BaseContent
        {
            var rpsAttributes = typeof(T)
                                .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false)
                                .Cast <RedirectPropertySourceAttribute>()
                                .Where(rpsa => !rpsa.ReadOnly)
                                .ToList();
            List <ContentAddress> addresses = rpsAttributes
                                              .Select(a => contentItem.ContentAddress.Redirect(a.SourceDescriptor))
                                              .Distinct()
                                              .ToList();
            List <ContentItem> items = ContentRepository.Instance.GetContentItems(addresses);

            JObject jObjectContent = JObject.FromObject(content);

            if (rpsAttributes.Any())
            {
                foreach (var rpsAttribute in rpsAttributes)
                {
                    ContentAddress refdAddress = contentItem.ContentAddress.Redirect(rpsAttribute.SourceDescriptor);
                    if (refdAddress == contentItem.ContentAddress) // redirected to itself
                    {
                        continue;
                    }
                    ContentItem refdItem = items.FirstOrDefault(ci => ci.ContentAddress == refdAddress);
                    if (refdItem != null)
                    {
                        foreach (string path in rpsAttribute.Paths)
                        {
                            UpdateItemForPathSource(refdItem, path, jObjectContent);
                        }
                    }
                }
            }

            contentItem.SetContent <T>(jObjectContent);

            ContentRepository.Instance.Save();
        }
Example #13
0
        public async Task SaveVideo(Models.Resource resource, MemoryStream stream)
        {
            var client = await GetClient();

            var credential = _mediaCredential.Value;

            var contentAddress = new ContentAddress(resource.ContentAddress);
            var sourceName     = $"raw-{contentAddress.ContainerName}";

            Asset asset = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, sourceName);

            if (asset != null)
            {
                await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, sourceName);

                await Task.Delay(TimeSpan.FromSeconds(60));
            }
            //do
            //{
            //    asset = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, sourceName);
            //} while (asset != null);

            asset = await client.Assets.CreateOrUpdateAsync(credential.ResourceGroup, credential.AccountName,
                                                            sourceName, new Asset(name : sourceName, container : sourceName));

            var response = await client.Assets.ListContainerSasAsync(
                credential.ResourceGroup,
                credential.AccountName,
                asset.Name,
                permissions : AssetContainerPermission.ReadWrite,
                expiryTime : DateTime.UtcNow.AddHours(4).ToUniversalTime());

            var sasUri = new Uri(response.AssetContainerSasUrls.First());
            CloudBlobContainer container = new CloudBlobContainer(sasUri);
            var blob = container.GetBlockBlobReference(Path.GetFileName(resource.Filename));

            // Use Strorage API to upload the file into the container in storage.
            await blob.UploadFromStreamAsync(stream);
        }
        public async Task RunnerActivity(
            [ActivityTrigger] ContextDTO contextDTO,
            ILogger log)
        {
            Resource       resource = contextDTO.resource;
            ContentAddress source   = contextDTO.source;

            try
            {
                string q1      = $"SELECT * FROM c WHERE c.id='{resource.Id}'";
                var    resTemp = await repoResource.GetAsync(sqlQuery : q1);

                var res = resTemp.Items.FirstOrDefault();

                if (!string.IsNullOrEmpty(res.Status))
                {
                    return;
                }

                resource.Status = "migrating";
                await repoResource.UpsertAsync(resource.Id, resource);

                var sourceEndpoint = Environment.GetEnvironmentVariable("SourceClientEndpoint");
                if (string.IsNullOrWhiteSpace(sourceEndpoint) ||
                    string.IsNullOrWhiteSpace(source.ContainerName) ||
                    string.IsNullOrWhiteSpace(source.BlobName))
                {
                    throw new Exception($"SourceEndpoint, SourceContainerName, SourceFilename " +
                                        $"are required, any one of them are missing");
                }

                var sourceUri = new Uri($"{sourceEndpoint.Trim('/')}/{source.ContainerName}/{source.BlobName}");
                await _mediaHandler.SaveVideo(resource, sourceUri);

                // nama menggunakan prefix "migrating-" agar tidak bentrok dengan reguler
                //  encoding saat user upload video dari device
                resource.Status = "migrating-encoding";

                //await UpdateOtherData(resource, log);

                await repoResource.UpsertAsync(resource.Id, resource);

                await _mediaHandler.StartEncode(resource);
            }
            catch (PendingException ex)
            {
                resource.Status            = "pending";
                resource.StatusDescription = $"{ex.Message}\r\n{ex.InnerException?.Message}";
                resource.ProcessedDate     = DateTime.UtcNow;

                //await UpdateOtherData(resource, log);

                await repoResource.UpsertAsync(resource.Id, resource);
            }
            catch (Exception ex)
            {
                resource.Status            = "error";
                resource.StatusDescription = $"{ex.Message}\r\n{ex.InnerException?.Message}";
                resource.ProcessedDate     = DateTime.UtcNow;
                resource.ContentAddress    = null;

                //await UpdateOtherData(resource, log);

                await repoResource.UpsertAsync(resource.Id, resource);

                throw;
            }
        }
Example #15
0
        public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
        {
            RouteData rd = base.GetRouteData(httpContext);

            if (rd == null)
            {
                return(null);
            }

            string action = rd.Values["action"] as string;
            RequestDataSpecification rds;

            try
            {
                rds = new RequestDataSpecification(rd, httpContext.Request);
            }
            catch (StructureException sEx)
            {
                if (sEx is MissingControllerException) // if controller is missing, its not a content controller, process as normal
                {
                    rds = new RequestDataSpecification(rd, httpContext.Request, false);
                    RequestDataSpecification.Current = rds;
                    return(rd);
                }
                else
                {
                    return(null);
                }
            }

            RequestDataSpecification.Current = rds;

            if (!SiteStructure.Current.HasController(rds.Controller))
            {
                return(null);
            }
            ControllerInfo ci      = SiteStructure.Current[rds.Controller];
            ContentAddress ca      = new ContentAddress(rds, ci.SignificantRouteKeys);
            ContentItem    content = CollatorBuilder.Factory.Create(rd).GetContent(ca);

            if (content == null && action != "create")
            {
                return(null);
            }
            else
            {
                if (content != null)
                {
                    RequestContent = content;
                }
                //string mode = httpContext.Request.QueryString["-mode"];
                //if (mode != null) mode = mode.ToLower();
                //if (!isDiverted && mode != "view" && Roles.IsUserInRole(User.EditorRole))
                //{
                //    rd.Values["originalAction"] = rd.Values["action"];
                //    rd.Values["action"] = "DualWindow";
                //}

                return(rd);
            }
        }
Example #16
0
        public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
        {
            RouteData rd = base.GetRouteData(httpContext);

            if (rd == null) return null;

            string action = rd.Values["action"] as string;
            RequestDataSpecification rds;
            try
            {
                rds = new RequestDataSpecification(rd, httpContext.Request);
            }
            catch (StructureException sEx)
            {
                if (sEx is MissingControllerException) // if controller is missing, its not a content controller, process as normal
                {
                    rds = new RequestDataSpecification(rd, httpContext.Request, false);
                    RequestDataSpecification.Current = rds;
                    return rd;
                }
                else
                    return null;
            }

            RequestDataSpecification.Current = rds;

            if (!SiteStructure.Current.HasController(rds.Controller))
                return null;
            ControllerInfo ci = SiteStructure.Current[rds.Controller];
            ContentAddress ca = new ContentAddress(rds, ci.SignificantRouteKeys);
            ContentItem content = CollatorBuilder.Factory.Create(rd).GetContent(ca);

            if (content == null && action != "create")
                return null;
            else
            {
                if (content != null)
                {
                    RequestContent = content;
                }
                //string mode = httpContext.Request.QueryString["-mode"];
                //if (mode != null) mode = mode.ToLower();
                //if (!isDiverted && mode != "view" && Roles.IsUserInRole(User.EditorRole))
                //{
                //    rd.Values["originalAction"] = rd.Values["action"];
                //    rd.Values["action"] = "DualWindow";
                //}

                return rd;
            }
        }
Example #17
0
        public async Task <Job> StartEncode(Models.Resource resource)
        {
            var client = await GetClient();

            var credential = _mediaCredential.Value;

            var contentAddress = new ContentAddress(resource.ContentAddress);
            var sourceName     = $"raw-{contentAddress.ContainerName}";
            var resultName     = $"result-{contentAddress.ContainerName}";
            var jobName        = $"job-{contentAddress.ContainerName}";

            //await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, resultName);
            Asset resultAsset = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, resultName);

            if (resultAsset != null)
            {
                await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, resultName);

                await Task.Delay(TimeSpan.FromSeconds(60));
            }
            //do
            //{
            //    await Task.Delay(TimeSpan.FromSeconds(60));
            //    resultAsset = client.Assets.Get(credential.ResourceGroup, credential.AccountName, resultName);
            //}
            //while (resultAsset != null);

            resultAsset = await client.Assets.CreateOrUpdateAsync(credential.ResourceGroup, credential.AccountName,
                                                                  resultName, new Asset(name : resultName, container : resultName));

            await client.Transforms.CreateOrUpdateAsync(
                credential.ResourceGroup,
                credential.AccountName,
                TRANSFORM_NAME,
                new List <TransformOutput> {
                new TransformOutput(MediaTransform.Preset)
            });

            JobInput jobInput = new JobInputAsset(sourceName);

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(resultName),
            };

            Job job = await client.Jobs.GetAsync(credential.ResourceGroup, credential.AccountName,
                                                 TRANSFORM_NAME, jobName);

            if (job != null)
            {
                await client.Jobs.DeleteAsync(credential.ResourceGroup, credential.AccountName,
                                              TRANSFORM_NAME, jobName);
            }

            do // poll job status
            {
                await Task.Delay(TimeSpan.FromSeconds(3));

                job = await client.Jobs.GetAsync(credential.ResourceGroup, credential.AccountName,
                                                 TRANSFORM_NAME, jobName);
            }while (job != null);

            try
            {
                var tt = await client.Jobs.CreateWithHttpMessagesAsync(credential.ResourceGroup, credential.AccountName,
                                                                       TRANSFORM_NAME, jobName,
                                                                       parameters : new Job(input: jobInput, outputs: jobOutputs));
            }
            catch (ApiErrorException aee)
            {
                throw new Exception(aee.Message);
            }
            catch (Exception e)
            {
                throw e;
            }

            return(job);
        }
Example #18
0
        public async Task <List <Exception> > DeleteResource(Models.Resource resource)
        {
            var contentAddress = new ContentAddress(resource.ContentAddress);
            var sourceName     = $"raw-{contentAddress.ContainerName}";
            var resultName     = $"result-{contentAddress.ContainerName}";
            var jobName        = $"job-{contentAddress.ContainerName}";
            var locatorName    = $"locator-{contentAddress.ContainerName}";

            var client = await GetClient();

            var credential = _mediaCredential.Value;

            var error = new List <Exception>();

            try
            {
                await client.StreamingLocators.DeleteAsync(credential.ResourceGroup, credential.AccountName, locatorName);
            }
            catch (Exception e)
            {
                error.Add(new Exception($"error deleting streaming locator {resource.Id} -- {e.Message}\r\n{e.InnerException.Message}"));
            }

            try
            {
                await client.Jobs.DeleteAsync(credential.ResourceGroup, credential.AccountName, TRANSFORM_NAME, jobName);
            }
            catch (Exception e)
            {
                error.Add(new Exception($"error deleting job {resource.Id} -- {e.Message}\r\n{e.InnerException.Message}"));
            }

            try
            {
                await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, sourceName);
            }
            catch (Exception e)
            {
                error.Add(new Exception($"error deleting source asset {resource.Id} -- {e.Message}\r\n{e.InnerException.Message}"));
            }

            try
            {
                await client.Assets.DeleteAsync(credential.ResourceGroup, credential.AccountName, resultName);
            }
            catch (Exception e)
            {
                error.Add(new Exception($"error deleting result asset {resource.Id} -- {e.Message}\r\n{e.InnerException.Message}"));
            }

            StreamingLocator locator = null;
            Job   job    = null;
            Asset source = null;
            Asset result = null;

            do
            {
                locator = await client.StreamingLocators.GetAsync(credential.ResourceGroup, credential.AccountName, locatorName);

                job = await client.Jobs.GetAsync(credential.ResourceGroup, credential.AccountName, TRANSFORM_NAME, jobName);

                source = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, sourceName);

                result = await client.Assets.GetAsync(credential.ResourceGroup, credential.AccountName, resultName);
            }while (locator != null || job != null || source != null || result != null);

            return(error.Count > 0 ? error : null);
        }
Example #19
0
 static public string Action(this UrlHelper urls, ContentAddress addr)
 {
     return(SiteStructure.Current.GetUrl(addr));
 }
Example #20
0
        public string GetUrl(ContentAddress ca)
        {
            RouteValueDictionary rvs = new RouteValueDictionary();
            rvs.Add("controller", ca.Controller);
            rvs.Add("action", ca.Action);
            ControllerInfo cInfo = GetController((string)rvs["controller"]); ;
            if (cInfo == null) throw new MissingControllerException("Can't find controller " + ca.Controller);

            for (int i = 0; i < Math.Min(cInfo.SignificantRouteKeys.Count, ca.Subindexes.Count); i++)
                rvs.Add(cInfo.SignificantRouteKeys[i], ca.Subindexes[i]);

            UrlPattern patt = cInfo.UrlPatterns.FirstOrDefault(up => up.Matches(rvs));
            if (patt == null) throw new StructureException("No matching pattern found on controller " + ca.Controller);
            return patt.BuildUrl(rvs);
        }