View Javadoc

1   // ========================================================================
2   // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  package org.mortbay.resource;
15  
16  import java.io.File;
17  import java.io.FileInputStream;
18  import java.io.FileOutputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.net.MalformedURLException;
23  import java.net.URI;
24  import java.net.URISyntaxException;
25  import java.net.URL;
26  import java.net.URLConnection;
27  import java.security.Permission;
28  
29  import org.mortbay.log.Log;
30  import org.mortbay.util.StringUtil;
31  import org.mortbay.util.URIUtil;
32  
33  
34  /* ------------------------------------------------------------ */
35  /** File Resource.
36   *
37   * Handle resources of implied or explicit file type.
38   * This class can check for aliasing in the filesystem (eg case
39   * insensitivity).  By default this is turned on, or it can be controlled with the
40   * "org.mortbay.util.FileResource.checkAliases" system parameter.
41   *
42   * @author Greg Wilkins (gregw)
43   */
44  public class FileResource extends URLResource
45  {
46      private static boolean __checkAliases;
47      static
48      {
49          __checkAliases=
50              "true".equalsIgnoreCase
51              (System.getProperty("org.mortbay.util.FileResource.checkAliases","true"));
52   
53         if (__checkAliases)
54              Log.debug("Checking Resource aliases");
55      }
56      
57      /* ------------------------------------------------------------ */
58      private File _file;
59      private transient URL _alias=null;
60      private transient boolean _aliasChecked=false;
61  
62      /* ------------------------------------------------------------------------------- */
63      /** setCheckAliases.
64       * @param checkAliases True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
65       */
66      public static void setCheckAliases(boolean checkAliases)
67      {
68          __checkAliases=checkAliases;
69      }
70  
71      /* ------------------------------------------------------------------------------- */
72      /** getCheckAliases.
73       * @return True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
74       */
75      public static boolean getCheckAliases()
76      {
77          return __checkAliases;
78      }
79      
80      /* -------------------------------------------------------- */
81      public FileResource(URL url)
82          throws IOException, URISyntaxException
83      {
84          super(url,null);
85  
86          try
87          {
88              // Try standard API to convert URL to file.
89              _file =new File(new URI(url.toString()));
90          }
91          catch (Exception e)
92          {
93              Log.ignore(e);
94              try
95              {
96                  // Assume that File.toURL produced unencoded chars. So try
97                  // encoding them.
98                  String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));           
99                  URI uri = new URI(file_url);
100                 if (uri.getAuthority()==null) 
101                     _file = new File(uri);
102                 else
103                     _file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
104             }
105             catch (Exception e2)
106             {
107                 Log.ignore(e2);
108 
109                 // Still can't get the file.  Doh! try good old hack!
110                 checkConnection();
111                 Permission perm = _connection.getPermission();
112                 _file = new File(perm==null?url.getFile():perm.getName());
113             }
114         }
115         if (_file.isDirectory())
116         {
117             if (!_urlString.endsWith("/"))
118                 _urlString=_urlString+"/";
119         }
120         else
121         {
122             if (_urlString.endsWith("/"))
123                 _urlString=_urlString.substring(0,_urlString.length()-1);
124         }
125             
126     }
127     
128     /* -------------------------------------------------------- */
129     FileResource(URL url, URLConnection connection, File file)
130     {
131         super(url,connection);
132         _file=file;
133         if (_file.isDirectory() && !_urlString.endsWith("/"))
134             _urlString=_urlString+"/";
135     }
136     
137     /* -------------------------------------------------------- */
138     public Resource addPath(String path)
139         throws IOException,MalformedURLException
140     {
141         URLResource r=null;
142         String url=null;
143 
144         path = org.mortbay.util.URIUtil.canonicalPath(path);
145         
146         if (!isDirectory())
147         {
148             r=(FileResource)super.addPath(path);
149             url=r._urlString;
150         }
151         else
152         {
153             if (path==null)
154                 throw new MalformedURLException();   
155             
156             // treat all paths being added as relative
157             String rel=path;
158             if (path.startsWith("/"))
159                 rel = path.substring(1);
160             
161             url=URIUtil.addPaths(_urlString,URIUtil.encodePath(rel));
162             r=(URLResource)Resource.newResource(url);
163         }
164         
165         String encoded=URIUtil.encodePath(path);
166         int expected=r.toString().length()-encoded.length();
167         int index = r._urlString.lastIndexOf(encoded, expected);
168         
169         if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
170         {
171             if (!(r instanceof BadResource))
172             {
173                 ((FileResource)r)._alias=new URL(url);
174                 ((FileResource)r)._aliasChecked=true;
175             }
176         }                             
177         return r;
178     }
179    
180     
181     /* ------------------------------------------------------------ */
182     public URL getAlias()
183     {
184         if (__checkAliases && !_aliasChecked)
185         {
186             try
187             {    
188                 String abs=_file.getAbsolutePath();
189                 String can=_file.getCanonicalPath();
190                 
191                 if (abs.length()!=can.length() || !abs.equals(can))
192                     _alias=new File(can).toURI().toURL();
193                 
194                 _aliasChecked=true;
195                 
196                 if (_alias!=null && Log.isDebugEnabled())
197                 {
198                     Log.debug("ALIAS abs="+abs);
199                     Log.debug("ALIAS can="+can);
200                 }
201             }
202             catch(Exception e)
203             {
204                 Log.warn(Log.EXCEPTION,e);
205                 return getURL();
206             }                
207         }
208         return _alias;
209     }
210     
211     /* -------------------------------------------------------- */
212     /**
213      * Returns true if the resource exists.
214      */
215     public boolean exists()
216     {
217         return _file.exists();
218     }
219         
220     /* -------------------------------------------------------- */
221     /**
222      * Returns the last modified time
223      */
224     public long lastModified()
225     {
226         return _file.lastModified();
227     }
228 
229     /* -------------------------------------------------------- */
230     /**
231      * Returns true if the respresenetd resource is a container/directory.
232      */
233     public boolean isDirectory()
234     {
235         return _file.isDirectory();
236     }
237 
238     /* --------------------------------------------------------- */
239     /**
240      * Return the length of the resource
241      */
242     public long length()
243     {
244         return _file.length();
245     }
246         
247 
248     /* --------------------------------------------------------- */
249     /**
250      * Returns the name of the resource
251      */
252     public String getName()
253     {
254         return _file.getAbsolutePath();
255     }
256         
257     /* ------------------------------------------------------------ */
258     /**
259      * Returns an File representing the given resource or NULL if this
260      * is not possible.
261      */
262     public File getFile()
263     {
264         return _file;
265     }
266         
267     /* --------------------------------------------------------- */
268     /**
269      * Returns an input stream to the resource
270      */
271     public InputStream getInputStream() throws IOException
272     {
273         return new FileInputStream(_file);
274     }
275         
276     /* --------------------------------------------------------- */
277     /**
278      * Returns an output stream to the resource
279      */
280     public OutputStream getOutputStream()
281         throws java.io.IOException, SecurityException
282     {
283         return new FileOutputStream(_file);
284     }
285         
286     /* --------------------------------------------------------- */
287     /**
288      * Deletes the given resource
289      */
290     public boolean delete()
291         throws SecurityException
292     {
293         return _file.delete();
294     }
295 
296     /* --------------------------------------------------------- */
297     /**
298      * Rename the given resource
299      */
300     public boolean renameTo( Resource dest)
301         throws SecurityException
302     {
303         if( dest instanceof FileResource)
304             return _file.renameTo( ((FileResource)dest)._file);
305         else
306             return false;
307     }
308 
309     /* --------------------------------------------------------- */
310     /**
311      * Returns a list of resources contained in the given resource
312      */
313     public String[] list()
314     {
315         String[] list =_file.list();
316         if (list==null)
317             return null;
318         for (int i=list.length;i-->0;)
319         {
320             if (new File(_file,list[i]).isDirectory() &&
321                 !list[i].endsWith("/"))
322                 list[i]+="/";
323         }
324         return list;
325     }
326          
327     /* ------------------------------------------------------------ */
328     /** Encode according to this resource type.
329      * File URIs are encoded.
330      * @param uri URI to encode.
331      * @return The uri unchanged.
332      */
333     public String encode(String uri)
334     {
335         return uri;
336     }
337     
338     /* ------------------------------------------------------------ */
339     /** 
340      * @param o
341      * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
342      */
343     public boolean equals( Object o)
344     {
345         if (this == o)
346             return true;
347 
348         if (null == o || ! (o instanceof FileResource))
349             return false;
350 
351         FileResource f=(FileResource)o;
352         return f._file == _file || (null != _file && _file.equals(f._file));
353     }
354 
355     /* ------------------------------------------------------------ */
356     /**
357      * @return the hashcode.
358      */
359     public int hashCode()
360     {
361        return null == _file ? super.hashCode() : _file.hashCode();
362     }
363 }