27 changed files with 1417 additions and 28 deletions
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public abstract class AbstractResourceResolver implements ResourceResolver { |
||||
|
||||
@Override |
||||
public final Resource resolve(HttpServletRequest request, String path, |
||||
List<Resource> locations, ResourceResolverChain chain) { |
||||
|
||||
Resource candidate = chain.next(this).resolve(request, path, locations, chain); |
||||
|
||||
return resolveInternal(request, path, locations, chain, candidate); |
||||
} |
||||
|
||||
protected abstract Resource resolveInternal(HttpServletRequest request, String path, |
||||
List<Resource> locations, ResourceResolverChain chain, Resource resolved); |
||||
|
||||
@Override |
||||
public String resolveUrl(String resourcePath, List<Resource> locations, |
||||
ResourceResolverChain chain) { |
||||
return chain.next(this).resolveUrl(resourcePath, locations, chain); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class DefaultResourceResolverChain implements ResourceResolverChain{ |
||||
|
||||
private static final ResourceResolver DEFAULT_RESOLVER = new PathMappingResourceResolver(); |
||||
|
||||
private final List<ResourceResolver> resolvers; |
||||
|
||||
private List<ResourceTransformer> transformers = new ArrayList<ResourceTransformer>(); |
||||
|
||||
public DefaultResourceResolverChain(List<ResourceResolver> resolvers, List<ResourceTransformer> transformers) { |
||||
this.resolvers = resolvers; |
||||
this.resolvers.add(DEFAULT_RESOLVER); |
||||
this.transformers = transformers; |
||||
} |
||||
|
||||
@Override |
||||
public ResourceResolver next(ResourceResolver current) { |
||||
return this.resolvers.get(this.resolvers.indexOf(current) + 1); |
||||
} |
||||
|
||||
@Override |
||||
public Resource resolveAndTransform(HttpServletRequest request, String path, |
||||
List<Resource> locations) throws IOException{ |
||||
Resource resolved = this.resolvers.get(0).resolve(request, path, locations, this); |
||||
return resolved != null ? applyTransformers(request, resolved) : resolved; |
||||
} |
||||
|
||||
@Override |
||||
public String resolveUrl(String resourcePath, List<Resource> locations) { |
||||
return this.resolvers.get(0).resolveUrl(resourcePath, locations, this); |
||||
} |
||||
|
||||
protected Resource applyTransformers(HttpServletRequest request, Resource resource) throws IOException{ |
||||
for (ResourceTransformer transformer : transformers) { |
||||
if (transformer.handles(request, resource)) { |
||||
return applyTransformers(request, transformer.transform(resource)); |
||||
} |
||||
} |
||||
return resource; |
||||
} |
||||
|
||||
private static class PathMappingResourceResolver implements ResourceResolver { |
||||
|
||||
private static final Log logger = LogFactory.getLog(PathMappingResourceResolver.class); |
||||
|
||||
@Override |
||||
public Resource resolve(HttpServletRequest request, String path, List<Resource> locations, ResourceResolverChain chain) { |
||||
for (Resource location : locations) { |
||||
try { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Trying relative path [" + path + "] against base location: " + location); |
||||
} |
||||
Resource resource = location.createRelative(path); |
||||
if (resource.exists() && resource.isReadable()) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Found matching resource: " + resource); |
||||
} |
||||
return resource; |
||||
} |
||||
else if (logger.isTraceEnabled()) { |
||||
logger.trace("Relative resource doesn't exist or isn't readable: " + resource); |
||||
} |
||||
} |
||||
catch (IOException ex) { |
||||
logger.debug("Failed to create relative resource - trying next resource location", ex); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String resolveUrl(String resourcePath, List<Resource> locations, ResourceResolverChain chain) { |
||||
if (resolve(null, resourcePath, locations, chain) != null) { |
||||
return resourcePath; |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public interface EncodedResource extends Resource { |
||||
|
||||
public String getEncoding(); |
||||
} |
||||
@ -0,0 +1,117 @@
@@ -0,0 +1,117 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.File; |
||||
import java.io.FilenameFilter; |
||||
import java.io.IOException; |
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class ExtensionMappingResourceResolver extends AbstractResourceResolver { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private final boolean compareTimeStamp; |
||||
|
||||
public ExtensionMappingResourceResolver() { |
||||
this.compareTimeStamp = false; |
||||
} |
||||
|
||||
public ExtensionMappingResourceResolver(boolean compareTimeStamp) { |
||||
this.compareTimeStamp = compareTimeStamp; |
||||
} |
||||
|
||||
@Override |
||||
protected Resource resolveInternal(HttpServletRequest request, String path, |
||||
List<Resource> locations, ResourceResolverChain chain, Resource resolved) { |
||||
if (resolved != null && !compareTimeStamp) { |
||||
return resolved; |
||||
} |
||||
|
||||
for (Resource location : locations) { |
||||
String baseFilename = StringUtils.getFilename(path); |
||||
|
||||
try { |
||||
Resource basePath = location.createRelative(StringUtils.delete(path, baseFilename)); |
||||
if (basePath.getFile().isDirectory()) { |
||||
for (String fileName : basePath.getFile().list(new ExtensionFilter(baseFilename))) { |
||||
//Always use the first match
|
||||
Resource matched = basePath.createRelative(fileName); |
||||
if (resolved == null || matched.lastModified() > resolved.lastModified()) { |
||||
return matched; |
||||
} else { |
||||
return resolved; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (IOException e) { |
||||
this.logger.trace("Error occurred locating resource based on file extension mapping", e); |
||||
} |
||||
|
||||
} |
||||
return resolved; |
||||
} |
||||
|
||||
@Override |
||||
public String resolveUrl(String resourcePath, List<Resource> locations, |
||||
ResourceResolverChain chain) { |
||||
String resolved = super.resolveUrl(resourcePath, locations, chain); |
||||
if (StringUtils.hasText(resolved)) { |
||||
return resolved; |
||||
} |
||||
|
||||
Resource mappedResource = resolveInternal(null, resourcePath, locations, chain, null); |
||||
if (mappedResource != null) { |
||||
return resourcePath; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
|
||||
private static final class ExtensionFilter implements FilenameFilter{ |
||||
|
||||
private final String baseFilename; |
||||
private final String baseExtension; |
||||
private final int baseExtLen; |
||||
|
||||
|
||||
public ExtensionFilter(String baseFilename) { |
||||
this.baseFilename = baseFilename; |
||||
this.baseExtension = "." + StringUtils.getFilenameExtension(baseFilename); |
||||
this.baseExtLen = this.baseExtension.length(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean accept(File dir, String name) { |
||||
return name.contains(baseExtension) && baseFilename.equals(name.substring(0, name.lastIndexOf(baseExtension) + this.baseExtLen)); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.List; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.DigestUtils; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class FingerprintingResourceResolver extends AbstractResourceResolver { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private Pattern pattern = Pattern.compile("-(\\S*)\\."); |
||||
|
||||
@Override |
||||
protected Resource resolveInternal(HttpServletRequest request, String path, List<Resource> locations, |
||||
ResourceResolverChain chain, Resource resolved) { |
||||
//First try the resolved full path, in case resource has been written that way to disk at build-time
|
||||
//or the resource is requested without fingerprint
|
||||
if (resolved != null) { |
||||
return resolved; |
||||
} |
||||
|
||||
//Now try extracting and matching the hash for dev mode
|
||||
String hash = extractHash(path); |
||||
String simplePath = !StringUtils.isEmpty(hash) ? StringUtils.delete(path, "-" + hash) : path; |
||||
Resource baseResource = chain.next(this).resolve(request, simplePath, locations, chain); |
||||
|
||||
if (StringUtils.isEmpty(hash) || baseResource == null) { |
||||
return baseResource; |
||||
} |
||||
|
||||
String candidateHash = calculateHash(baseResource); |
||||
|
||||
if (candidateHash.equals(hash)) { |
||||
this.logger.debug("Fingerprint match succeeded."); |
||||
return baseResource; |
||||
} else { |
||||
this.logger.debug("Potential resource found, but fingerprint doesn't match."); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String resolveUrl(String resourcePath, List<Resource> locations, |
||||
ResourceResolverChain chain) { |
||||
//TODO - Consider caching here for better efficiency
|
||||
String baseUrl = chain.next(this).resolveUrl(resourcePath, locations, chain); |
||||
if (StringUtils.hasText(baseUrl)) { |
||||
Resource original = chain.next(this).resolve(null, resourcePath, locations, chain); |
||||
String hash = calculateHash(original); |
||||
return StringUtils.stripFilenameExtension(baseUrl) + "-" + hash + "." + StringUtils.getFilenameExtension(baseUrl); |
||||
} |
||||
return baseUrl; |
||||
} |
||||
|
||||
/** |
||||
* @param candidate |
||||
* @return |
||||
*/ |
||||
private String calculateHash(Resource resource) { |
||||
try { |
||||
byte[] content = FileCopyUtils.copyToByteArray(resource.getInputStream()); |
||||
return DigestUtils.md5DigestAsHex(content); |
||||
} |
||||
catch (IOException e) { |
||||
this.logger.error("Failed to calculate hash on resource " + resource.toString()); |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param path |
||||
* @return |
||||
*/ |
||||
private String extractHash(String path) { |
||||
Matcher matcher = pattern.matcher(path); |
||||
if (matcher.find()) { |
||||
this.logger.debug("Found fingerprint in path: " + matcher.group(1)); |
||||
String match = matcher.group(1); |
||||
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match; |
||||
} else { |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.URI; |
||||
import java.net.URL; |
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.AbstractResource; |
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class GzipResourceResolver extends AbstractResourceResolver { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
@Override |
||||
protected Resource resolveInternal(HttpServletRequest request, String path, |
||||
List<Resource> locations, ResourceResolverChain chain, Resource resolved) { |
||||
|
||||
if (!isGzipAccepted(request) || resolved == null) { |
||||
return resolved; |
||||
} |
||||
|
||||
try { |
||||
Resource gzipped = new GzippedResource(resolved); |
||||
if (gzipped.exists()) { |
||||
return gzipped; |
||||
} |
||||
} catch (IOException e) { |
||||
this.logger.trace("Error occurred locating gzipped resource", e); |
||||
} |
||||
return resolved; |
||||
} |
||||
|
||||
/** |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
private boolean isGzipAccepted(HttpServletRequest request) { |
||||
String val = request.getHeader("Accept-Encoding"); |
||||
return val != null && val.toLowerCase().contains("gzip"); |
||||
} |
||||
|
||||
private static final class GzippedResource extends AbstractResource implements EncodedResource { |
||||
|
||||
private final Resource original; |
||||
|
||||
private final Resource gzipped; |
||||
|
||||
public GzippedResource(Resource original) throws IOException { |
||||
this.original = original; |
||||
this.gzipped = original.createRelative(original.getFilename()+".gz"); |
||||
} |
||||
|
||||
public InputStream getInputStream() throws IOException { |
||||
return gzipped.getInputStream(); |
||||
} |
||||
|
||||
public boolean exists() { |
||||
return gzipped.exists(); |
||||
} |
||||
|
||||
public boolean isReadable() { |
||||
return gzipped.isReadable(); |
||||
} |
||||
|
||||
public boolean isOpen() { |
||||
return gzipped.isOpen(); |
||||
} |
||||
|
||||
public URL getURL() throws IOException { |
||||
return gzipped.getURL(); |
||||
} |
||||
|
||||
public URI getURI() throws IOException { |
||||
return gzipped.getURI(); |
||||
} |
||||
|
||||
public File getFile() throws IOException { |
||||
return gzipped.getFile(); |
||||
} |
||||
|
||||
public long contentLength() throws IOException { |
||||
return gzipped.contentLength(); |
||||
} |
||||
|
||||
public long lastModified() throws IOException { |
||||
return gzipped.lastModified(); |
||||
} |
||||
|
||||
public Resource createRelative(String relativePath) throws IOException { |
||||
return gzipped.createRelative(relativePath); |
||||
} |
||||
|
||||
public String getFilename() { |
||||
return original.getFilename(); |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return gzipped.getDescription(); |
||||
} |
||||
|
||||
public String getEncoding() { |
||||
return "gzip"; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.lesscss.LessCompiler; |
||||
import org.lesscss.LessException; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class LessResourceTransformer implements ResourceTransformer { |
||||
|
||||
private static final String LESS_EXT = "less"; |
||||
|
||||
private final LessCompiler compiler = new LessCompiler(); |
||||
|
||||
@Override |
||||
public Resource transform(Resource original) throws IOException { |
||||
TransformedResource transformed; |
||||
try { |
||||
String content = ""; |
||||
if (original instanceof TransformedResource) { |
||||
content = ((TransformedResource) original).getContentAsString(); |
||||
} else { |
||||
content = compiler.compile(original.getFile()); |
||||
} |
||||
transformed = new TransformedResource(original.getFilename() |
||||
.replace("."+LESS_EXT, ""), content.getBytes("UTF-8"), original.lastModified()); |
||||
} |
||||
catch (LessException le) { |
||||
//TODO - Nicely print out the compilation error
|
||||
le.printStackTrace(); |
||||
return null; |
||||
} |
||||
|
||||
return transformed; |
||||
} |
||||
|
||||
@Override |
||||
public boolean handles(HttpServletRequest request, Resource original) { |
||||
return LESS_EXT.equals(StringUtils.getFilenameExtension(original.getFilename())); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public interface ResourceResolver { |
||||
|
||||
public Resource resolve(HttpServletRequest request, String path, List<Resource> locations, ResourceResolverChain chain); |
||||
|
||||
public String resolveUrl(String resourcePath, List<Resource> locations, |
||||
ResourceResolverChain chain); |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public interface ResourceResolverChain { |
||||
|
||||
public Resource resolveAndTransform(HttpServletRequest request, String path, List<Resource> locations) |
||||
throws IOException; |
||||
|
||||
public ResourceResolver next(ResourceResolver current); |
||||
|
||||
public String resolveUrl(String resourcePath, List<Resource> locations); |
||||
} |
||||
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
|
||||
/** |
||||
* More than meets the eye. |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public interface ResourceTransformer { |
||||
|
||||
public Resource transform(Resource original) throws IOException; |
||||
|
||||
public boolean handles(HttpServletRequest request, Resource original); |
||||
|
||||
} |
||||
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.HttpServletResponseWrapper; |
||||
|
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.context.support.WebApplicationContextUtils; |
||||
import org.springframework.web.filter.OncePerRequestFilter; |
||||
import org.springframework.web.util.UrlPathHelper; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class ResourceUrlEncodingFilter extends OncePerRequestFilter { |
||||
|
||||
private ResourceUrlMapper mapper; |
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, |
||||
HttpServletResponse response, FilterChain filterChain) |
||||
throws ServletException, IOException { |
||||
filterChain.doFilter(request, new ResourceUrlResponseWrapper(request, response)); |
||||
} |
||||
|
||||
@Override |
||||
protected void initFilterBean() throws ServletException { |
||||
WebApplicationContext appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); |
||||
this.mapper = appContext.getBean(ResourceUrlMapper.class); |
||||
} |
||||
|
||||
private class ResourceUrlResponseWrapper extends HttpServletResponseWrapper { |
||||
|
||||
private final UrlPathHelper pathHelper = new UrlPathHelper(); |
||||
|
||||
private String pathPrefix; |
||||
|
||||
private ResourceUrlResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) { |
||||
super(wrapped); |
||||
|
||||
this.pathPrefix = pathHelper.getContextPath(request); |
||||
String servletPath = pathHelper.getServletPath(request); |
||||
String appPath = pathHelper.getPathWithinApplication(request); |
||||
//This accounts for the behavior when servlet is mapped to "/"
|
||||
if (!servletPath.equals(appPath)) { |
||||
this.pathPrefix += pathHelper.getServletPath(request); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String encodeURL(String url) { |
||||
if(url.startsWith(pathPrefix)) { |
||||
String relativeUrl = url.replaceFirst(pathPrefix, ""); |
||||
if (!relativeUrl.startsWith("/")) { |
||||
relativeUrl = "/" + relativeUrl; |
||||
} |
||||
if (mapper.isResourceUrl(relativeUrl)) { |
||||
String resourceUrl = mapper.getUrlForResource(relativeUrl); |
||||
if (resourceUrl != null) { |
||||
return resourceUrl; |
||||
} |
||||
} |
||||
} |
||||
return super.encodeURL(url); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.context.event.ContextRefreshedEvent; |
||||
import org.springframework.core.OrderComparator; |
||||
import org.springframework.util.AntPathMatcher; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.PathMatcher; |
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class ResourceUrlMapper implements BeanPostProcessor, ApplicationListener<ContextRefreshedEvent>{ |
||||
|
||||
private final Map<String, ResourceHttpRequestHandler> handlers = new LinkedHashMap<String, ResourceHttpRequestHandler>(); |
||||
|
||||
private final List<SimpleUrlHandlerMapping> mappings = new ArrayList<SimpleUrlHandlerMapping>(); |
||||
|
||||
private final PathMatcher matcher = new AntPathMatcher(); |
||||
|
||||
@Override |
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) |
||||
throws BeansException { |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) |
||||
throws BeansException { |
||||
if (ClassUtils.isAssignableValue(SimpleUrlHandlerMapping.class, bean)) { |
||||
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) bean; |
||||
for(Entry<String, ?> mappingEntry : mapping.getUrlMap().entrySet()) { |
||||
Object val = mappingEntry.getValue(); |
||||
if (val instanceof ResourceHttpRequestHandler) { |
||||
this.mappings.add(mapping); |
||||
} |
||||
} |
||||
} |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public void onApplicationEvent(ContextRefreshedEvent event) { |
||||
OrderComparator.sort(this.mappings); |
||||
for (SimpleUrlHandlerMapping mapping : mappings) { |
||||
for(Entry<String, ?> mappingEntry : mapping.getUrlMap().entrySet()) { |
||||
Object val = mappingEntry.getValue(); |
||||
this.handlers.put(mappingEntry.getKey(), (ResourceHttpRequestHandler) val); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public String getUrlForResource(String resourcePath) { |
||||
for (Entry<String, ResourceHttpRequestHandler> mapping : this.handlers.entrySet()) { |
||||
if (matcher.match(mapping.getKey(), resourcePath)) { |
||||
ResourceHttpRequestHandler handler = mapping.getValue(); |
||||
String nestedPath = matcher.extractPathWithinPattern(mapping.getKey(), resourcePath); |
||||
String prefix = resourcePath.replace(nestedPath, ""); |
||||
String url = new DefaultResourceResolverChain(handler.getResourceResolvers(), handler. |
||||
getResourceTransformers()).resolveUrl(nestedPath, handler.getLocations()); |
||||
if (url != null) { |
||||
return prefix + url; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public boolean isResourceUrl(String relativeUrl) { |
||||
for (String mapping : this.handlers.keySet()) { |
||||
if (matcher.match(mapping, relativeUrl)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.util.Date; |
||||
|
||||
import org.springframework.core.io.ByteArrayResource; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class TransformedResource extends ByteArrayResource { |
||||
|
||||
private final String filename; |
||||
private final long lastModified; |
||||
|
||||
public TransformedResource(String filename, byte[] transformedContent) { |
||||
super(transformedContent); |
||||
this.filename = filename; |
||||
this.lastModified = new Date().getTime(); |
||||
} |
||||
|
||||
public TransformedResource(String filename, byte[] transformedContent, long lastModified) { |
||||
super(transformedContent); |
||||
this.filename = filename; |
||||
this.lastModified = lastModified; |
||||
} |
||||
|
||||
@Override |
||||
public String getFilename() { |
||||
return this.filename; |
||||
} |
||||
|
||||
@Override |
||||
public long lastModified() throws IOException { |
||||
return this.lastModified; |
||||
} |
||||
|
||||
public String getContentAsString() { |
||||
try { |
||||
return new String(getByteArray(), "UTF-8"); |
||||
} |
||||
catch (UnsupportedEncodingException e) { |
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace(); |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
function foo() { console.log("hello bar"); } |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
function foo() { console.log("hello world"); } |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class ExtensionMappingResourceResolverTests { |
||||
|
||||
private ResourceResolverChain resolver; |
||||
|
||||
private List<Resource> locations; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>(); |
||||
resolvers.add(new ExtensionMappingResourceResolver()); |
||||
resolver = new DefaultResourceResolverChain(resolvers, new ArrayList<ResourceTransformer>()); |
||||
locations = new ArrayList<Resource>(); |
||||
locations.add(new ClassPathResource("test/", getClass())); |
||||
locations.add(new ClassPathResource("testalternatepath/", getClass())); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveLessResource() throws Exception { |
||||
String resourceId = "zoo.css"; |
||||
Resource resource = new ClassPathResource("test/"+resourceId+".less", getClass()); |
||||
Resource resolved = resolver.resolveAndTransform(null, resourceId, locations); |
||||
assertEquals(resource, resolved); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveLessUrl() { |
||||
String resourceId = "zoo.css"; |
||||
String url = "zoo.css"; |
||||
assertEquals(url, resolver.resolveUrl(resourceId, locations)); |
||||
} |
||||
} |
||||
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.DigestUtils; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class FingerprintingResourceResolverTests { |
||||
|
||||
private ResourceResolverChain chain; |
||||
|
||||
private FingerprintingResourceResolver resolver = new FingerprintingResourceResolver(); |
||||
|
||||
private List<Resource> locations; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>(); |
||||
resolvers.add(resolver); |
||||
chain = new DefaultResourceResolverChain(resolvers, new ArrayList<ResourceTransformer>()); |
||||
locations = new ArrayList<Resource>(); |
||||
locations.add(new ClassPathResource("test/", getClass())); |
||||
locations.add(new ClassPathResource("testalternatepath/", getClass())); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveStaticFingerprintedResource() throws Exception { |
||||
String file = "foo-e36d2e05253c6c7085a91522ce43a0b4.css"; |
||||
Resource resource = new ClassPathResource("test/"+file, getClass()); |
||||
Resource resolved = chain.resolveAndTransform(null, file, locations); |
||||
assertEquals(resource, resolved); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveDynamicFingerprintedResource() throws Exception { |
||||
Resource resource = new ClassPathResource("test/bar.css", getClass()); |
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(resource.getInputStream())); |
||||
String path = "/bar-"+hash+".css"; |
||||
|
||||
Resource resolved = chain.resolveAndTransform(null, path, locations); |
||||
|
||||
assertEquals(resource, resolved); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveWithMultipleExtensions() throws Exception { |
||||
Resource resource = new ClassPathResource("test/bar.min.css", getClass()); |
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(resource.getInputStream())); |
||||
String path = "/bar.min-"+hash+".css"; |
||||
|
||||
Resource resolved = chain.resolveAndTransform(null, path, locations); |
||||
|
||||
assertEquals(resource, resolved); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveWithMultipleHyphens() throws Exception { |
||||
Resource resource = new ClassPathResource("test/foo-bar/foo-bar.css", getClass()); |
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(resource.getInputStream())); |
||||
String path = "/foo-bar/foo-bar-"+hash+".css"; |
||||
|
||||
Resource resolved = chain.resolveAndTransform(null, path, locations); |
||||
|
||||
assertEquals(resource, resolved); |
||||
} |
||||
|
||||
@Test |
||||
public void extractHash() throws Exception { |
||||
String hash = "7fbe76cdac6093784895bb4989203e5a"; |
||||
String path = "font-awesome/css/font-awesome.min-"+hash+".css"; |
||||
|
||||
Method extractHash = ReflectionUtils.findMethod(FingerprintingResourceResolver.class, "extractHash", String.class); |
||||
ReflectionUtils.makeAccessible(extractHash); |
||||
String result = (String) ReflectionUtils.invokeMethod(extractHash, resolver, path); |
||||
assertEquals(hash, result); |
||||
} |
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.zip.GZIPOutputStream; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.FileSystemResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.mock.web.test.MockHttpServletRequest; |
||||
import org.springframework.util.FileCopyUtils; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class GzipResourceResolverTests { |
||||
|
||||
private ResourceResolverChain resolver; |
||||
|
||||
private List<Resource> locations; |
||||
|
||||
@BeforeClass |
||||
public static void createGzippedResources() throws IOException { |
||||
Resource location = new ClassPathResource("test/", GzipResourceResolverTests.class); |
||||
Resource jsFile = new FileSystemResource(location.createRelative("/js/foo.js").getFile()); |
||||
Resource gzJsFile = jsFile.createRelative("foo.js.gz"); |
||||
Resource fingerPrintedFile = new FileSystemResource(location.createRelative("foo-e36d2e05253c6c7085a91522ce43a0b4.css").getFile()); |
||||
Resource gzFingerPrintedFile = fingerPrintedFile.createRelative("foo-e36d2e05253c6c7085a91522ce43a0b4.css.gz"); |
||||
|
||||
if (gzJsFile.getFile().createNewFile()) { |
||||
GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(gzJsFile.getFile())); |
||||
FileCopyUtils.copy(jsFile.getInputStream(), out); |
||||
} |
||||
|
||||
if (gzFingerPrintedFile.getFile().createNewFile()) { |
||||
GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(gzFingerPrintedFile.getFile())); |
||||
FileCopyUtils.copy(fingerPrintedFile.getInputStream(), out); |
||||
} |
||||
|
||||
assertTrue(gzJsFile.exists()); |
||||
assertTrue(gzFingerPrintedFile.exists()); |
||||
} |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>(); |
||||
resolvers.add(new GzipResourceResolver()); |
||||
resolvers.add(new FingerprintingResourceResolver()); |
||||
resolver = new DefaultResourceResolverChain(resolvers, new ArrayList<ResourceTransformer>()); |
||||
locations = new ArrayList<Resource>(); |
||||
locations.add(new ClassPathResource("test/", getClass())); |
||||
locations.add(new ClassPathResource("testalternatepath/", getClass())); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveGzippedFile() throws IOException { |
||||
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||
request.addHeader("Accept-Encoding", "gzip"); |
||||
String file = "js/foo.js"; |
||||
String gzFile = file+".gz"; |
||||
Resource resource = new ClassPathResource("test/"+gzFile, getClass()); |
||||
Resource resolved = resolver.resolveAndTransform(request, file, locations); |
||||
assertEquals(resource.getDescription(), resolved.getDescription()); |
||||
assertEquals(new ClassPathResource("test/"+file).getFilename(), resolved.getFilename()); |
||||
assertTrue("Expected " + resolved + " to be of type " + EncodedResource.class, resolved instanceof EncodedResource); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveFingerprintedGzippedFile() throws IOException { |
||||
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||
request.addHeader("Accept-Encoding", "gzip"); |
||||
String file = "foo-e36d2e05253c6c7085a91522ce43a0b4.css"; |
||||
String gzFile = file+".gz"; |
||||
Resource resource = new ClassPathResource("test/"+gzFile, getClass()); |
||||
Resource resolved = resolver.resolveAndTransform(request, file, locations); |
||||
assertEquals(resource.getDescription(), resolved.getDescription()); |
||||
assertEquals(new ClassPathResource("test/"+file).getFilename(), resolved.getFilename()); |
||||
assertTrue("Expected " + resolved + " to be of type " + EncodedResource.class, resolved instanceof EncodedResource); |
||||
} |
||||
} |
||||
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.resource; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Jeremy Grelle |
||||
*/ |
||||
public class ResourceUrlMapperTests { |
||||
|
||||
ResourceHttpRequestHandler handler; |
||||
|
||||
SimpleUrlHandlerMapping mapping; |
||||
|
||||
ResourceUrlMapper mapper; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
List<Resource> resourcePaths = new ArrayList<Resource>(); |
||||
resourcePaths.add(new ClassPathResource("test/", getClass())); |
||||
resourcePaths.add(new ClassPathResource("testalternatepath/", getClass())); |
||||
|
||||
Map<String, ResourceHttpRequestHandler> urlMap = new HashMap<String, ResourceHttpRequestHandler>(); |
||||
handler = new ResourceHttpRequestHandler(); |
||||
handler.setLocations(resourcePaths); |
||||
urlMap.put("/resources/**", handler); |
||||
|
||||
mapping = new SimpleUrlHandlerMapping(); |
||||
mapping.setUrlMap(urlMap); |
||||
} |
||||
|
||||
private void resetMapper() { |
||||
mapper = new ResourceUrlMapper(); |
||||
mapper.postProcessAfterInitialization(mapping, "resourceMapping"); |
||||
mapper.onApplicationEvent(null); |
||||
} |
||||
|
||||
@Test |
||||
public void getStaticResourceUrl() { |
||||
resetMapper(); |
||||
|
||||
String url = mapper.getUrlForResource("/resources/foo.css"); |
||||
assertEquals("/resources/foo.css", url); |
||||
} |
||||
|
||||
@Test |
||||
public void getFingerprintedResourceUrl() { |
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>(); |
||||
resolvers.add(new FingerprintingResourceResolver()); |
||||
handler.setResourceResolvers(resolvers); |
||||
resetMapper(); |
||||
|
||||
String url = mapper.getUrlForResource("/resources/foo.css"); |
||||
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url); |
||||
} |
||||
|
||||
@Test |
||||
public void getExtensionMappedResourceUrl() { |
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>(); |
||||
resolvers.add(new ExtensionMappingResourceResolver()); |
||||
handler.setResourceResolvers(resolvers); |
||||
resetMapper(); |
||||
|
||||
String url = mapper.getUrlForResource("/resources/zoo.css"); |
||||
assertEquals("/resources/zoo.css", url); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
h2 { color:white; } |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
h1 { color:red; } |
||||
Loading…
Reference in new issue