1 // ========================================================================
2 // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
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 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14 package org.mortbay.resource;
15
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.net.MalformedURLException;
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.net.URL;
26 import java.net.URLConnection;
27 import java.security.Permission;
28
29 import org.mortbay.log.Log;
30 import org.mortbay.util.StringUtil;
31 import org.mortbay.util.URIUtil;
32
33
34 /* ------------------------------------------------------------ */
35 /** File Resource.
36 *
37 * Handle resources of implied or explicit file type.
38 * This class can check for aliasing in the filesystem (eg case
39 * insensitivity). By default this is turned on, or it can be controlled with the
40 * "org.mortbay.util.FileResource.checkAliases" system parameter.
41 *
42 * @author Greg Wilkins (gregw)
43 */
44 public class FileResource extends URLResource
45 {
46 private static boolean __checkAliases;
47 static
48 {
49 __checkAliases=
50 "true".equalsIgnoreCase
51 (System.getProperty("org.mortbay.util.FileResource.checkAliases","true"));
52
53 if (__checkAliases)
54 Log.debug("Checking Resource aliases");
55 else
56 Log.warn("Resource alias checking is disabled");
57 }
58
59 /* ------------------------------------------------------------ */
60 private File _file;
61 private transient URL _alias=null;
62 private transient boolean _aliasChecked=false;
63
64 /* ------------------------------------------------------------------------------- */
65 /** setCheckAliases.
66 * @param checkAliases True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
67 */
68 public static void setCheckAliases(boolean checkAliases)
69 {
70 __checkAliases=checkAliases;
71 }
72
73 /* ------------------------------------------------------------------------------- */
74 /** getCheckAliases.
75 * @return True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
76 */
77 public static boolean getCheckAliases()
78 {
79 return __checkAliases;
80 }
81
82 /* -------------------------------------------------------- */
83 public FileResource(URL url)
84 throws IOException, URISyntaxException
85 {
86 super(url,null);
87
88 try
89 {
90 // Try standard API to convert URL to file.
91 _file =new File(new URI(url.toString()));
92 }
93 catch (Exception e)
94 {
95 Log.ignore(e);
96 try
97 {
98 // Assume that File.toURL produced unencoded chars. So try
99 // encoding them.
100 String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));
101 URI uri = new URI(file_url);
102 if (uri.getAuthority()==null)
103 _file = new File(uri);
104 else
105 _file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
106 }
107 catch (Exception e2)
108 {
109 Log.ignore(e2);
110
111 // Still can't get the file. Doh! try good old hack!
112 checkConnection();
113 Permission perm = _connection.getPermission();
114 _file = new File(perm==null?url.getFile():perm.getName());
115 }
116 }
117 if (_file.isDirectory())
118 {
119 if (!_urlString.endsWith("/"))
120 _urlString=_urlString+"/";
121 }
122 else
123 {
124 if (_urlString.endsWith("/"))
125 _urlString=_urlString.substring(0,_urlString.length()-1);
126 }
127
128 }
129
130 /* -------------------------------------------------------- */
131 FileResource(URL url, URLConnection connection, File file)
132 {
133 super(url,connection);
134 _file=file;
135 if (_file.isDirectory() && !_urlString.endsWith("/"))
136 _urlString=_urlString+"/";
137 }
138
139 /* -------------------------------------------------------- */
140 public Resource addPath(String path)
141 throws IOException,MalformedURLException
142 {
143 URLResource r=null;
144 String url=null;
145
146 path = org.mortbay.util.URIUtil.canonicalPath(path);
147
148 if (!isDirectory())
149 {
150 r=(FileResource)super.addPath(path);
151 url=r._urlString;
152 }
153 else
154 {
155 if (path==null)
156 throw new MalformedURLException();
157
158 // treat all paths being added as relative
159 String rel=path;
160 if (path.startsWith("/"))
161 rel = path.substring(1);
162
163 url=URIUtil.addPaths(_urlString,URIUtil.encodePath(rel));
164 r=(URLResource)Resource.newResource(url);
165 }
166
167 String encoded=URIUtil.encodePath(path);
168 int expected=r.toString().length()-encoded.length();
169 int index = r._urlString.lastIndexOf(encoded, expected);
170
171 if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
172 {
173 if (!(r instanceof BadResource))
174 {
175 ((FileResource)r)._alias=new URL(url);
176 ((FileResource)r)._aliasChecked=true;
177 }
178 }
179 return r;
180 }
181
182
183 /* ------------------------------------------------------------ */
184 public URL getAlias()
185 {
186 if (__checkAliases && !_aliasChecked)
187 {
188 try
189 {
190 String abs=_file.getAbsolutePath();
191 String can=_file.getCanonicalPath();
192
193 if (abs.length()!=can.length() || !abs.equals(can))
194 _alias=new File(can).toURI().toURL();
195
196 _aliasChecked=true;
197
198 if (_alias!=null && Log.isDebugEnabled())
199 {
200 Log.debug("ALIAS abs="+abs);
201 Log.debug("ALIAS can="+can);
202 }
203 }
204 catch(Exception e)
205 {
206 Log.warn(Log.EXCEPTION,e);
207 return getURL();
208 }
209 }
210 return _alias;
211 }
212
213 /* -------------------------------------------------------- */
214 /**
215 * Returns true if the resource exists.
216 */
217 public boolean exists()
218 {
219 return _file.exists();
220 }
221
222 /* -------------------------------------------------------- */
223 /**
224 * Returns the last modified time
225 */
226 public long lastModified()
227 {
228 return _file.lastModified();
229 }
230
231 /* -------------------------------------------------------- */
232 /**
233 * Returns true if the respresenetd resource is a container/directory.
234 */
235 public boolean isDirectory()
236 {
237 return _file.isDirectory();
238 }
239
240 /* --------------------------------------------------------- */
241 /**
242 * Return the length of the resource
243 */
244 public long length()
245 {
246 return _file.length();
247 }
248
249
250 /* --------------------------------------------------------- */
251 /**
252 * Returns the name of the resource
253 */
254 public String getName()
255 {
256 return _file.getAbsolutePath();
257 }
258
259 /* ------------------------------------------------------------ */
260 /**
261 * Returns an File representing the given resource or NULL if this
262 * is not possible.
263 */
264 public File getFile()
265 {
266 return _file;
267 }
268
269 /* --------------------------------------------------------- */
270 /**
271 * Returns an input stream to the resource
272 */
273 public InputStream getInputStream() throws IOException
274 {
275 return new FileInputStream(_file);
276 }
277
278 /* --------------------------------------------------------- */
279 /**
280 * Returns an output stream to the resource
281 */
282 public OutputStream getOutputStream()
283 throws java.io.IOException, SecurityException
284 {
285 return new FileOutputStream(_file);
286 }
287
288 /* --------------------------------------------------------- */
289 /**
290 * Deletes the given resource
291 */
292 public boolean delete()
293 throws SecurityException
294 {
295 return _file.delete();
296 }
297
298 /* --------------------------------------------------------- */
299 /**
300 * Rename the given resource
301 */
302 public boolean renameTo( Resource dest)
303 throws SecurityException
304 {
305 if( dest instanceof FileResource)
306 return _file.renameTo( ((FileResource)dest)._file);
307 else
308 return false;
309 }
310
311 /* --------------------------------------------------------- */
312 /**
313 * Returns a list of resources contained in the given resource
314 */
315 public String[] list()
316 {
317 String[] list =_file.list();
318 if (list==null)
319 return null;
320 for (int i=list.length;i-->0;)
321 {
322 if (new File(_file,list[i]).isDirectory() &&
323 !list[i].endsWith("/"))
324 list[i]+="/";
325 }
326 return list;
327 }
328
329 /* ------------------------------------------------------------ */
330 /** Encode according to this resource type.
331 * File URIs are encoded.
332 * @param uri URI to encode.
333 * @return The uri unchanged.
334 */
335 public String encode(String uri)
336 {
337 return uri;
338 }
339
340 /* ------------------------------------------------------------ */
341 /**
342 * @param o
343 * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource.
344 */
345 public boolean equals( Object o)
346 {
347 if (this == o)
348 return true;
349
350 if (null == o || ! (o instanceof FileResource))
351 return false;
352
353 FileResource f=(FileResource)o;
354 return f._file == _file || (null != _file && _file.equals(f._file));
355 }
356
357 /* ------------------------------------------------------------ */
358 /**
359 * @return the hashcode.
360 */
361 public int hashCode()
362 {
363 return null == _file ? super.hashCode() : _file.hashCode();
364 }
365 }