001/* 002 * Copyright 2002-2018 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.regex.Matcher; 020import java.util.regex.Pattern; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027import org.springframework.util.StringUtils; 028 029/** 030 * Abstract base class for {@link VersionStrategy} implementations. 031 * 032 * <p>Supports versions as: 033 * <ul> 034 * <li>prefix in the request path, like "version/static/myresource.js" 035 * <li>file name suffix in the request path, like "static/myresource-version.js" 036 * </ul> 037 * 038 * <p>Note: This base class does <i>not</i> provide support for generating the 039 * version string. 040 * 041 * @author Brian Clozel 042 * @author Rossen Stoyanchev 043 * @since 4.1 044 */ 045public abstract class AbstractVersionStrategy implements VersionStrategy { 046 047 protected final Log logger = LogFactory.getLog(getClass()); 048 049 private final VersionPathStrategy pathStrategy; 050 051 052 protected AbstractVersionStrategy(VersionPathStrategy pathStrategy) { 053 Assert.notNull(pathStrategy, "VersionPathStrategy is required"); 054 this.pathStrategy = pathStrategy; 055 } 056 057 058 public VersionPathStrategy getVersionPathStrategy() { 059 return this.pathStrategy; 060 } 061 062 063 @Override 064 @Nullable 065 public String extractVersion(String requestPath) { 066 return this.pathStrategy.extractVersion(requestPath); 067 } 068 069 @Override 070 public String removeVersion(String requestPath, String version) { 071 return this.pathStrategy.removeVersion(requestPath, version); 072 } 073 074 @Override 075 public String addVersion(String requestPath, String version) { 076 return this.pathStrategy.addVersion(requestPath, version); 077 } 078 079 080 /** 081 * A prefix-based {@code VersionPathStrategy}, 082 * e.g. {@code "{version}/path/foo.js"}. 083 */ 084 protected static class PrefixVersionPathStrategy implements VersionPathStrategy { 085 086 private final String prefix; 087 088 public PrefixVersionPathStrategy(String version) { 089 Assert.hasText(version, "Version must not be empty"); 090 this.prefix = version; 091 } 092 093 @Override 094 @Nullable 095 public String extractVersion(String requestPath) { 096 return (requestPath.startsWith(this.prefix) ? this.prefix : null); 097 } 098 099 @Override 100 public String removeVersion(String requestPath, String version) { 101 return requestPath.substring(this.prefix.length()); 102 } 103 104 @Override 105 public String addVersion(String path, String version) { 106 if (path.startsWith(".")) { 107 return path; 108 } 109 else { 110 return (this.prefix.endsWith("/") || path.startsWith("/") ? 111 this.prefix + path : this.prefix + '/' + path); 112 } 113 } 114 } 115 116 117 /** 118 * File name-based {@code VersionPathStrategy}, 119 * e.g. {@code "path/foo-{version}.css"}. 120 */ 121 protected static class FileNameVersionPathStrategy implements VersionPathStrategy { 122 123 private static final Pattern pattern = Pattern.compile("-(\\S*)\\."); 124 125 @Override 126 @Nullable 127 public String extractVersion(String requestPath) { 128 Matcher matcher = pattern.matcher(requestPath); 129 if (matcher.find()) { 130 String match = matcher.group(1); 131 return (match.contains("-") ? match.substring(match.lastIndexOf('-') + 1) : match); 132 } 133 else { 134 return null; 135 } 136 } 137 138 @Override 139 public String removeVersion(String requestPath, String version) { 140 return StringUtils.delete(requestPath, "-" + version); 141 } 142 143 @Override 144 public String addVersion(String requestPath, String version) { 145 String baseFilename = StringUtils.stripFilenameExtension(requestPath); 146 String extension = StringUtils.getFilenameExtension(requestPath); 147 return (baseFilename + '-' + version + '.' + extension); 148 } 149 } 150 151}