View Javadoc

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
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
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  	 */
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
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
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
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
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 
192 		public static Resource getResource(URL url) throws IOException {
193 			return new VfsResource(VfsUtils.getRoot(url));
194 		}
195 
196 		public static Resource getResource(URI uri) throws IOException {
197 			return new VfsResource(VfsUtils.getRoot(uri));
198 		}
199 	}
200 
201 }