From a8ed98255f350c5217f897c930e4d5600a8ab17f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 May 2010 12:41:32 +0000 Subject: [PATCH] ServletContextResourcePatternResolver handles "/WEB-INF/lib/*.jar!/**/context.xml" style patterns (SPR-7198) --- ...ServletContextResourcePatternResolver.java | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextResourcePatternResolver.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextResourcePatternResolver.java index 220540f2ff..b40f7dbecb 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextResourcePatternResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextResourcePatternResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -17,14 +17,21 @@ package org.springframework.web.context.support; import java.io.IOException; -import java.util.Iterator; +import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import javax.servlet.ServletContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.UrlResource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; /** @@ -38,6 +45,9 @@ import org.springframework.util.StringUtils; */ public class ServletContextResourcePatternResolver extends PathMatchingResourcePatternResolver { + private static final Log logger = LogFactory.getLog(ServletContextResourcePatternResolver.class); + + /** * Create a new ServletContextResourcePatternResolver. * @param servletContext the ServletContext to load resources with @@ -102,8 +112,15 @@ public class ServletContextResourcePatternResolver extends PathMatchingResourceP Set candidates = servletContext.getResourcePaths(dir); if (candidates != null) { boolean dirDepthNotFixed = fullPattern.contains("**"); - for (Iterator it = candidates.iterator(); it.hasNext();) { - String currPath = (String) it.next(); + int jarFileSep = fullPattern.indexOf(ResourceUtils.JAR_URL_SEPARATOR); + String jarFilePath = null; + String pathInJarFile = null; + if (jarFileSep > 0 && jarFileSep + ResourceUtils.JAR_URL_SEPARATOR.length() < fullPattern.length()) { + jarFilePath = fullPattern.substring(0, jarFileSep); + pathInJarFile = fullPattern.substring(jarFileSep + ResourceUtils.JAR_URL_SEPARATOR.length()); + } + for (Object candidate : candidates) { + String currPath = (String) candidate; if (!currPath.startsWith(dir)) { // Returned resource path does not start with relative directory: // assuming absolute path returned -> strip absolute path. @@ -112,13 +129,19 @@ public class ServletContextResourcePatternResolver extends PathMatchingResourceP currPath = currPath.substring(dirIndex); } } - if (currPath.endsWith("/") && - (dirDepthNotFixed || - StringUtils.countOccurrencesOf(currPath, "/") <= StringUtils.countOccurrencesOf(fullPattern, "/"))) { + if (currPath.endsWith("/") && (dirDepthNotFixed || StringUtils.countOccurrencesOf(currPath, "/") <= + StringUtils.countOccurrencesOf(fullPattern, "/"))) { // Search subdirectories recursively: ServletContext.getResourcePaths // only returns entries for one directory level. doRetrieveMatchingServletContextResources(servletContext, fullPattern, currPath, result); } + if (jarFilePath != null && getPathMatcher().match(jarFilePath, currPath)) { + // Base pattern matches a jar file - search for matching entries within. + String absoluteJarPath = servletContext.getRealPath(currPath); + if (absoluteJarPath != null) { + doRetrieveMatchingJarEntries(absoluteJarPath, pathInJarFile, result); + } + } if (getPathMatcher().match(fullPattern, currPath)) { result.add(new ServletContextResource(servletContext, currPath)); } @@ -126,4 +149,36 @@ public class ServletContextResourcePatternResolver extends PathMatchingResourceP } } + /** + * Method extracts entries from the given jar by pattern. + * @param jarFilePath the path to the jar file + * @param entryPattern the pattern for jar entries to match + * @param result the Set of matching Resources to add to + * @throws IOException if jar contents could not be retrieved + */ + @SuppressWarnings("unchecked") + private void doRetrieveMatchingJarEntries(String jarFilePath, String entryPattern, Set result) { + if (logger.isDebugEnabled()) { + logger.debug("Searching jar file [" + jarFilePath + "] for entries matching [" + entryPattern + "]"); + } + try { + JarFile jarFile = new JarFile(jarFilePath); + for (Enumeration entries = jarFile.entries(); entries.hasMoreElements();) { + JarEntry entry = entries.nextElement(); + String entryPath = entry.getName(); + if (getPathMatcher().match(entryPattern, entryPath)) { + result.add(new UrlResource(ResourceUtils.URL_PROTOCOL_JAR + ":" + + ResourceUtils.URL_PROTOCOL_FILE + ":" + jarFilePath + + ResourceUtils.JAR_URL_SEPARATOR + entryPath)); + } + } + } + catch (IOException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Cannot search for matching resources in jar file [" + jarFilePath + + "] because the jar cannot be opened through the file system", ex); + } + } + } + }