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