001/* 002 * Copyright 2002-2017 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; 020 021import javax.servlet.http.HttpServletRequest; 022 023import org.webjars.WebJarAssetLocator; 024 025import org.springframework.core.io.Resource; 026 027/** 028 * A {@code ResourceResolver} that delegates to the chain to locate a resource and then 029 * attempts to find a matching versioned resource contained in a WebJar JAR file. 030 * 031 * <p>This allows WebJars.org users to write version agnostic paths in their templates, 032 * like {@code <script src="/jquery/jquery.min.js"/>}. 033 * This path will be resolved to the unique version {@code <script src="/jquery/1.2.0/jquery.min.js"/>}, 034 * which is a better fit for HTTP caching and version management in applications. 035 * 036 * <p>This also resolves resources for version agnostic HTTP requests {@code "GET /jquery/jquery.min.js"}. 037 * 038 * <p>This resolver requires the "org.webjars:webjars-locator" library on classpath, 039 * and is automatically registered if that library is present. 040 * 041 * @author Brian Clozel 042 * @since 4.2 043 * @see org.springframework.web.servlet.config.annotation.ResourceChainRegistration 044 * @see <a href="https://www.webjars.org">webjars.org</a> 045 */ 046public class WebJarsResourceResolver extends AbstractResourceResolver { 047 048 private final static String WEBJARS_LOCATION = "META-INF/resources/webjars/"; 049 050 private final static int WEBJARS_LOCATION_LENGTH = WEBJARS_LOCATION.length(); 051 052 053 private final WebJarAssetLocator webJarAssetLocator; 054 055 056 /** 057 * Create a {@code WebJarsResourceResolver} with a default {@code WebJarAssetLocator} instance. 058 */ 059 public WebJarsResourceResolver() { 060 this(new WebJarAssetLocator()); 061 } 062 063 /** 064 * Create a {@code WebJarsResourceResolver} with a custom {@code WebJarAssetLocator} instance, 065 * e.g. with a custom index. 066 * @since 4.3 067 */ 068 public WebJarsResourceResolver(WebJarAssetLocator webJarAssetLocator) { 069 this.webJarAssetLocator = webJarAssetLocator; 070 } 071 072 073 @Override 074 protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath, 075 List<? extends Resource> locations, ResourceResolverChain chain) { 076 077 Resource resolved = chain.resolveResource(request, requestPath, locations); 078 if (resolved == null) { 079 String webJarResourcePath = findWebJarResourcePath(requestPath); 080 if (webJarResourcePath != null) { 081 return chain.resolveResource(request, webJarResourcePath, locations); 082 } 083 } 084 return resolved; 085 } 086 087 @Override 088 protected String resolveUrlPathInternal(String resourceUrlPath, 089 List<? extends Resource> locations, ResourceResolverChain chain) { 090 091 String path = chain.resolveUrlPath(resourceUrlPath, locations); 092 if (path == null) { 093 String webJarResourcePath = findWebJarResourcePath(resourceUrlPath); 094 if (webJarResourcePath != null) { 095 return chain.resolveUrlPath(webJarResourcePath, locations); 096 } 097 } 098 return path; 099 } 100 101 protected String findWebJarResourcePath(String path) { 102 int startOffset = (path.startsWith("/") ? 1 : 0); 103 int endOffset = path.indexOf('/', 1); 104 if (endOffset != -1) { 105 String webjar = path.substring(startOffset, endOffset); 106 String partialPath = path.substring(endOffset + 1); 107 String webJarPath = webJarAssetLocator.getFullPathExact(webjar, partialPath); 108 if (webJarPath != null) { 109 return webJarPath.substring(WEBJARS_LOCATION_LENGTH); 110 } 111 } 112 return null; 113 } 114 115}