2 // ========================================================================
3 // Copyright (c) 1995-2016 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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util.resource;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.MalformedURLException;
27 import java.net.URISyntaxException;
29 import java.net.URLConnection;
30 import java.nio.channels.FileChannel;
31 import java.nio.channels.ReadableByteChannel;
32 import java.nio.file.InvalidPathException;
33 import java.nio.file.StandardOpenOption;
34 import java.security.Permission;
36 import org.eclipse.jetty.util.IO;
37 import org.eclipse.jetty.util.StringUtil;
38 import org.eclipse.jetty.util.URIUtil;
39 import org.eclipse.jetty.util.log.Log;
40 import org.eclipse.jetty.util.log.Logger;
43 /* ------------------------------------------------------------ */
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)
52 public class FileResource extends Resource
54 private static final Logger LOG = Log.getLogger(FileResource.class);
56 /* ------------------------------------------------------------ */
57 private final File _file;
58 private final String _uri;
59 private final URI _alias;
61 /* -------------------------------------------------------- */
62 public FileResource(URL url)
63 throws IOException, URISyntaxException
68 // Try standard API to convert URL to file.
69 file =new File(url.toURI());
70 assertValidPath(file.toString());
72 catch (URISyntaxException e)
78 if (!url.toString().startsWith("file:"))
79 throw new IllegalArgumentException("!file:");
84 // Assume that File.toURL produced unencoded chars. So try encoding them.
85 String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));
86 URI uri = new URI(file_url);
87 if (uri.getAuthority()==null)
90 file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
95 // Still can't get the file. Doh! try good old hack!
96 URLConnection connection=url.openConnection();
97 Permission perm = connection.getPermission();
98 file = new File(perm==null?url.getFile():perm.getName());
103 _uri=normalizeURI(_file,url.toURI());
104 _alias=checkFileAlias(_file);
107 /* -------------------------------------------------------- */
108 public FileResource(URI uri)
110 File file=new File(uri);
112 URI file_uri=_file.toURI();
113 _uri=normalizeURI(_file,uri);
114 assertValidPath(file.toString());
116 // Is it a URI alias?
117 if (!URIUtil.equalsIgnoreEncodings(_uri,file_uri.toString()))
118 _alias=_file.toURI();
120 _alias=checkFileAlias(_file);
123 /* -------------------------------------------------------- */
124 FileResource(File file)
126 assertValidPath(file.toString());
128 _uri=normalizeURI(_file,_file.toURI());
129 _alias=checkFileAlias(_file);
132 /* -------------------------------------------------------- */
133 private static String normalizeURI(File file, URI uri)
135 String u =uri.toASCIIString();
136 if (file.isDirectory())
141 else if (file.exists() && u.endsWith("/"))
142 u=u.substring(0,u.length()-1);
146 /* -------------------------------------------------------- */
147 private static URI checkFileAlias(File file)
151 String abs=file.getAbsolutePath();
152 String can=file.getCanonicalPath();
154 if (!abs.equals(can))
156 if (LOG.isDebugEnabled())
157 LOG.debug("ALIAS abs={} can={}",abs,can);
159 URI alias=new File(can).toURI();
160 // Have to encode the path as File.toURI does not!
161 return new URI("file://"+URIUtil.encodePath(alias.getPath()));
166 LOG.warn("bad alias for {}: {}",file,e.toString());
170 return new URI("http://eclipse.org/bad/canonical/alias");
175 throw new RuntimeException(e);
182 /* -------------------------------------------------------- */
184 public Resource addPath(String path)
185 throws IOException,MalformedURLException
187 assertValidPath(path);
188 path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
191 throw new MalformedURLException();
193 if ("/".equals(path))
196 path=URIUtil.encodePath(path);
197 // The encoded path should be a suffix of the resource (give or take a directory / )
201 if (_file.isDirectory())
203 // treat all paths being added as relative
204 uri=new URI(URIUtil.addPaths(_uri,path));
208 uri=new URI(_uri+path);
211 catch(final URISyntaxException e)
213 throw new InvalidPathException(path, e.getMessage());
216 return new FileResource(uri);
219 private void assertValidPath(String path)
221 int idx = StringUtil.indexOfControlChars(path);
224 throw new InvalidPathException(path, "Invalid Character at index " + idx);
228 /* ------------------------------------------------------------ */
230 public URI getAlias()
235 /* -------------------------------------------------------- */
237 * Returns true if the resource exists.
240 public boolean exists()
242 return _file.exists();
245 /* -------------------------------------------------------- */
247 * Returns the last modified time
250 public long lastModified()
252 return _file.lastModified();
255 /* -------------------------------------------------------- */
257 * Returns true if the resource is a container/directory.
260 public boolean isDirectory()
262 return _file.exists() && _file.isDirectory() || _uri.endsWith("/");
265 /* --------------------------------------------------------- */
267 * Return the length of the resource
272 return _file.length();
276 /* --------------------------------------------------------- */
278 * Returns the name of the resource
281 public String getName()
283 return _file.getAbsolutePath();
286 /* ------------------------------------------------------------ */
288 * Returns an File representing the given resource or NULL if this
292 public File getFile()
297 /* --------------------------------------------------------- */
299 * Returns an input stream to the resource
302 public InputStream getInputStream() throws IOException
304 return new FileInputStream(_file);
307 /* ------------------------------------------------------------ */
309 public ReadableByteChannel getReadableByteChannel() throws IOException
311 return FileChannel.open(_file.toPath(),StandardOpenOption.READ);
314 /* --------------------------------------------------------- */
316 * Deletes the given resource
319 public boolean delete()
320 throws SecurityException
322 return _file.delete();
325 /* --------------------------------------------------------- */
327 * Rename the given resource
330 public boolean renameTo( Resource dest)
331 throws SecurityException
333 if( dest instanceof FileResource)
334 return _file.renameTo( ((FileResource)dest)._file);
339 /* --------------------------------------------------------- */
341 * Returns a list of resources contained in the given resource
344 public String[] list()
346 String[] list =_file.list();
349 for (int i=list.length;i-->0;)
351 if (new File(_file,list[i]).isDirectory() &&
352 !list[i].endsWith("/"))
358 /* ------------------------------------------------------------ */
361 * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource.
364 public boolean equals( Object o)
369 if (null == o || ! (o instanceof FileResource))
372 FileResource f=(FileResource)o;
373 return f._file == _file || (null != _file && _file.equals(f._file));
376 /* ------------------------------------------------------------ */
378 * @return the hashcode.
381 public int hashCode()
383 return null == _file ? super.hashCode() : _file.hashCode();
386 /* ------------------------------------------------------------ */
388 public void copyTo(File destination)
393 IO.copyDir(getFile(),destination);
397 if (destination.exists())
398 throw new IllegalArgumentException(destination+" exists");
399 IO.copy(getFile(),destination);
404 public boolean isContainedIn(Resource r) throws MalformedURLException
419 return new URL(_uri);
421 catch (MalformedURLException e)
423 throw new IllegalStateException(e);
430 return _file.toURI();
434 public String toString()