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