001/*
002 * Copyright 2002-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.resource;
018
019import java.util.List;
020import javax.servlet.http.HttpServletRequest;
021
022import org.springframework.cache.Cache;
023import org.springframework.cache.CacheManager;
024import org.springframework.core.io.Resource;
025import org.springframework.util.Assert;
026
027/**
028 * A {@link org.springframework.web.servlet.resource.ResourceResolver} that
029 * resolves resources from a {@link org.springframework.cache.Cache} or otherwise
030 * delegates to the resolver chain and saves the result in the cache.
031 *
032 * @author Rossen Stoyanchev
033 * @author Brian Clozel
034 * @since 4.1
035 */
036public class CachingResourceResolver extends AbstractResourceResolver {
037
038        public static final String RESOLVED_RESOURCE_CACHE_KEY_PREFIX = "resolvedResource:";
039
040        public static final String RESOLVED_URL_PATH_CACHE_KEY_PREFIX = "resolvedUrlPath:";
041
042
043        private final Cache cache;
044
045
046        public CachingResourceResolver(CacheManager cacheManager, String cacheName) {
047                this(cacheManager.getCache(cacheName));
048        }
049
050        public CachingResourceResolver(Cache cache) {
051                Assert.notNull(cache, "Cache is required");
052                this.cache = cache;
053        }
054
055
056        /**
057         * Return the configured {@code Cache}.
058         */
059        public Cache getCache() {
060                return this.cache;
061        }
062
063
064        @Override
065        protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath,
066                        List<? extends Resource> locations, ResourceResolverChain chain) {
067
068                String key = computeKey(request, requestPath);
069                Resource resource = this.cache.get(key, Resource.class);
070
071                if (resource != null) {
072                        if (logger.isTraceEnabled()) {
073                                logger.trace("Found match: " + resource);
074                        }
075                        return resource;
076                }
077
078                resource = chain.resolveResource(request, requestPath, locations);
079                if (resource != null) {
080                        if (logger.isTraceEnabled()) {
081                                logger.trace("Putting resolved resource in cache: " + resource);
082                        }
083                        this.cache.put(key, resource);
084                }
085
086                return resource;
087        }
088
089        protected String computeKey(HttpServletRequest request, String requestPath) {
090                StringBuilder key = new StringBuilder(RESOLVED_RESOURCE_CACHE_KEY_PREFIX);
091                key.append(requestPath);
092                if (request != null) {
093                        String encoding = request.getHeader("Accept-Encoding");
094                        if (encoding != null && encoding.contains("gzip")) {
095                                key.append("+encoding=gzip");
096                        }
097                }
098                return key.toString();
099        }
100
101        @Override
102        protected String resolveUrlPathInternal(String resourceUrlPath,
103                        List<? extends Resource> locations, ResourceResolverChain chain) {
104
105                String key = RESOLVED_URL_PATH_CACHE_KEY_PREFIX + resourceUrlPath;
106                String resolvedUrlPath = this.cache.get(key, String.class);
107
108                if (resolvedUrlPath != null) {
109                        if (logger.isTraceEnabled()) {
110                                logger.trace("Found match: \"" + resolvedUrlPath + "\"");
111                        }
112                        return resolvedUrlPath;
113                }
114
115                resolvedUrlPath = chain.resolveUrlPath(resourceUrlPath, locations);
116                if (resolvedUrlPath != null) {
117                        if (logger.isTraceEnabled()) {
118                                logger.trace("Putting resolved resource URL path in cache: \"" + resolvedUrlPath + "\"");
119                        }
120                        this.cache.put(key, resolvedUrlPath);
121                }
122
123                return resolvedUrlPath;
124        }
125
126}