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