1 /* 2 * Copyright 2002-2011 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.io; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.net.HttpURLConnection; 23 import java.net.URI; 24 import java.net.URL; 25 import java.net.URLConnection; 26 27 import org.springframework.util.ResourceUtils; 28 29 /** 30 * Abstract base class for resources which resolve URLs into File references, 31 * such as {@link UrlResource} or {@link ClassPathResource}. 32 * 33 * <p>Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs, 34 * resolving file system references accordingly. 35 * 36 * @author Juergen Hoeller 37 * @since 3.0 38 */ 39 public abstract class AbstractFileResolvingResource extends AbstractResource { 40 41 /** 42 * This implementation returns a File reference for the underlying class path 43 * resource, provided that it refers to a file in the file system. 44 * @see org.springframework.util.ResourceUtils#getFile(java.net.URL, String) 45 */ 46 @Override getFile()47 public File getFile() throws IOException { 48 URL url = getURL(); 49 if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { 50 return VfsResourceDelegate.getResource(url).getFile(); 51 } 52 return ResourceUtils.getFile(url, getDescription()); 53 } 54 55 /** 56 * This implementation determines the underlying File 57 * (or jar file, in case of a resource in a jar/zip). 58 */ 59 @Override getFileForLastModifiedCheck()60 protected File getFileForLastModifiedCheck() throws IOException { 61 URL url = getURL(); 62 if (ResourceUtils.isJarURL(url)) { 63 URL actualUrl = ResourceUtils.extractJarFileURL(url); 64 if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { 65 return VfsResourceDelegate.getResource(actualUrl).getFile(); 66 } 67 return ResourceUtils.getFile(actualUrl, "Jar URL"); 68 } 69 else { 70 return getFile(); 71 } 72 } 73 74 /** 75 * This implementation returns a File reference for the underlying class path 76 * resource, provided that it refers to a file in the file system. 77 * @see org.springframework.util.ResourceUtils#getFile(java.net.URI, String) 78 */ getFile(URI uri)79 protected File getFile(URI uri) throws IOException { 80 if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { 81 return VfsResourceDelegate.getResource(uri).getFile(); 82 } 83 return ResourceUtils.getFile(uri, getDescription()); 84 } 85 86 87 @Override exists()88 public boolean exists() { 89 try { 90 URL url = getURL(); 91 if (ResourceUtils.isFileURL(url)) { 92 // Proceed with file system resolution... 93 return getFile().exists(); 94 } 95 else { 96 // Try a URL connection content-length header... 97 URLConnection con = url.openConnection(); 98 ResourceUtils.useCachesIfNecessary(con); 99 HttpURLConnection httpCon = 100 (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); 101 if (httpCon != null) { 102 httpCon.setRequestMethod("HEAD"); 103 int code = httpCon.getResponseCode(); 104 if (code == HttpURLConnection.HTTP_OK) { 105 return true; 106 } 107 else if (code == HttpURLConnection.HTTP_NOT_FOUND) { 108 return false; 109 } 110 } 111 if (con.getContentLength() >= 0) { 112 return true; 113 } 114 if (httpCon != null) { 115 // no HTTP OK status, and no content-length header: give up 116 httpCon.disconnect(); 117 return false; 118 } 119 else { 120 // Fall back to stream existence: can we open the stream? 121 InputStream is = getInputStream(); 122 is.close(); 123 return true; 124 } 125 } 126 } 127 catch (IOException ex) { 128 return false; 129 } 130 } 131 132 @Override isReadable()133 public boolean isReadable() { 134 try { 135 URL url = getURL(); 136 if (ResourceUtils.isFileURL(url)) { 137 // Proceed with file system resolution... 138 File file = getFile(); 139 return (file.canRead() && !file.isDirectory()); 140 } 141 else { 142 return true; 143 } 144 } 145 catch (IOException ex) { 146 return false; 147 } 148 } 149 150 @Override contentLength()151 public long contentLength() throws IOException { 152 URL url = getURL(); 153 if (ResourceUtils.isFileURL(url)) { 154 // Proceed with file system resolution... 155 return getFile().length(); 156 } 157 else { 158 // Try a URL connection content-length header... 159 URLConnection con = url.openConnection(); 160 ResourceUtils.useCachesIfNecessary(con); 161 if (con instanceof HttpURLConnection) { 162 ((HttpURLConnection) con).setRequestMethod("HEAD"); 163 } 164 return con.getContentLength(); 165 } 166 } 167 168 @Override lastModified()169 public long lastModified() throws IOException { 170 URL url = getURL(); 171 if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) { 172 // Proceed with file system resolution... 173 return super.lastModified(); 174 } 175 else { 176 // Try a URL connection last-modified header... 177 URLConnection con = url.openConnection(); 178 ResourceUtils.useCachesIfNecessary(con); 179 if (con instanceof HttpURLConnection) { 180 ((HttpURLConnection) con).setRequestMethod("HEAD"); 181 } 182 return con.getLastModified(); 183 } 184 } 185 186 187 /** 188 * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. 189 */ 190 private static class VfsResourceDelegate { 191 getResource(URL url)192 public static Resource getResource(URL url) throws IOException { 193 return new VfsResource(VfsUtils.getRoot(url)); 194 } 195 getResource(URI uri)196 public static Resource getResource(URI uri) throws IOException { 197 return new VfsResource(VfsUtils.getRoot(uri)); 198 } 199 } 200 201 } 202