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.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.nio.channels.FileChannel;
33  import java.nio.channels.ReadableByteChannel;
34  import java.nio.file.StandardOpenOption;
35  import java.security.Permission;
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   */
53  public class FileResource extends URLResource
54  {
55      private static final Logger LOG = Log.getLogger(FileResource.class);
56  
57      /* ------------------------------------------------------------ */
58      private File _file;
59      private transient URL _alias=null;
60      private transient boolean _aliasChecked=false;
61      
62      /* -------------------------------------------------------- */
63      public FileResource(URL url)
64          throws IOException, URISyntaxException
65      {
66          super(url,null);
67  
68          try
69          {
70              // Try standard API to convert URL to file.
71              _file =new File(new URI(url.toString()));
72          }
73          catch (URISyntaxException e) 
74          {
75              throw e;
76          }
77          catch (Exception e)
78          {
79              LOG.ignore(e);
80              try
81              {
82                  // Assume that File.toURL produced unencoded chars. So try
83                  // 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  
95                  // Still can't get the file.  Doh! try good old hack!
96                  checkConnection();
97                  Permission perm = _connection.getPermission();
98                  _file = new File(perm==null?url.getFile():perm.getName());
99              }
100         }
101         if (_file.isDirectory())
102         {
103             if (!_urlString.endsWith("/"))
104                 _urlString=_urlString+"/";
105         }
106         else
107         {
108             if (_urlString.endsWith("/"))
109                 _urlString=_urlString.substring(0,_urlString.length()-1);
110         }
111 
112     }
113 
114     /* -------------------------------------------------------- */
115     FileResource(URL url, URLConnection connection, File file)
116     {
117         super(url,connection);
118         _file=file;
119         if (_file.isDirectory() && !_urlString.endsWith("/"))
120             _urlString=_urlString+"/";
121     }
122     
123     /* -------------------------------------------------------- */
124     @Override
125     public Resource addPath(String path)
126         throws IOException,MalformedURLException
127     {
128         URLResource r=null;
129         String url=null;
130 
131         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
132        
133         if ("/".equals(path))
134             return this;
135         else if (!isDirectory())
136         {
137             r=(FileResource)super.addPath(path);
138             url=r._urlString;
139         }
140         else
141         {
142             if (path==null)
143                 throw new MalformedURLException();   
144             
145             // treat all paths being added as relative
146             String rel=path;
147             if (path.startsWith("/"))
148                 rel = path.substring(1);
149             
150             url=URIUtil.addPaths(_urlString,URIUtil.encodePath(rel));
151             r=(URLResource)Resource.newResource(url);
152         }
153         
154         String encoded=URIUtil.encodePath(path);
155         int expected=r.toString().length()-encoded.length();
156         int index = r._urlString.lastIndexOf(encoded, expected);
157         
158         if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
159         {
160             if (!(r instanceof BadResource))
161             {
162                 ((FileResource)r)._alias=new URL(url);
163                 ((FileResource)r)._aliasChecked=true;
164             }
165         }                             
166         return r;
167     }
168    
169     
170     /* ------------------------------------------------------------ */
171     @Override
172     public URL getAlias()
173     {
174         if (!_aliasChecked)
175         {
176             try
177             {    
178                 String abs=_file.getAbsolutePath();
179                 String can=_file.getCanonicalPath();
180                 
181                 if (abs.length()!=can.length() || !abs.equals(can))
182                     _alias=Resource.toURL(new File(can));
183                 
184                 _aliasChecked=true;
185                 
186                 if (_alias!=null && LOG.isDebugEnabled())
187                 {
188                     LOG.debug("ALIAS abs="+abs);
189                     LOG.debug("ALIAS can="+can);
190                 }
191             }
192             catch(Exception e)
193             {
194                 LOG.warn(Log.EXCEPTION,e);
195                 return getURL();
196             }                
197         }
198         return _alias;
199     }
200     
201     /* -------------------------------------------------------- */
202     /**
203      * Returns true if the resource exists.
204      */
205     @Override
206     public boolean exists()
207     {
208         return _file.exists();
209     }
210         
211     /* -------------------------------------------------------- */
212     /**
213      * Returns the last modified time
214      */
215     @Override
216     public long lastModified()
217     {
218         return _file.lastModified();
219     }
220 
221     /* -------------------------------------------------------- */
222     /**
223      * Returns true if the respresenetd resource is a container/directory.
224      */
225     @Override
226     public boolean isDirectory()
227     {
228         return _file.isDirectory();
229     }
230 
231     /* --------------------------------------------------------- */
232     /**
233      * Return the length of the resource
234      */
235     @Override
236     public long length()
237     {
238         return _file.length();
239     }
240         
241 
242     /* --------------------------------------------------------- */
243     /**
244      * Returns the name of the resource
245      */
246     @Override
247     public String getName()
248     {
249         return _file.getAbsolutePath();
250     }
251         
252     /* ------------------------------------------------------------ */
253     /**
254      * Returns an File representing the given resource or NULL if this
255      * is not possible.
256      */
257     @Override
258     public File getFile()
259     {
260         return _file;
261     }
262         
263     /* --------------------------------------------------------- */
264     /**
265      * Returns an input stream to the resource
266      */
267     @Override
268     public InputStream getInputStream() throws IOException
269     {
270         return new FileInputStream(_file);
271     }
272 
273     /* ------------------------------------------------------------ */
274     @Override
275     public ReadableByteChannel getReadableByteChannel() throws IOException
276     {
277         return FileChannel.open(_file.toPath(),StandardOpenOption.READ);
278     }
279     
280     /* --------------------------------------------------------- */
281     /**
282      * Returns an output stream to the resource
283      */
284     @Override
285     public OutputStream getOutputStream()
286         throws java.io.IOException, SecurityException
287     {
288         return new FileOutputStream(_file);
289     }
290         
291     /* --------------------------------------------------------- */
292     /**
293      * Deletes the given resource
294      */
295     @Override
296     public boolean delete()
297         throws SecurityException
298     {
299         return _file.delete();
300     }
301 
302     /* --------------------------------------------------------- */
303     /**
304      * Rename the given resource
305      */
306     @Override
307     public boolean renameTo( Resource dest)
308         throws SecurityException
309     {
310         if( dest instanceof FileResource)
311             return _file.renameTo( ((FileResource)dest)._file);
312         else
313             return false;
314     }
315 
316     /* --------------------------------------------------------- */
317     /**
318      * Returns a list of resources contained in the given resource
319      */
320     @Override
321     public String[] list()
322     {
323         String[] list =_file.list();
324         if (list==null)
325             return null;
326         for (int i=list.length;i-->0;)
327         {
328             if (new File(_file,list[i]).isDirectory() &&
329                 !list[i].endsWith("/"))
330                 list[i]+="/";
331         }
332         return list;
333     }
334          
335     /* ------------------------------------------------------------ */
336     /** Encode according to this resource type.
337      * File URIs are encoded.
338      * @param uri URI to encode.
339      * @return The uri unchanged.
340      */
341     @Override
342     public String encode(String uri)
343     {
344         return uri;
345     }
346     
347     /* ------------------------------------------------------------ */
348     /** 
349      * @param o
350      * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
351      */
352     @Override
353     public boolean equals( Object o)
354     {
355         if (this == o)
356             return true;
357 
358         if (null == o || ! (o instanceof FileResource))
359             return false;
360 
361         FileResource f=(FileResource)o;
362         return f._file == _file || (null != _file && _file.equals(f._file));
363     }
364 
365     /* ------------------------------------------------------------ */
366     /**
367      * @return the hashcode.
368      */
369     @Override
370     public int hashCode()
371     {
372        return null == _file ? super.hashCode() : _file.hashCode();
373     }
374     
375     /* ------------------------------------------------------------ */
376     @Override
377     public void copyTo(File destination)
378         throws IOException
379     {
380         if (isDirectory())
381         {
382             IO.copyDir(getFile(),destination);
383         }
384         else
385         {
386             if (destination.exists())
387                 throw new IllegalArgumentException(destination+" exists");
388             IO.copy(getFile(),destination);
389         }
390     }
391 }