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