From 1c3a668370cdac328b2f9920d68f170c99a2a76c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Oct 2015 20:09:21 +0200 Subject: [PATCH] AbstractFileResolvingResource uses extractArchiveURL for last-modified check Issue: SPR-13393 (cherry picked from commit 302a069) --- .../io/AbstractFileResolvingResource.java | 4 +- .../springframework/util/ResourceUtils.java | 49 +++++++++++++++---- .../util/ResourceUtilsTests.java | 31 ++++++++++-- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index c58ea9aba4..a81f6c3818 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -60,7 +60,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { protected File getFileForLastModifiedCheck() throws IOException { URL url = getURL(); if (ResourceUtils.isJarURL(url)) { - URL actualUrl = ResourceUtils.extractJarFileURL(url); + URL actualUrl = ResourceUtils.extractArchiveURL(url); if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(actualUrl).getFile(); } diff --git a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java index 945eead6cc..3ea92707e1 100644 --- a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java @@ -57,9 +57,12 @@ public abstract class ResourceUtils { /** URL prefix for loading from the file system: "file:" */ public static final String FILE_URL_PREFIX = "file:"; - /** URL prefix for loading from the file system: "jar:" */ + /** URL prefix for loading from a jar file: "jar:" */ public static final String JAR_URL_PREFIX = "jar:"; + /** URL prefix for loading from a war file on Tomcat: "war:" */ + public static final String WAR_URL_PREFIX = "war:"; + /** URL protocol for a file in the file system: "file" */ public static final String URL_PROTOCOL_FILE = "file"; @@ -69,9 +72,6 @@ public abstract class ResourceUtils { /** URL protocol for an entry from a zip file: "zip" */ public static final String URL_PROTOCOL_ZIP = "zip"; - /** URL protocol for an entry from a Tomcat war file: "war" */ - public static final String URL_PROTOCOL_WAR = "war"; - /** URL protocol for an entry from a WebSphere jar file: "wsjar" */ public static final String URL_PROTOCOL_WSJAR = "wsjar"; @@ -90,6 +90,9 @@ public abstract class ResourceUtils { /** Separator between JAR URL and file path within the JAR: "!/" */ public static final String JAR_URL_SEPARATOR = "!/"; + /** Special separator between WAR URL and jar part on Tomcat */ + public static final String WAR_URL_SEPARATOR = "*/"; + /** * Return whether the given resource location is a URL: @@ -278,8 +281,7 @@ public abstract class ResourceUtils { public static boolean isJarURL(URL url) { String protocol = url.getProtocol(); return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) || - URL_PROTOCOL_WAR.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol) || - URL_PROTOCOL_VFSZIP.equals(protocol)); + URL_PROTOCOL_VFSZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol)); } /** @@ -303,10 +305,9 @@ public abstract class ResourceUtils { */ public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException { String urlFile = jarUrl.getFile(); - int startIndex = (urlFile.startsWith(JAR_URL_PREFIX) ? JAR_URL_PREFIX.length() : 0); - int endIndex = urlFile.indexOf(JAR_URL_SEPARATOR); - if (endIndex != -1) { - String jarFile = urlFile.substring(startIndex, endIndex); + int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR); + if (separatorIndex != -1) { + String jarFile = urlFile.substring(0, separatorIndex); try { return new URL(jarFile); } @@ -324,6 +325,34 @@ public abstract class ResourceUtils { } } + /** + * Extract the URL for the outermost archive from the given jar/war URL + * (which may point to a resource in a jar file or to a jar file itself). + *

In the case of a jar file nested within a war file, this will return + * a URL to the war file since that is the one resolvable in the file system. + * @param jarUrl the original URL + * @return the URL for the actual jar file + * @throws MalformedURLException if no valid jar file URL could be extracted + * @since 4.1.8 + * @see #extractJarFileURL(URL) + */ + public static URL extractArchiveURL(URL jarUrl) throws MalformedURLException { + String urlFile = jarUrl.getFile(); + + int endIndex = urlFile.indexOf(WAR_URL_SEPARATOR); + if (endIndex != -1) { + // Tomcat's "jar:war:file:...mywar.war*/WEB-INF/lib/myjar.jar!/myentry.txt" + String warFile = urlFile.substring(0, endIndex); + int startIndex = warFile.indexOf(WAR_URL_PREFIX); + if (startIndex != -1) { + return new URL(warFile.substring(startIndex + WAR_URL_PREFIX.length())); + } + } + + // Regular "jar:file:...myjar.jar!/myentry.txt" + return extractJarFileURL(jarUrl); + } + /** * Create a URI instance for the given URL, * replacing spaces with "%20" URI encoding first. diff --git a/spring-core/src/test/java/org/springframework/util/ResourceUtilsTests.java b/spring-core/src/test/java/org/springframework/util/ResourceUtilsTests.java index 1115e3b09a..d70b383114 100644 --- a/spring-core/src/test/java/org/springframework/util/ResourceUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/ResourceUtilsTests.java @@ -35,7 +35,7 @@ public class ResourceUtilsTests { assertTrue(ResourceUtils.isJarURL(new URL("jar:file:myjar.jar!/mypath"))); assertTrue(ResourceUtils.isJarURL(new URL(null, "zip:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); assertTrue(ResourceUtils.isJarURL(new URL(null, "wsjar:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); - assertTrue(ResourceUtils.isJarURL(new URL(null, "war:jar:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); + assertTrue(ResourceUtils.isJarURL(new URL(null, "jar:war:file:mywar.war*/myjar.jar!/mypath", new DummyURLStreamHandler()))); assertFalse(ResourceUtils.isJarURL(new URL("file:myjar.jar"))); assertFalse(ResourceUtils.isJarURL(new URL("http:myserver/myjar.jar"))); } @@ -50,19 +50,40 @@ public class ResourceUtilsTests { ResourceUtils.extractJarFileURL(new URL(null, "zip:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); assertEquals(new URL("file:myjar.jar"), ResourceUtils.extractJarFileURL(new URL(null, "wsjar:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); - assertEquals(new URL("file:myjar.jar"), - ResourceUtils.extractJarFileURL(new URL(null, "war:jar:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractJarFileURL(new URL("file:myjar.jar"))); assertEquals(new URL("file:myjar.jar"), ResourceUtils.extractJarFileURL(new URL("jar:file:myjar.jar!/"))); assertEquals(new URL("file:myjar.jar"), ResourceUtils.extractJarFileURL(new URL(null, "zip:file:myjar.jar!/", new DummyURLStreamHandler()))); assertEquals(new URL("file:myjar.jar"), ResourceUtils.extractJarFileURL(new URL(null, "wsjar:file:myjar.jar!/", new DummyURLStreamHandler()))); + } + + @Test + public void extractArchiveURL() throws Exception { + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractArchiveURL(new URL("jar:file:myjar.jar!/mypath"))); + assertEquals(new URL("file:/myjar.jar"), + ResourceUtils.extractArchiveURL(new URL(null, "jar:myjar.jar!/mypath", new DummyURLStreamHandler()))); assertEquals(new URL("file:myjar.jar"), - ResourceUtils.extractJarFileURL(new URL(null, "war:jar:file:myjar.jar!/", new DummyURLStreamHandler()))); + ResourceUtils.extractArchiveURL(new URL(null, "zip:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); assertEquals(new URL("file:myjar.jar"), - ResourceUtils.extractJarFileURL(new URL("file:myjar.jar"))); + ResourceUtils.extractArchiveURL(new URL(null, "wsjar:file:myjar.jar!/mypath", new DummyURLStreamHandler()))); + assertEquals(new URL("file:mywar.war"), + ResourceUtils.extractArchiveURL(new URL(null, "jar:war:file:mywar.war*/myjar.jar!/mypath", new DummyURLStreamHandler()))); + + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractArchiveURL(new URL("file:myjar.jar"))); + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractArchiveURL(new URL("jar:file:myjar.jar!/"))); + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractArchiveURL(new URL(null, "zip:file:myjar.jar!/", new DummyURLStreamHandler()))); + assertEquals(new URL("file:myjar.jar"), + ResourceUtils.extractArchiveURL(new URL(null, "wsjar:file:myjar.jar!/", new DummyURLStreamHandler()))); + assertEquals(new URL("file:mywar.war"), + ResourceUtils.extractArchiveURL(new URL(null, "jar:war:file:mywar.war*/myjar.jar!/", new DummyURLStreamHandler()))); }