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