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.util; 018 019import java.io.File; 020import java.io.FileNotFoundException; 021import java.net.MalformedURLException; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.net.URL; 025import java.net.URLConnection; 026 027import org.springframework.lang.Nullable; 028 029/** 030 * Utility methods for resolving resource locations to files in the 031 * file system. Mainly for internal use within the framework. 032 * 033 * <p>Consider using Spring's Resource abstraction in the core package 034 * for handling all kinds of file resources in a uniform manner. 035 * {@link org.springframework.core.io.ResourceLoader}'s {@code getResource()} 036 * method can resolve any location to a {@link org.springframework.core.io.Resource} 037 * object, which in turn allows one to obtain a {@code java.io.File} in the 038 * file system through its {@code getFile()} method. 039 * 040 * @author Juergen Hoeller 041 * @since 1.1.5 042 * @see org.springframework.core.io.Resource 043 * @see org.springframework.core.io.ClassPathResource 044 * @see org.springframework.core.io.FileSystemResource 045 * @see org.springframework.core.io.UrlResource 046 * @see org.springframework.core.io.ResourceLoader 047 */ 048public abstract class ResourceUtils { 049 050 /** Pseudo URL prefix for loading from the class path: "classpath:". */ 051 public static final String CLASSPATH_URL_PREFIX = "classpath:"; 052 053 /** URL prefix for loading from the file system: "file:". */ 054 public static final String FILE_URL_PREFIX = "file:"; 055 056 /** URL prefix for loading from a jar file: "jar:". */ 057 public static final String JAR_URL_PREFIX = "jar:"; 058 059 /** URL prefix for loading from a war file on Tomcat: "war:". */ 060 public static final String WAR_URL_PREFIX = "war:"; 061 062 /** URL protocol for a file in the file system: "file". */ 063 public static final String URL_PROTOCOL_FILE = "file"; 064 065 /** URL protocol for an entry from a jar file: "jar". */ 066 public static final String URL_PROTOCOL_JAR = "jar"; 067 068 /** URL protocol for an entry from a war file: "war". */ 069 public static final String URL_PROTOCOL_WAR = "war"; 070 071 /** URL protocol for an entry from a zip file: "zip". */ 072 public static final String URL_PROTOCOL_ZIP = "zip"; 073 074 /** URL protocol for an entry from a WebSphere jar file: "wsjar". */ 075 public static final String URL_PROTOCOL_WSJAR = "wsjar"; 076 077 /** URL protocol for an entry from a JBoss jar file: "vfszip". */ 078 public static final String URL_PROTOCOL_VFSZIP = "vfszip"; 079 080 /** URL protocol for a JBoss file system resource: "vfsfile". */ 081 public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; 082 083 /** URL protocol for a general JBoss VFS resource: "vfs". */ 084 public static final String URL_PROTOCOL_VFS = "vfs"; 085 086 /** File extension for a regular jar file: ".jar". */ 087 public static final String JAR_FILE_EXTENSION = ".jar"; 088 089 /** Separator between JAR URL and file path within the JAR: "!/". */ 090 public static final String JAR_URL_SEPARATOR = "!/"; 091 092 /** Special separator between WAR URL and jar part on Tomcat. */ 093 public static final String WAR_URL_SEPARATOR = "*/"; 094 095 096 /** 097 * Return whether the given resource location is a URL: 098 * either a special "classpath" pseudo URL or a standard URL. 099 * @param resourceLocation the location String to check 100 * @return whether the location qualifies as a URL 101 * @see #CLASSPATH_URL_PREFIX 102 * @see java.net.URL 103 */ 104 public static boolean isUrl(@Nullable String resourceLocation) { 105 if (resourceLocation == null) { 106 return false; 107 } 108 if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { 109 return true; 110 } 111 try { 112 new URL(resourceLocation); 113 return true; 114 } 115 catch (MalformedURLException ex) { 116 return false; 117 } 118 } 119 120 /** 121 * Resolve the given resource location to a {@code java.net.URL}. 122 * <p>Does not check whether the URL actually exists; simply returns 123 * the URL that the given location would correspond to. 124 * @param resourceLocation the resource location to resolve: either a 125 * "classpath:" pseudo URL, a "file:" URL, or a plain file path 126 * @return a corresponding URL object 127 * @throws FileNotFoundException if the resource cannot be resolved to a URL 128 */ 129 public static URL getURL(String resourceLocation) throws FileNotFoundException { 130 Assert.notNull(resourceLocation, "Resource location must not be null"); 131 if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { 132 String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length()); 133 ClassLoader cl = ClassUtils.getDefaultClassLoader(); 134 URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path)); 135 if (url == null) { 136 String description = "class path resource [" + path + "]"; 137 throw new FileNotFoundException(description + 138 " cannot be resolved to URL because it does not exist"); 139 } 140 return url; 141 } 142 try { 143 // try URL 144 return new URL(resourceLocation); 145 } 146 catch (MalformedURLException ex) { 147 // no URL -> treat as file path 148 try { 149 return new File(resourceLocation).toURI().toURL(); 150 } 151 catch (MalformedURLException ex2) { 152 throw new FileNotFoundException("Resource location [" + resourceLocation + 153 "] is neither a URL not a well-formed file path"); 154 } 155 } 156 } 157 158 /** 159 * Resolve the given resource location to a {@code java.io.File}, 160 * i.e. to a file in the file system. 161 * <p>Does not check whether the file actually exists; simply returns 162 * the File that the given location would correspond to. 163 * @param resourceLocation the resource location to resolve: either a 164 * "classpath:" pseudo URL, a "file:" URL, or a plain file path 165 * @return a corresponding File object 166 * @throws FileNotFoundException if the resource cannot be resolved to 167 * a file in the file system 168 */ 169 public static File getFile(String resourceLocation) throws FileNotFoundException { 170 Assert.notNull(resourceLocation, "Resource location must not be null"); 171 if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { 172 String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length()); 173 String description = "class path resource [" + path + "]"; 174 ClassLoader cl = ClassUtils.getDefaultClassLoader(); 175 URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path)); 176 if (url == null) { 177 throw new FileNotFoundException(description + 178 " cannot be resolved to absolute file path because it does not exist"); 179 } 180 return getFile(url, description); 181 } 182 try { 183 // try URL 184 return getFile(new URL(resourceLocation)); 185 } 186 catch (MalformedURLException ex) { 187 // no URL -> treat as file path 188 return new File(resourceLocation); 189 } 190 } 191 192 /** 193 * Resolve the given resource URL to a {@code java.io.File}, 194 * i.e. to a file in the file system. 195 * @param resourceUrl the resource URL to resolve 196 * @return a corresponding File object 197 * @throws FileNotFoundException if the URL cannot be resolved to 198 * a file in the file system 199 */ 200 public static File getFile(URL resourceUrl) throws FileNotFoundException { 201 return getFile(resourceUrl, "URL"); 202 } 203 204 /** 205 * Resolve the given resource URL to a {@code java.io.File}, 206 * i.e. to a file in the file system. 207 * @param resourceUrl the resource URL to resolve 208 * @param description a description of the original resource that 209 * the URL was created for (for example, a class path location) 210 * @return a corresponding File object 211 * @throws FileNotFoundException if the URL cannot be resolved to 212 * a file in the file system 213 */ 214 public static File getFile(URL resourceUrl, String description) throws FileNotFoundException { 215 Assert.notNull(resourceUrl, "Resource URL must not be null"); 216 if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) { 217 throw new FileNotFoundException( 218 description + " cannot be resolved to absolute file path " + 219 "because it does not reside in the file system: " + resourceUrl); 220 } 221 try { 222 return new File(toURI(resourceUrl).getSchemeSpecificPart()); 223 } 224 catch (URISyntaxException ex) { 225 // Fallback for URLs that are not valid URIs (should hardly ever happen). 226 return new File(resourceUrl.getFile()); 227 } 228 } 229 230 /** 231 * Resolve the given resource URI to a {@code java.io.File}, 232 * i.e. to a file in the file system. 233 * @param resourceUri the resource URI to resolve 234 * @return a corresponding File object 235 * @throws FileNotFoundException if the URL cannot be resolved to 236 * a file in the file system 237 * @since 2.5 238 */ 239 public static File getFile(URI resourceUri) throws FileNotFoundException { 240 return getFile(resourceUri, "URI"); 241 } 242 243 /** 244 * Resolve the given resource URI to a {@code java.io.File}, 245 * i.e. to a file in the file system. 246 * @param resourceUri the resource URI to resolve 247 * @param description a description of the original resource that 248 * the URI was created for (for example, a class path location) 249 * @return a corresponding File object 250 * @throws FileNotFoundException if the URL cannot be resolved to 251 * a file in the file system 252 * @since 2.5 253 */ 254 public static File getFile(URI resourceUri, String description) throws FileNotFoundException { 255 Assert.notNull(resourceUri, "Resource URI must not be null"); 256 if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) { 257 throw new FileNotFoundException( 258 description + " cannot be resolved to absolute file path " + 259 "because it does not reside in the file system: " + resourceUri); 260 } 261 return new File(resourceUri.getSchemeSpecificPart()); 262 } 263 264 /** 265 * Determine whether the given URL points to a resource in the file system, 266 * i.e. has protocol "file", "vfsfile" or "vfs". 267 * @param url the URL to check 268 * @return whether the URL has been identified as a file system URL 269 */ 270 public static boolean isFileURL(URL url) { 271 String protocol = url.getProtocol(); 272 return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || 273 URL_PROTOCOL_VFS.equals(protocol)); 274 } 275 276 /** 277 * Determine whether the given URL points to a resource in a jar file. 278 * i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar". 279 * @param url the URL to check 280 * @return whether the URL has been identified as a JAR URL 281 */ 282 public static boolean isJarURL(URL url) { 283 String protocol = url.getProtocol(); 284 return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_WAR.equals(protocol) || 285 URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_VFSZIP.equals(protocol) || 286 URL_PROTOCOL_WSJAR.equals(protocol)); 287 } 288 289 /** 290 * Determine whether the given URL points to a jar file itself, 291 * that is, has protocol "file" and ends with the ".jar" extension. 292 * @param url the URL to check 293 * @return whether the URL has been identified as a JAR file URL 294 * @since 4.1 295 */ 296 public static boolean isJarFileURL(URL url) { 297 return (URL_PROTOCOL_FILE.equals(url.getProtocol()) && 298 url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION)); 299 } 300 301 /** 302 * Extract the URL for the actual jar file from the given URL 303 * (which may point to a resource in a jar file or to a jar file itself). 304 * @param jarUrl the original URL 305 * @return the URL for the actual jar file 306 * @throws MalformedURLException if no valid jar file URL could be extracted 307 */ 308 public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException { 309 String urlFile = jarUrl.getFile(); 310 int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR); 311 if (separatorIndex != -1) { 312 String jarFile = urlFile.substring(0, separatorIndex); 313 try { 314 return new URL(jarFile); 315 } 316 catch (MalformedURLException ex) { 317 // Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar". 318 // This usually indicates that the jar file resides in the file system. 319 if (!jarFile.startsWith("/")) { 320 jarFile = "/" + jarFile; 321 } 322 return new URL(FILE_URL_PREFIX + jarFile); 323 } 324 } 325 else { 326 return jarUrl; 327 } 328 } 329 330 /** 331 * Extract the URL for the outermost archive from the given jar/war URL 332 * (which may point to a resource in a jar file or to a jar file itself). 333 * <p>In the case of a jar file nested within a war file, this will return 334 * a URL to the war file since that is the one resolvable in the file system. 335 * @param jarUrl the original URL 336 * @return the URL for the actual jar file 337 * @throws MalformedURLException if no valid jar file URL could be extracted 338 * @since 4.1.8 339 * @see #extractJarFileURL(URL) 340 */ 341 public static URL extractArchiveURL(URL jarUrl) throws MalformedURLException { 342 String urlFile = jarUrl.getFile(); 343 344 int endIndex = urlFile.indexOf(WAR_URL_SEPARATOR); 345 if (endIndex != -1) { 346 // Tomcat's "war:file:...mywar.war*/WEB-INF/lib/myjar.jar!/myentry.txt" 347 String warFile = urlFile.substring(0, endIndex); 348 if (URL_PROTOCOL_WAR.equals(jarUrl.getProtocol())) { 349 return new URL(warFile); 350 } 351 int startIndex = warFile.indexOf(WAR_URL_PREFIX); 352 if (startIndex != -1) { 353 return new URL(warFile.substring(startIndex + WAR_URL_PREFIX.length())); 354 } 355 } 356 357 // Regular "jar:file:...myjar.jar!/myentry.txt" 358 return extractJarFileURL(jarUrl); 359 } 360 361 /** 362 * Create a URI instance for the given URL, 363 * replacing spaces with "%20" URI encoding first. 364 * @param url the URL to convert into a URI instance 365 * @return the URI instance 366 * @throws URISyntaxException if the URL wasn't a valid URI 367 * @see java.net.URL#toURI() 368 */ 369 public static URI toURI(URL url) throws URISyntaxException { 370 return toURI(url.toString()); 371 } 372 373 /** 374 * Create a URI instance for the given location String, 375 * replacing spaces with "%20" URI encoding first. 376 * @param location the location String to convert into a URI instance 377 * @return the URI instance 378 * @throws URISyntaxException if the location wasn't a valid URI 379 */ 380 public static URI toURI(String location) throws URISyntaxException { 381 return new URI(StringUtils.replace(location, " ", "%20")); 382 } 383 384 /** 385 * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the 386 * given connection, preferring {@code false} but leaving the 387 * flag at {@code true} for JNLP based resources. 388 * @param con the URLConnection to set the flag on 389 */ 390 public static void useCachesIfNecessary(URLConnection con) { 391 con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); 392 } 393 394}