View Javadoc

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