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.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.net.MalformedURLException;
28  import java.net.URI;
29  import java.net.URISyntaxException;
30  import java.net.URL;
31  import java.net.URLConnection;
32  import java.security.Permission;
33  
34  import org.eclipse.jetty.util.IO;
35  import org.eclipse.jetty.util.URIUtil;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  
39  
40  /* ------------------------------------------------------------ */
41  /** File Resource.
42   *
43   * Handle resources of implied or explicit file type.
44   * This class can check for aliasing in the filesystem (eg case
45   * insensitivity).  By default this is turned on, or it can be controlled 
46   * by calling the static method @see FileResource#setCheckAliases(boolean)
47   * 
48   */
49  public class FileResource extends URLResource
50  {
51      private static final Logger LOG = Log.getLogger(FileResource.class);
52      private static boolean __checkAliases = true;
53  
54      /* ------------------------------------------------------------ */
55      private File _file;
56      private transient URL _alias=null;
57      private transient boolean _aliasChecked=false;
58  
59      /* ------------------------------------------------------------------------------- */
60      /** setCheckAliases.
61       * @param checkAliases True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
62       */
63      public static void setCheckAliases(boolean checkAliases)
64      {
65          __checkAliases=checkAliases;
66      }
67  
68      /* ------------------------------------------------------------------------------- */
69      /** getCheckAliases.
70       * @return True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
71       */
72      public static boolean getCheckAliases()
73      {
74          return __checkAliases;
75      }
76      
77      /* -------------------------------------------------------- */
78      public FileResource(URL url)
79          throws IOException, URISyntaxException
80      {
81          super(url,null);
82  
83          try
84          {
85              // Try standard API to convert URL to file.
86              _file =new File(new URI(url.toString()));
87          }
88          catch (URISyntaxException e) 
89          {
90              throw e;
91          }
92          catch (Exception e)
93          {
94              LOG.ignore(e);
95              try
96              {
97                  // Assume that File.toURL produced unencoded chars. So try
98                  // encoding them.
99                  String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));           
100                 URI uri = new URI(file_url);
101                 if (uri.getAuthority()==null) 
102                     _file = new File(uri);
103                 else
104                     _file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
105             }
106             catch (Exception e2)
107             {
108                 LOG.ignore(e2);
109 
110                 // Still can't get the file.  Doh! try good old hack!
111                 checkConnection();
112                 Permission perm = _connection.getPermission();
113                 _file = new File(perm==null?url.getFile():perm.getName());
114             }
115         }
116         if (_file.isDirectory())
117         {
118             if (!_urlString.endsWith("/"))
119                 _urlString=_urlString+"/";
120         }
121         else
122         {
123             if (_urlString.endsWith("/"))
124                 _urlString=_urlString.substring(0,_urlString.length()-1);
125         }
126 
127     }
128 
129     /* -------------------------------------------------------- */
130     FileResource(URL url, URLConnection connection, File file)
131     {
132         super(url,connection);
133         _file=file;
134         if (_file.isDirectory() && !_urlString.endsWith("/"))
135             _urlString=_urlString+"/";
136     }
137     
138     /* -------------------------------------------------------- */
139     @Override
140     public Resource addPath(String path)
141         throws IOException,MalformedURLException
142     {
143         URLResource r=null;
144         String url=null;
145 
146         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
147        
148         if ("/".equals(path))
149             return this;
150         else if (!isDirectory())
151         {
152             r=(FileResource)super.addPath(path);
153             url=r._urlString;
154         }
155         else
156         {
157             if (path==null)
158                 throw new MalformedURLException();   
159             
160             // treat all paths being added as relative
161             String rel=path;
162             if (path.startsWith("/"))
163                 rel = path.substring(1);
164             
165             url=URIUtil.addPaths(_urlString,URIUtil.encodePath(rel));
166             r=(URLResource)Resource.newResource(url);
167         }
168         
169         // Check for encoding aliases
170         // The encoded path should be a suffix of the resource (give or take a directory / )
171         String encoded=URIUtil.encodePath(path);
172         int expected=r.toString().length()-encoded.length();
173         int index = r._urlString.lastIndexOf(encoded, expected);
174         if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
175         {
176             if (r instanceof FileResource)
177             {
178                 ((FileResource)r)._alias=((FileResource)r)._file.getCanonicalFile().toURI().toURL();
179                 ((FileResource)r)._aliasChecked=true;
180             }
181         }                             
182         return r;
183     }
184    
185     
186     /* ------------------------------------------------------------ */
187     @Override
188     public URL getAlias()
189     {
190         if (__checkAliases && !_aliasChecked)
191         {
192             try
193             {    
194                 String abs=_file.getAbsolutePath();
195                 String can=_file.getCanonicalPath();
196                 
197                 if (abs.length()!=can.length() || !abs.equals(can))
198                     _alias=Resource.toURL(new File(can));
199                 
200                 _aliasChecked=true;
201                 
202                 if (_alias!=null && LOG.isDebugEnabled())
203                 {
204                     LOG.debug("ALIAS abs="+abs);
205                     LOG.debug("ALIAS can="+can);
206                 }
207             }
208             catch(Exception e)
209             {
210                 LOG.warn(Log.EXCEPTION,e);
211                 return getURL();
212             }                
213         }
214         return _alias;
215     }
216     
217     /* -------------------------------------------------------- */
218     /**
219      * Returns true if the resource exists.
220      */
221     @Override
222     public boolean exists()
223     {
224         return _file.exists();
225     }
226         
227     /* -------------------------------------------------------- */
228     /**
229      * Returns the last modified time
230      */
231     @Override
232     public long lastModified()
233     {
234         return _file.lastModified();
235     }
236 
237     /* -------------------------------------------------------- */
238     /**
239      * Returns true if the respresenetd resource is a container/directory.
240      */
241     @Override
242     public boolean isDirectory()
243     {
244         return _file.isDirectory();
245     }
246 
247     /* --------------------------------------------------------- */
248     /**
249      * Return the length of the resource
250      */
251     @Override
252     public long length()
253     {
254         return _file.length();
255     }
256         
257 
258     /* --------------------------------------------------------- */
259     /**
260      * Returns the name of the resource
261      */
262     @Override
263     public String getName()
264     {
265         return _file.getAbsolutePath();
266     }
267         
268     /* ------------------------------------------------------------ */
269     /**
270      * Returns an File representing the given resource or NULL if this
271      * is not possible.
272      */
273     @Override
274     public File getFile()
275     {
276         return _file;
277     }
278         
279     /* --------------------------------------------------------- */
280     /**
281      * Returns an input stream to the resource
282      */
283     @Override
284     public InputStream getInputStream() throws IOException
285     {
286         return new FileInputStream(_file);
287     }
288         
289     /* --------------------------------------------------------- */
290     /**
291      * Returns an output stream to the resource
292      */
293     @Override
294     public OutputStream getOutputStream()
295         throws java.io.IOException, SecurityException
296     {
297         return new FileOutputStream(_file);
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     /** Encode according to this resource type.
346      * File URIs are encoded.
347      * @param uri URI to encode.
348      * @return The uri unchanged.
349      */
350     @Override
351     public String encode(String uri)
352     {
353         return uri;
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 }