1 /* 2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.nio.file; 27 28 import java.nio.file.spi.FileTypeDetector; 29 import java.nio.file.attribute.*; 30 import java.io.IOException; 31 import java.util.*; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 35 /** 36 * This class consists exclusively of static methods that operate on files or 37 * directories. 38 * 39 * @since 1.7 40 */ 41 42 public final class Files { 43 private Files() { } 44 45 // lazy loading of default and installed file type detectors 46 private static class DefaultFileTypeDetectorHolder { 47 static final FileTypeDetector defaultFileTypeDetector = 48 sun.nio.fs.DefaultFileTypeDetector.create(); 49 static final List<FileTypeDetector> installeDetectors = 50 loadInstalledDetectors(); 51 52 // loads all installed file type detectors 53 private static List<FileTypeDetector> loadInstalledDetectors() { 54 return AccessController 55 .doPrivileged(new PrivilegedAction<List<FileTypeDetector>>() { 56 @Override public List<FileTypeDetector> run() { 57 List<FileTypeDetector> list = new ArrayList<FileTypeDetector>(); 58 ServiceLoader<FileTypeDetector> loader = ServiceLoader 59 .load(FileTypeDetector.class, ClassLoader.getSystemClassLoader()); 60 for (FileTypeDetector detector: loader) { 61 list.add(detector); 62 } 63 return list; 64 }}); 65 } 66 } 67 68 /** 69 * Probes the content type of a file. 70 * 71 * <p> This method uses the installed {@link FileTypeDetector} implementations 72 * to probe the given file to determine its content type. Each file type 73 * detector's {@link FileTypeDetector#probeContentType probeContentType} is 74 * invoked, in turn, to probe the file type. If the file is recognized then 75 * the content type is returned. If the file is not recognized by any of the 76 * installed file type detectors then a system-default file type detector is 77 * invoked to guess the content type. 78 * 79 * <p> A given invocation of the Java virtual machine maintains a system-wide 80 * list of file type detectors. Installed file type detectors are loaded 81 * using the service-provider loading facility defined by the {@link ServiceLoader} 82 * class. Installed file type detectors are loaded using the system class 83 * loader. If the system class loader cannot be found then the extension class 84 * loader is used; If the extension class loader cannot be found then the 85 * bootstrap class loader is used. File type detectors are typically installed 86 * by placing them in a JAR file on the application class path or in the 87 * extension directory, the JAR file contains a provider-configuration file 88 * named {@code java.nio.file.spi.FileTypeDetector} in the resource directory 89 * {@code META-INF/services}, and the file lists one or more fully-qualified 90 * names of concrete subclass of {@code FileTypeDetector } that have a zero 91 * argument constructor. If the process of locating or instantiating the 92 * installed file type detectors fails then an unspecified error is thrown. 93 * The ordering that installed providers are located is implementation 94 * specific. 95 * 96 * <p> The return value of this method is the string form of the value of a 97 * Multipurpose Internet Mail Extension (MIME) content type as 98 * defined by <a href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC 2045: 99 * Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet 100 * Message Bodies</i></a>. The string is guaranteed to be parsable according 101 * to the grammar in the RFC. 102 * 103 * @param file 104 * The file reference 105 * 106 * @return The content type of the file, or {@code null} if the content 107 * type cannot be determined 108 * 109 * @throws IOException 110 * If an I/O error occurs 111 * @throws SecurityException 112 * If a security manager is installed and it denies an unspecified 113 * permission required by a file type detector implementation. 114 */ 115 public static String probeContentType(FileRef file) 116 throws IOException 117 { 118 // try installed file type detectors 119 for (FileTypeDetector detector: DefaultFileTypeDetectorHolder.installeDetectors) { 120 String result = detector.probeContentType(file); 121 if (result != null) 122 return result; 123 } 124 125 // fallback to default 126 return DefaultFileTypeDetectorHolder.defaultFileTypeDetector 127 .probeContentType(file); 128 } 129 130 /** 131 * Walks a file tree. 132 * 133 * <p> This method walks a file tree rooted at a given starting file. The 134 * file tree traversal is <em>depth-first</em> with the given {@link 135 * FileVisitor} invoked for each file encountered. File tree traversal 136 * completes when all accessible files in the tree have been visited, or a 137 * visit method returns a result of {@link FileVisitResult#TERMINATE 138 * TERMINATE}. Where a visit method terminates due an {@code IOException}, 139 * an uncaught error, or runtime exception, then the traversal is terminated 140 * and the error or exception is propagated to the caller of this method. 141 * 142 * <p> For each file encountered this method attempts to gets its {@link 143 * java.nio.file.attribute.BasicFileAttributes}. If the file is not a 144 * directory then the {@link FileVisitor#visitFile visitFile} method is 145 * invoked with the file attributes. If the file attributes cannot be read, 146 * due to an I/O exception, then the {@link FileVisitor#visitFileFailed 147 * visitFileFailed} method is invoked with the I/O exception. 148 * 149 * <p> Where the file is a directory, and the directory could not be opened, 150 * then the {@code visitFileFailed} method is invoked with the I/O exception, 151 * after which, the file tree walk continues, by default, at the next 152 * <em>sibling</em> of the directory. 153 * 154 * <p> Where the directory is opened successfully, then the entries in the 155 * directory, and their <em>descendants</em> are visited. When all entries 156 * have been visited, or an I/O error occurs during iteration of the 157 * directory, then the directory is closed and the visitor's {@link 158 * FileVisitor#postVisitDirectory postVisitDirectory} method is invoked. 159 * The file tree walk then continues, by default, at the next <em>sibling</em> 160 * of the directory. 161 * 162 * <p> By default, symbolic links are not automatically followed by this 163 * method. If the {@code options} parameter contains the {@link 164 * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are 165 * followed. When following links, and the attributes of the target cannot 166 * be read, then this method attempts to get the {@code BasicFileAttributes} 167 * of the link. If they can be read then the {@code visitFile} method is 168 * invoked with the attributes of the link (otherwise the {@code visitFileFailed} 169 * method is invoked as specified above). 170 * 171 * <p> If the {@code options} parameter contains the {@link 172 * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then this method keeps 173 * track of directories visited so that cycles can be detected. A cycle 174 * arises when there is an entry in a directory that is an ancestor of the 175 * directory. Cycle detection is done by recording the {@link 176 * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories, 177 * or if file keys are not available, by invoking the {@link Path#isSameFile 178 * isSameFile} method to test if a directory is the same file as an 179 * ancestor. When a cycle is detected it is treated as an I/O error, and the 180 * {@link FileVisitor#visitFileFailed visitFileFailed} method is invoked with 181 * an instance of {@link FileSystemLoopException}. 182 * 183 * <p> The {@code maxDepth} parameter is the maximum number of levels of 184 * directories to visit. A value of {@code 0} means that only the starting 185 * file is visited, unless denied by the security manager. A value of 186 * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all 187 * levels should be visited. The {@code visitFile} method is invoked for all 188 * files, including directories, encountered at {@code maxDepth}, unless the 189 * basic file attributes cannot be read, in which case the {@code 190 * visitFileFailed} method is invoked. 191 * 192 * <p> If a visitor returns a result of {@code null} then {@code 193 * NullPointerException} is thrown. 194 * 195 * <p> When a security manager is installed and it denies access to a file 196 * (or directory), then it is ignored and the visitor is not invoked for 197 * that file (or directory). 198 * 199 * @param start 200 * The starting file 201 * @param options 202 * Options to configure the traversal 203 * @param maxDepth 204 * The maximum number of directory levels to visit 205 * @param visitor 206 * The file visitor to invoke for each file 207 * 208 * @throws IllegalArgumentException 209 * If the {@code maxDepth} parameter is negative 210 * @throws SecurityException 211 * If the security manager denies access to the starting file. 212 * In the case of the default provider, the {@link 213 * SecurityManager#checkRead(String) checkRead} method is invoked 214 * to check read access to the directory. 215 * @throws IOException 216 * If an I/O error is thrown by a visitor method 217 */ 218 public static void walkFileTree(Path start, 219 Set<FileVisitOption> options, 220 int maxDepth, 221 FileVisitor<? super Path> visitor) 222 throws IOException 223 { 224 if (maxDepth < 0) 225 throw new IllegalArgumentException("'maxDepth' is negative"); 226 new FileTreeWalker(options, visitor, maxDepth).walk(start); 227 } 228 229 /** 230 * Walks a file tree. 231 * 232 * <p> This method works as if invoking it were equivalent to evaluating the 233 * expression: 234 * <blockquote><pre> 235 * walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, visitor) 236 * </pre></blockquote> 237 * 238 * @param start 239 * The starting file 240 * @param visitor 241 * The file visitor to invoke for each file 242 * 243 * @throws SecurityException 244 * If the security manager denies access to the starting file. 245 * In the case of the default provider, the {@link 246 * SecurityManager#checkRead(String) checkRead} method is invoked 247 * to check read access to the directory. 248 * @throws IOException 249 * If an I/O error is thrown by a visitor method 250 */ 251 public static void walkFileTree(Path start, FileVisitor<? super Path> visitor) 252 throws IOException 253 { 254 walkFileTree(start, 255 EnumSet.noneOf(FileVisitOption.class), 256 Integer.MAX_VALUE, 257 visitor); 258 } 259 260 /** 261 * Creates a directory by creating all nonexistent parent directories first. 262 * 263 * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute 264 * file-attributes} to set atomically when creating the nonexistent 265 * directories. Each file attribute is identified by its {@link 266 * FileAttribute#name name}. If more than one attribute of the same name is 267 * included in the array then all but the last occurrence is ignored. 268 * 269 * <p> If this method fails, then it may do so after creating some, but not 270 * all, of the parent directories. 271 * 272 * @param dir 273 * the directory to create 274 * 275 * @param attrs 276 * an optional list of file attributes to set atomically when 277 * creating the directory 278 * 279 * @throws UnsupportedOperationException 280 * if the array contains an attribute that cannot be set atomically 281 * when creating the directory 282 * @throws FileAlreadyExistsException 283 * if {@code dir} exists but is not a directory <i>(optional specific 284 * exception)</i> 285 * @throws IOException 286 * if an I/O error occurs 287 * @throws SecurityException 288 * in the case of the default provider, and a security manager is 289 * installed, the {@link SecurityManager#checkWrite(String) checkWrite} 290 * method is invoked prior to attempting to create a directory and 291 * its {@link SecurityManager#checkRead(String) checkRead} is 292 * invoked for each parent directory that is checked. If {@code 293 * dir} is not an absolute path then its {@link Path#toAbsolutePath 294 * toAbsolutePath} may need to be invoked to get its absolute path. 295 * This may invoke the security manager's {@link 296 * SecurityManager#checkPropertyAccess(String) checkPropertyAccess} 297 * method to check access to the system property {@code user.dir} 298 * 299 */ 300 public static void createDirectories(Path dir, FileAttribute<?>... attrs) 301 throws IOException 302 { 303 // attempt to create the directory 304 try { 305 createAndCheckIsDirectory(dir, attrs); 306 return; 307 } catch (FileAlreadyExistsException x) { 308 // file exists and is not a directory 309 throw x; 310 } catch (IOException x) { 311 // parent may not exist or other reason 312 } 313 314 // find existing parent (may require absolute path) 315 SecurityException se = null; 316 try { 317 dir = dir.toAbsolutePath(); 318 } catch (SecurityException x) { 319 // don't have permission to get absolute path 320 se = x; 321 } 322 Path parent = dir.getParent(); 323 while (parent != null) { 324 try { 325 parent.checkAccess(); 326 break; 327 } catch (NoSuchFileException x) { 328 // does not exist 329 } 330 parent = parent.getParent(); 331 } 332 if (parent == null) { 333 // unable to find existing parent 334 if (se != null) 335 throw se; 336 throw new IOException("Root directory does not exist"); 337 } 338 339 // create directories 340 Path child = parent; 341 for (Path name: parent.relativize(dir)) { 342 child = child.resolve(name); 343 createAndCheckIsDirectory(child, attrs); 344 } 345 } 346 347 /** 348 * Attempts to create a directory. Does nothing if the directory already 349 * exists. 350 */ 351 private static void createAndCheckIsDirectory(Path dir, FileAttribute<?>... attrs) 352 throws IOException 353 { 354 try { 355 dir.createDirectory(attrs); 356 } catch (FileAlreadyExistsException x) { 357 boolean isDirectory = Attributes 358 .readBasicFileAttributes(dir, LinkOption.NOFOLLOW_LINKS).isDirectory(); 359 if (!isDirectory) 360 throw x; 361 } 362 } 363 }