View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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=checkFileAlias(_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         // Is it a URI alias?
113         if (!URIUtil.equalsIgnoreEncodings(_uri,file_uri.toString()))
114             _alias=_file.toURI();
115         else
116             _alias=checkFileAlias(_file);
117     }
118 
119     /* -------------------------------------------------------- */
120     FileResource(File file)
121     {
122         _file=file;
123         _uri=normalizeURI(_file,_file.toURI());
124         _alias=checkFileAlias(_file);
125     }
126 
127     /* -------------------------------------------------------- */
128     private static String normalizeURI(File file, URI uri)
129     {
130         String u =uri.toASCIIString();
131         if (file.isDirectory())
132         {
133             if(!u.endsWith("/"))
134                 u+="/";
135         } 
136         else if (file.exists() && u.endsWith("/"))
137             u=u.substring(0,u.length()-1);
138         return u;
139     }
140 
141     /* -------------------------------------------------------- */
142     private static URI checkFileAlias(File file)
143     {
144         try
145         {
146             String abs=file.getAbsolutePath();
147             String can=file.getCanonicalPath();
148 
149             if (!abs.equals(can))
150             {
151                 if (LOG.isDebugEnabled())
152                     LOG.debug("ALIAS abs={} can={}",abs,can);
153 
154                 URI alias=new File(can).toURI();
155                 // Have to encode the path as File.toURI does not!
156                 return new URI("file://"+URIUtil.encodePath(alias.getPath()));  
157             }
158         }
159         catch(Exception e)
160         {
161             LOG.warn("bad alias for {}: {}",file,e.toString());
162             LOG.debug(e);
163             try
164             {
165                 return new URI("http://eclipse.org/bad/canonical/alias");
166             }
167             catch(Exception e2)
168             {
169                 LOG.ignore(e2);
170                 throw new RuntimeException(e);
171             }
172         }
173 
174         return null;
175     }
176     
177     /* -------------------------------------------------------- */
178     @Override
179     public Resource addPath(String path)
180         throws IOException,MalformedURLException
181     {
182         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
183 
184         if (path==null)
185             throw new MalformedURLException();   
186         
187         if ("/".equals(path))
188             return this;
189         
190         path=URIUtil.encodePath(path);
191         // The encoded path should be a suffix of the resource (give or take a directory / )
192         URI uri;
193         try
194         {
195             if (_file.isDirectory())
196             {
197                 // treat all paths being added as relative
198                 uri=new URI(URIUtil.addPaths(_uri,path));
199             }
200             else
201             {
202                 uri=new URI(_uri+path);
203             }
204         }
205         catch(final URISyntaxException e)
206         {
207             throw new MalformedURLException(){{initCause(e);}};
208         }
209 
210         return new FileResource(uri);
211     }
212    
213     
214     /* ------------------------------------------------------------ */
215     @Override
216     public URI getAlias()
217     {
218         return _alias;
219     }
220     
221     /* -------------------------------------------------------- */
222     /**
223      * Returns true if the resource exists.
224      */
225     @Override
226     public boolean exists()
227     {
228         return _file.exists();
229     }
230         
231     /* -------------------------------------------------------- */
232     /**
233      * Returns the last modified time
234      */
235     @Override
236     public long lastModified()
237     {
238         return _file.lastModified();
239     }
240 
241     /* -------------------------------------------------------- */
242     /**
243      * Returns true if the resource is a container/directory.
244      */
245     @Override
246     public boolean isDirectory()
247     {
248         return _file.exists() && _file.isDirectory() || _uri.endsWith("/");
249     }
250 
251     /* --------------------------------------------------------- */
252     /**
253      * Return the length of the resource
254      */
255     @Override
256     public long length()
257     {
258         return _file.length();
259     }
260         
261 
262     /* --------------------------------------------------------- */
263     /**
264      * Returns the name of the resource
265      */
266     @Override
267     public String getName()
268     {
269         return _file.getAbsolutePath();
270     }
271         
272     /* ------------------------------------------------------------ */
273     /**
274      * Returns an File representing the given resource or NULL if this
275      * is not possible.
276      */
277     @Override
278     public File getFile()
279     {
280         return _file;
281     }
282         
283     /* --------------------------------------------------------- */
284     /**
285      * Returns an input stream to the resource
286      */
287     @Override
288     public InputStream getInputStream() throws IOException
289     {
290         return new FileInputStream(_file);
291     }
292 
293     /* ------------------------------------------------------------ */
294     @Override
295     public ReadableByteChannel getReadableByteChannel() throws IOException
296     {
297         return FileChannel.open(_file.toPath(),StandardOpenOption.READ);
298     }
299         
300     /* --------------------------------------------------------- */
301     /**
302      * Deletes the given resource
303      */
304     @Override
305     public boolean delete()
306         throws SecurityException
307     {
308         return _file.delete();
309     }
310 
311     /* --------------------------------------------------------- */
312     /**
313      * Rename the given resource
314      */
315     @Override
316     public boolean renameTo( Resource dest)
317         throws SecurityException
318     {
319         if( dest instanceof FileResource)
320             return _file.renameTo( ((FileResource)dest)._file);
321         else
322             return false;
323     }
324 
325     /* --------------------------------------------------------- */
326     /**
327      * Returns a list of resources contained in the given resource
328      */
329     @Override
330     public String[] list()
331     {
332         String[] list =_file.list();
333         if (list==null)
334             return null;
335         for (int i=list.length;i-->0;)
336         {
337             if (new File(_file,list[i]).isDirectory() &&
338                 !list[i].endsWith("/"))
339                 list[i]+="/";
340         }
341         return list;
342     }
343     
344     /* ------------------------------------------------------------ */
345     /** 
346      * @param o
347      * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
348      */
349     @Override
350     public boolean equals( Object o)
351     {
352         if (this == o)
353             return true;
354 
355         if (null == o || ! (o instanceof FileResource))
356             return false;
357 
358         FileResource f=(FileResource)o;
359         return f._file == _file || (null != _file && _file.equals(f._file));
360     }
361 
362     /* ------------------------------------------------------------ */
363     /**
364      * @return the hashcode.
365      */
366     @Override
367     public int hashCode()
368     {
369        return null == _file ? super.hashCode() : _file.hashCode();
370     }
371     
372     /* ------------------------------------------------------------ */
373     @Override
374     public void copyTo(File destination)
375         throws IOException
376     {
377         if (isDirectory())
378         {
379             IO.copyDir(getFile(),destination);
380         }
381         else
382         {
383             if (destination.exists())
384                 throw new IllegalArgumentException(destination+" exists");
385             IO.copy(getFile(),destination);
386         }
387     }
388 
389     @Override
390     public boolean isContainedIn(Resource r) throws MalformedURLException
391     {
392         return false;
393     }
394 
395     @Override
396     public void close()
397     {
398     }
399 
400     @Override
401     public URL getURL()
402     {
403         try
404         {
405             return new URL(_uri);
406         }
407         catch (MalformedURLException e)
408         {
409             throw new IllegalStateException(e);
410         }
411     }
412     
413     @Override
414     public URI getURI()
415     {
416         return _file.toURI();
417     }
418 
419     @Override
420     public String toString()
421     {
422         return _uri;
423     }
424 
425 }