View Javadoc

1   /*
2    * Copyright 2002-2012 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.FileNotFoundException;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.net.URISyntaxException;
25  import java.net.URL;
26  
27  import org.springframework.core.NestedIOException;
28  import org.springframework.util.ResourceUtils;
29  
30  /**
31   * Convenience base class for {@link Resource} implementations,
32   * pre-implementing typical behavior.
33   *
34   * <p>The "exists" method will check whether a File or InputStream can
35   * be opened; "isOpen" will always return false; "getURL" and "getFile"
36   * throw an exception; and "toString" will return the description.
37   *
38   * @author Juergen Hoeller
39   * @since 28.12.2003
40   */
41  public abstract class AbstractResource implements Resource {
42  
43  	/**
44  	 * This implementation checks whether a File can be opened,
45  	 * falling back to whether an InputStream can be opened.
46  	 * This will cover both directories and content resources.
47  	 */
48  	public boolean exists() {
49  		// Try file existence: can we find the file in the file system?
50  		try {
51  			return getFile().exists();
52  		}
53  		catch (IOException ex) {
54  			// Fall back to stream existence: can we open the stream?
55  			try {
56  				InputStream is = getInputStream();
57  				is.close();
58  				return true;
59  			}
60  			catch (Throwable isEx) {
61  				return false;
62  			}
63  		}
64  	}
65  
66  	/**
67  	 * This implementation always returns <code>true</code>.
68  	 */
69  	public boolean isReadable() {
70  		return true;
71  	}
72  
73  	/**
74  	 * This implementation always returns <code>false</code>.
75  	 */
76  	public boolean isOpen() {
77  		return false;
78  	}
79  
80  	/**
81  	 * This implementation throws a FileNotFoundException, assuming
82  	 * that the resource cannot be resolved to a URL.
83  	 */
84  	public URL getURL() throws IOException {
85  		throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
86  	}
87  
88  	/**
89  	 * This implementation builds a URI based on the URL returned
90  	 * by {@link #getURL()}.
91  	 */
92  	public URI getURI() throws IOException {
93  		URL url = getURL();
94  		try {
95  			return ResourceUtils.toURI(url);
96  		}
97  		catch (URISyntaxException ex) {
98  			throw new NestedIOException("Invalid URI [" + url + "]", ex);
99  		}
100 	}
101 
102 	/**
103 	 * This implementation throws a FileNotFoundException, assuming
104 	 * that the resource cannot be resolved to an absolute file path.
105 	 */
106 	public File getFile() throws IOException {
107 		throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
108 	}
109 
110 	/**
111 	 * This implementation reads the entire InputStream to calculate the
112 	 * content length. Subclasses will almost always be able to provide
113 	 * a more optimal version of this, e.g. checking a File length.
114 	 * @see #getInputStream()
115 	 */
116 	public long contentLength() throws IOException {
117 		InputStream is = getInputStream();
118 		try {
119 			long size = 0;
120 			byte[] buf = new byte[255];
121 			int read;
122 			while ((read = is.read(buf)) != -1) {
123 				size += read;
124 			}
125 			return size;
126 		}
127 		finally {
128 			try {
129 				is.close();
130 			}
131 			catch (IOException ex) {
132 			}
133 		}
134 	}
135 
136 	/**
137 	 * This implementation checks the timestamp of the underlying File,
138 	 * if available.
139 	 * @see #getFileForLastModifiedCheck()
140 	 */
141 	public long lastModified() throws IOException {
142 		long lastModified = getFileForLastModifiedCheck().lastModified();
143 		if (lastModified == 0L) {
144 			throw new FileNotFoundException(getDescription() +
145 					" cannot be resolved in the file system for resolving its last-modified timestamp");
146 		}
147 		return lastModified;
148 	}
149 
150 	/**
151 	 * Determine the File to use for timestamp checking.
152 	 * <p>The default implementation delegates to {@link #getFile()}.
153 	 * @return the File to use for timestamp checking (never <code>null</code>)
154 	 * @throws IOException if the resource cannot be resolved as absolute
155 	 * file path, i.e. if the resource is not available in a file system
156 	 */
157 	protected File getFileForLastModifiedCheck() throws IOException {
158 		return getFile();
159 	}
160 
161 	/**
162 	 * This implementation throws a FileNotFoundException, assuming
163 	 * that relative resources cannot be created for this resource.
164 	 */
165 	public Resource createRelative(String relativePath) throws IOException {
166 		throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
167 	}
168 
169 	/**
170 	 * This implementation always returns <code>null</code>,
171 	 * assuming that this resource type does not have a filename.
172 	 */
173 	public String getFilename() {
174 		return null;
175 	}
176 
177 
178 	/**
179 	 * This implementation returns the description of this resource.
180 	 * @see #getDescription()
181 	 */
182 	@Override
183 	public String toString() {
184 		return getDescription();
185 	}
186 
187 	/**
188 	 * This implementation compares description strings.
189 	 * @see #getDescription()
190 	 */
191 	@Override
192 	public boolean equals(Object obj) {
193 		return (obj == this ||
194 		    (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
195 	}
196 
197 	/**
198 	 * This implementation returns the description's hash code.
199 	 * @see #getDescription()
200 	 */
201 	@Override
202 	public int hashCode() {
203 		return getDescription().hashCode();
204 	}
205 
206 }