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