Ejemplo n.º 1
0
        public IList <CombinatorResource> GetCombinedResources(int hashCode)
        {
            return(_cacheManager.Get(MakeCacheKey("GetCombinedResources." + hashCode.ToString()), ctx =>
            {
                _combinatorEventMonitor.MonitorCacheEmptied(ctx);

                var files = GetRecords(hashCode);
                var fileCount = files.Count;

                var resources = new List <CombinatorResource>(fileCount);

                foreach (var file in files)
                {
                    var resource = _combinatorResourceManager.ResourceFactory(file.Type);
                    resource.FillRequiredContext(
                        "CombinedResource" + file.Id.ToString(),
                        _storageProvider.GetPublicUrl(MakePath(file)));
                    _combinatorResourceManager.DeserializeSettings(file.Settings, resource);
                    resource.LastUpdatedUtc = file.LastUpdatedUtc ?? _clock.UtcNow;
                    resources.Add(resource);
                }

                return resources;
            }));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Combines (and minifies) the content of resources and saves the combinations.
        /// </summary>
        /// <param name="resources">Resources to combine.</param>
        /// <param name="fingerprint">Just so it shouldn't be recalculated.</param>
        /// <param name="resourceType">Type of the resources.</param>
        /// <param name="settings">Combination setting.s</param>
        /// <exception cref="ApplicationException">Thrown if there was a problem with a resource file (e.g. it was missing or could not be opened).</exception>
        private void Combine(IList <ResourceRequiredContext> resources, string fingerprint, ResourceType resourceType, ICombinatorSettings settings)
        {
            if (resources.Count == 0)
            {
                return;
            }

            var combinatorResources = new List <CombinatorResource>(resources.Count);

            foreach (var resource in resources)
            {
                var combinatorResource = _combinatorResourceManager.ResourceFactory(resourceType);

                // Copying the context so the original one won't be touched.
                combinatorResource.FillRequiredContext(
                    resource.Resource.Name,
                    resource.Resource.GetFullPath(),
                    resource.Settings.Culture,
                    resource.Settings.Condition,
                    resource.Settings.Attributes,
                    resource.Resource.TagBuilder.Attributes);

                combinatorResource.IsRemoteStorageResource = settings.RemoteStorageUrlPattern != null && settings.RemoteStorageUrlPattern.IsMatch(combinatorResource.AbsoluteUrl.ToString());

                combinatorResources.Add(combinatorResource);
            }

            var combinedContent = new StringBuilder(1000);
            var resourceBaseUri = settings.ResourceBaseUri != null ? settings.ResourceBaseUri : new Uri(_hca.Current().Request.Url, "/");

            Action <CombinatorResource, List <CombinatorResource> > saveCombination =
                (combinedResource, containedResources) =>
            {
                if (combinedResource == null)
                {
                    return;
                }

                // Don't save emtpy resources.
                if (combinedContent.Length == 0 && !combinedResource.IsOriginal)
                {
                    return;
                }

                if (!containedResources.Any())
                {
                    containedResources = new List <CombinatorResource> {
                        combinedResource
                    }
                }
                ;

                var bundleFingerprint = containedResources.GetCombinatorResourceListFingerprint(settings);

                var localSettings = new CombinatorSettings(settings);
                if (localSettings.EnableResourceSharing && settings.ResourceSharingExcludeFilter != null)
                {
                    foreach (var resource in containedResources)
                    {
                        if (localSettings.EnableResourceSharing)
                        {
                            localSettings.EnableResourceSharing = !settings.ResourceSharingExcludeFilter.IsMatch(resource.AbsoluteUrl.ToString());
                        }
                    }
                }

                if (!combinedResource.IsOriginal)
                {
                    combinedResource.Content = combinedContent.ToString();

                    if (combinedResource.Type == ResourceType.Style && !string.IsNullOrEmpty(combinedResource.Content) && settings.GenerateImageSprites)
                    {
                        _resourceProcessingService.ReplaceCssImagesWithSprite(combinedResource);
                    }

                    combinedResource.Content =
                        "/*" + Environment.NewLine
                        + "Resource bundle created by Combinator (http://combinator.codeplex.com/)" + Environment.NewLine + Environment.NewLine
                        + "Resources in this bundle:" + Environment.NewLine
                        + string.Join(Environment.NewLine, containedResources.Select(resource =>
                    {
                        var url = resource.AbsoluteUrl.ToString();
                        if (localSettings.EnableResourceSharing && !resource.IsCdnResource && !resource.IsRemoteStorageResource)
                        {
                            var uriBuilder  = new UriBuilder(url);
                            uriBuilder.Host = "DefaultTenant";
                            url             = uriBuilder.Uri.ToStringWithoutScheme();
                        }
                        return("- " + url);
                    }))
                        + Environment.NewLine + "*/"
                        + Environment.NewLine + Environment.NewLine + Environment.NewLine + combinedResource.Content;
                }


                // We save a bundle now. First the bundle should be saved separately under its unique name, then for this resource list.
                if (bundleFingerprint != fingerprint && containedResources.Count > 1)
                {
                    if (!_cacheFileService.Exists(bundleFingerprint, localSettings))
                    {
                        _cacheFileService.Save(bundleFingerprint, combinedResource, localSettings);
                    }

                    // Overriding the url for the resource in this resource list with the url of the set.
                    combinedResource.IsOriginal = true;

                    // The following should fetch one result theoretically but can more if the above Exists()-Save() happens
                    // in multiple requests at the same time.
                    var set = _cacheFileService.GetCombinedResources(bundleFingerprint, localSettings).First();

                    combinedResource.LastUpdatedUtc = set.LastUpdatedUtc;

                    if (IsOwnedResource(set))
                    {
                        if (settings.ResourceBaseUri != null && !set.IsRemoteStorageResource)
                        {
                            combinedResource.RequiredContext.Resource.SetUrl(UriHelper.Combine(resourceBaseUri.ToStringWithoutScheme(), set.AbsoluteUrl.PathAndQuery));
                        }
                        else if (set.IsRemoteStorageResource)
                        {
                            combinedResource.IsRemoteStorageResource = true;
                            combinedResource.RequiredContext.Resource.SetUrl(set.AbsoluteUrl.ToStringWithoutScheme());
                        }
                        else
                        {
                            combinedResource.RequiredContext.Resource.SetUrl(set.RelativeUrl.ToString());
                        }

                        AddTimestampToUrlIfNecessary(combinedResource);
                    }
                    else
                    {
                        combinedResource.RequiredContext.Resource.SetUrl(set.AbsoluteUrl.ToStringWithoutScheme());
                    }
                }

                _cacheFileService.Save(fingerprint, combinedResource, localSettings);

                combinedContent.Clear();
                containedResources.Clear();
            };


            Regex currentSetRegex        = null;
            var   resourcesInCombination = new List <CombinatorResource>();

            for (int i = 0; i < combinatorResources.Count; i++)
            {
                var resource             = combinatorResources[i];
                var previousResource     = (i != 0) ? combinatorResources[i - 1] : null;
                var absoluteUrlString    = "";
                var saveOriginalResource = false;

                try
                {
                    absoluteUrlString = resource.AbsoluteUrl.ToString();

                    if (settings.CombinationExcludeFilter == null || !settings.CombinationExcludeFilter.IsMatch(absoluteUrlString))
                    {
                        // If this resource differs from the previous one in terms of settings or CDN they can't be combined
                        if (previousResource != null &&
                            (!previousResource.SettingsEqual(resource) || (previousResource.IsCdnResource != resource.IsCdnResource && !settings.CombineCdnResources)))
                        {
                            saveCombination(previousResource, resourcesInCombination);
                            previousResource = null; // So it doesn't get combined again in if (saveOriginalResource) below.
                        }

                        // If this resource is in a different set than the previous, they can't be combined
                        if (currentSetRegex != null && !currentSetRegex.IsMatch(absoluteUrlString))
                        {
                            currentSetRegex = null;
                            saveCombination(previousResource, resourcesInCombination);
                        }

                        // Calculate if this resource is in a set
                        if (currentSetRegex == null && settings.ResourceSetFilters != null && settings.ResourceSetFilters.Length > 0)
                        {
                            int r = 0;
                            while (currentSetRegex == null && r < settings.ResourceSetFilters.Length)
                            {
                                if (settings.ResourceSetFilters[r].IsMatch(absoluteUrlString))
                                {
                                    currentSetRegex = settings.ResourceSetFilters[r];
                                }
                                r++;
                            }

                            // The previous resource is in a different set or in no set so it can't be combined with this resource
                            if (currentSetRegex != null && previousResource != null && resourcesInCombination.Any())
                            {
                                saveCombination(previousResource, resourcesInCombination);
                            }
                        }

                        // Nesting resources in such blocks is needed because some syntax is valid if in its own file but not anymore
                        // when bundled.
                        if (resourceType == ResourceType.JavaScript)
                        {
                            combinedContent.Append("{");
                        }
                        combinedContent.Append(Environment.NewLine);

                        _resourceProcessingService.ProcessResource(resource, combinedContent, settings);

                        // This can be because e.g. it's a CDN resource and CDN combination is disabled.
                        if (resource.IsOriginal)
                        {
                            saveOriginalResource = true;
                        }

                        combinedContent.Append(Environment.NewLine);
                        if (resourceType == ResourceType.JavaScript)
                        {
                            combinedContent.Append("}");
                        }
                        combinedContent.Append(Environment.NewLine);

                        resourcesInCombination.Add(resource);
                    }
                    else
                    {
                        saveOriginalResource = true;
                    }

                    if (saveOriginalResource)
                    {
                        // This is a fully excluded resource
                        if (previousResource != null)
                        {
                            saveCombination(previousResource, resourcesInCombination);
                        }
                        resource.IsOriginal = true;
                        saveCombination(resource, resourcesInCombination);
                        combinatorResources[i] = null; // So previous resource detection works correctly
                    }
                }
                catch (Exception ex)
                {
                    if (ex.IsFatal())
                    {
                        throw;
                    }
                    throw new OrchardException(T("Processing of resource {0} failed.", absoluteUrlString), ex);
                }
            }


            saveCombination(combinatorResources[combinatorResources.Count - 1], resourcesInCombination);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Combines (and minifies) the content of resources and saves the combinations
        /// </summary>
        /// <param name="resources">Resources to combine</param>
        /// <param name="hashCode">Just so it shouldn't be recalculated</param>
        /// <param name="resourceType">Type of the resources</param>
        /// <param name="settings">Combination settings</param>
        /// <exception cref="ApplicationException">Thrown if there was a problem with a resource file (e.g. it was missing or could not be opened)</exception>
        private void Combine(IList <ResourceRequiredContext> resources, int hashCode, ResourceType resourceType, ICombinatorSettings settings)
        {
            if (resources.Count == 0)
            {
                return;
            }

            var combinatorResources = new List <CombinatorResource>(resources.Count);

            foreach (var resource in resources)
            {
                var combinatorResource = _combinatorResourceManager.ResourceFactory(resourceType);

                // Copying the context so the original one won't be touched
                combinatorResource.FillRequiredContext(
                    resource.Resource.Name,
                    resource.Resource.GetFullPath(),
                    resource.Settings.Culture,
                    resource.Settings.Condition,
                    resource.Settings.Attributes);

                //var requiredContext = new ResourceRequiredContext();
                //var resourceManifest = new ResourceManifest();
                //requiredContext.Resource = resourceManifest.DefineResource(ResourceTypeHelper.EnumToStringType(resourceType), resource.Resource.Name);
                //requiredContext.Resource.SetUrl(resource.Resource.GetFullPath());
                //requiredContext.Settings = new RequireSettings();
                //requiredContext.Settings.Culture = resource.Settings.Culture;
                //requiredContext.Settings.Condition = resource.Settings.Condition;
                //requiredContext.Settings.Attributes = new Dictionary<string, string>(resource.Settings.Attributes);
                //combinatorResource.RequiredContext = requiredContext;

                combinatorResources.Add(combinatorResource);
            }

            var combinedContent = new StringBuilder(1000);

            Action <CombinatorResource> saveCombination =
                (combinedResource) =>
            {
                if (combinedResource == null)
                {
                    return;
                }
                // Don't save emtpy resources
                if (combinedContent.Length == 0 && !combinedResource.IsOriginal)
                {
                    return;
                }

                combinedResource.Content = combinedContent.ToString();
                _cacheFileService.Save(hashCode, combinedResource);

                combinedContent.Clear();
            };


            Regex currentSetRegex = null;

            for (int i = 0; i < combinatorResources.Count; i++)
            {
                var resource          = combinatorResources[i];
                var previousResource  = (i != 0) ? combinatorResources[i - 1] : null;
                var absoluteUrlString = "";

                try
                {
                    absoluteUrlString = resource.AbsoluteUrl.ToString();

                    if (settings.CombinationExcludeFilter == null || !settings.CombinationExcludeFilter.IsMatch(absoluteUrlString))
                    {
                        // If this resource differs from the previous one in terms of settings or CDN they can't be combined
                        if (previousResource != null &&
                            (!previousResource.SettingsEqual(resource) || (previousResource.IsCdnResource != resource.IsCdnResource && !settings.CombineCDNResources)))
                        {
                            saveCombination(previousResource);
                        }

                        // If this resource is in a different set than the previous, they can't be combined
                        if (currentSetRegex != null && !currentSetRegex.IsMatch(absoluteUrlString))
                        {
                            currentSetRegex = null;
                            saveCombination(previousResource);
                        }

                        // Calculate if this resource is in a set
                        if (currentSetRegex == null && settings.ResourceSetFilters != null && settings.ResourceSetFilters.Length > 0)
                        {
                            int r = 0;
                            while (currentSetRegex == null && r < settings.ResourceSetFilters.Length)
                            {
                                if (settings.ResourceSetFilters[r].IsMatch(absoluteUrlString))
                                {
                                    currentSetRegex = settings.ResourceSetFilters[r];
                                }
                                r++;
                            }

                            // The previous resource is in a different set or in no set so it can't be combined with this resource
                            if (currentSetRegex != null && previousResource != null)
                            {
                                saveCombination(previousResource);
                            }
                        }

                        _resourceProcessingService.ProcessResource(resource, combinedContent, settings);
                    }
                    else
                    {
                        // This is a fully excluded resource
                        if (previousResource != null)
                        {
                            saveCombination(previousResource);
                        }
                        resource.IsOriginal = true;
                        saveCombination(resource);
                        combinatorResources[i] = null; // So previous resource detection works correctly
                    }
                }
                catch (Exception ex)
                {
                    if (ex.IsFatal())
                    {
                        throw;
                    }
                    throw new OrchardException(T("Processing of resource {0} failed.", absoluteUrlString), ex);
                }
            }


            saveCombination(combinatorResources[combinatorResources.Count - 1]);
        }
Ejemplo n.º 4
0
        public void Save(string fingerprint, CombinatorResource resource, ICombinatorSettings settings)
        {
            if (settings.EnableResourceSharing && CallOnDefaultShell(cacheFileService => cacheFileService.Save(fingerprint, resource, new CombinatorSettings(settings)
            {
                EnableResourceSharing = false
            })))
            {
                return;
            }

            var sliceCount = _fileRepository.Count(file => file.Fingerprint == ConvertFingerprintToStorageFormat(fingerprint));

            if (resource.LastUpdatedUtc == DateTime.MinValue)
            {
                resource.LastUpdatedUtc = _clock.UtcNow;
            }

            // Ceil-ing timestamp to the second, because sub-second precision is not stored in the DB. This would cause a discrepancy between saved
            // and fetched vs freshly created date times, causing unwanted cache busting for the same resource.
            resource.LastUpdatedUtc = new DateTime(resource.LastUpdatedUtc.Year, resource.LastUpdatedUtc.Month, resource.LastUpdatedUtc.Day, resource.LastUpdatedUtc.Hour, resource.LastUpdatedUtc.Minute, resource.LastUpdatedUtc.Second);

            var fileRecord = new CombinedFileRecord()
            {
                Fingerprint    = ConvertFingerprintToStorageFormat(fingerprint),
                Slice          = ++sliceCount,
                Type           = resource.Type,
                LastUpdatedUtc = resource.LastUpdatedUtc,
                Settings       = _combinatorResourceManager.SerializeResourceSettings(resource)
            };

            _fileRepository.Create(fileRecord);

            if (!string.IsNullOrEmpty(resource.Content))
            {
                var path = MakePath(fileRecord);

                if (_storageProvider.FileExists(path))
                {
                    _storageProvider.DeleteFile(path);
                }

                using (var stream = _storageProvider.CreateFile(path).OpenWrite())
                {
                    var bytes = Encoding.UTF8.GetBytes(resource.Content);
                    stream.Write(bytes, 0, bytes.Length);
                }

                if (!resource.IsRemoteStorageResource)
                {
                    // This is needed to adjust relative paths if the resource is stored in a remote storage provider.
                    // Why the double-saving? Before saving the file there is no reliable way to tell whether the storage public url will be a
                    // remote one or not...
                    var testResource = _combinatorResourceManager.ResourceFactory(resource.Type);
                    testResource.FillRequiredContext("TestCombinedResource", _storageProvider.GetPublicUrl(path));
                    _combinatorResourceManager.DeserializeSettings(fileRecord.Settings, testResource);
                    testResource.IsRemoteStorageResource = settings.RemoteStorageUrlPattern != null && settings.RemoteStorageUrlPattern.IsMatch(testResource.AbsoluteUrl.ToString());
                    if (testResource.IsRemoteStorageResource)
                    {
                        _storageProvider.DeleteFile(path);

                        testResource.Content = resource.Content;
                        var relativeUrlsBaseUri = settings.ResourceBaseUri != null ? settings.ResourceBaseUri : new Uri(_urlHelper.RequestContext.HttpContext.Request.Url, _urlHelper.Content("~/"));
                        ResourceProcessingService.RegexConvertRelativeUrlsToAbsolute(testResource, relativeUrlsBaseUri);

                        using (var stream = _storageProvider.CreateFile(path).OpenWrite())
                        {
                            var bytes = Encoding.UTF8.GetBytes(testResource.Content);
                            stream.Write(bytes, 0, bytes.Length);
                        }

                        resource.IsRemoteStorageResource = true;
                        fileRecord.Settings = _combinatorResourceManager.SerializeResourceSettings(resource);
                    }
                }
            }

            _combinatorEventHandler.BundleChanged(fingerprint);
        }