1 /* 2 * Copyright (c) 2017, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.test.lib.util; 25 26 import jdk.test.lib.Platform; 27 28 import java.io.IOException; 29 import java.nio.file.DirectoryNotEmptyException; 30 import java.nio.file.FileVisitResult; 31 import java.nio.file.Files; 32 import java.nio.file.NoSuchFileException; 33 import java.nio.file.Path; 34 import java.nio.file.SimpleFileVisitor; 35 import java.nio.file.attribute.BasicFileAttributes; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.concurrent.TimeUnit; 39 40 41 /** 42 * Common library for various test file utility functions. 43 */ 44 public final class FileUtils { 45 private static final boolean IS_WINDOWS = Platform.isWindows(); 46 private static final int RETRY_DELETE_MILLIS = IS_WINDOWS ? 500 : 0; 47 private static final int MAX_RETRY_DELETE_TIMES = IS_WINDOWS ? 15 : 0; 48 49 /** 50 * Deletes a file, retrying if necessary. 51 * 52 * @param path the file to delete 53 * 54 * @throws NoSuchFileException 55 * if the file does not exist (optional specific exception) 56 * @throws DirectoryNotEmptyException 57 * if the file is a directory and could not otherwise be deleted 58 * because the directory is not empty (optional specific exception) 59 * @throws IOException 60 * if an I/O error occurs 61 */ 62 public static void deleteFileWithRetry(Path path) throws IOException { 63 try { 64 deleteFileWithRetry0(path); 65 } catch (InterruptedException x) { 66 throw new IOException("Interrupted while deleting.", x); 67 } 68 } 69 70 /** 71 * Deletes a file, retrying if necessary. 72 * No exception thrown if file doesn't exist. 73 * 74 * @param path the file to delete 75 * 76 * @throws NoSuchFileException 77 * if the file does not exist (optional specific exception) 78 * @throws DirectoryNotEmptyException 79 * if the file is a directory and could not otherwise be deleted 80 * because the directory is not empty (optional specific exception) 81 * @throws IOException 82 * if an I/O error occurs 83 */ 84 public static void deleteFileIfExistsWithRetry(Path path) throws IOException { 85 try { 86 if (Files.exists(path)) { 87 deleteFileWithRetry0(path); 88 } 89 } catch (InterruptedException x) { 90 throw new IOException("Interrupted while deleting.", x); 91 } 92 } 93 94 private static void deleteFileWithRetry0(Path path) 95 throws IOException, InterruptedException { 96 int times = 0; 97 IOException ioe = null; 98 while (true) { 99 try { 100 Files.delete(path); 101 while (!Files.notExists(path)) { 102 times++; 103 if (times > MAX_RETRY_DELETE_TIMES) { 104 throw new IOException("File still exists after " + times + " waits."); 105 } 106 Thread.sleep(RETRY_DELETE_MILLIS); 107 } 108 break; 109 } catch (NoSuchFileException | DirectoryNotEmptyException x) { 110 throw x; 111 } catch (IOException x) { 112 // Backoff/retry in case another process is accessing the file 113 times++; 114 if (ioe == null) { 115 ioe = x; 116 } else { 117 ioe.addSuppressed(x); 118 } 119 120 if (times > MAX_RETRY_DELETE_TIMES) { 121 throw ioe; 122 } 123 Thread.sleep(RETRY_DELETE_MILLIS); 124 } 125 } 126 } 127 128 /** 129 * Deletes a directory and its subdirectories, retrying if necessary. 130 * 131 * @param dir the directory to delete 132 * 133 * @throws IOException 134 * If an I/O error occurs. Any such exceptions are caught 135 * internally. If only one is caught, then it is re-thrown. 136 * If more than one exception is caught, then the second and 137 * following exceptions are added as suppressed exceptions of the 138 * first one caught, which is then re-thrown. 139 */ 140 public static void deleteFileTreeWithRetry(Path dir) throws IOException { 141 IOException ioe = null; 142 final List<IOException> excs = deleteFileTreeUnchecked(dir); 143 if (!excs.isEmpty()) { 144 ioe = excs.remove(0); 145 for (IOException x : excs) { 146 ioe.addSuppressed(x); 147 } 148 } 149 if (ioe != null) { 150 throw ioe; 151 } 152 } 153 154 public static List<IOException> deleteFileTreeUnchecked(Path dir) { 155 final List<IOException> excs = new ArrayList<>(); 156 try { 157 java.nio.file.Files.walkFileTree(dir, new SimpleFileVisitor<>() { 158 @Override 159 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 160 try { 161 deleteFileWithRetry0(file); 162 } catch (IOException x) { 163 excs.add(x); 164 } catch (InterruptedException x) { 165 excs.add(new IOException("Interrupted while deleting.", x)); 166 return FileVisitResult.TERMINATE; 167 } 168 return FileVisitResult.CONTINUE; 169 } 170 @Override 171 public FileVisitResult postVisitDirectory(Path dir, IOException exc) { 172 try { 173 deleteFileWithRetry0(dir); 174 } catch (IOException x) { 175 excs.add(x); 176 } catch (InterruptedException x) { 177 excs.add(new IOException("Interrupted while deleting.", x)); 178 return FileVisitResult.TERMINATE; 179 } 180 return FileVisitResult.CONTINUE; 181 } 182 @Override 183 public FileVisitResult visitFileFailed(Path file, IOException exc) { 184 excs.add(exc); 185 return FileVisitResult.CONTINUE; 186 } 187 }); 188 } catch (IOException x) { 189 excs.add(x); 190 } 191 return excs; 192 } 193 194 /** 195 * Checks whether all file systems are accessible. This is performed 196 * by checking free disk space on all mounted file systems via a 197 * separate, spawned process. File systems are considered to be 198 * accessible if this process completes successfully before a given 199 * fixed duration has elapsed. 200 * 201 * @implNote On Unix this executes the {@code df} command in a separate 202 * process and on Windows always returns {@code true}. 203 */ 204 public static boolean areFileSystemsAccessible() throws IOException { 205 boolean areFileSystemsAccessible = true; 206 if (!IS_WINDOWS) { 207 // try to check whether 'df' hangs 208 System.out.println("\n--- df output ---"); 209 System.out.flush(); 210 Process proc = new ProcessBuilder("df").inheritIO().start(); 211 try { 212 proc.waitFor(90, TimeUnit.SECONDS); 213 } catch (InterruptedException ignored) { 214 } 215 try { 216 int exitValue = proc.exitValue(); 217 if (exitValue != 0) { 218 System.err.printf("df process exited with %d != 0%n", 219 exitValue); 220 areFileSystemsAccessible = false; 221 } 222 } catch (IllegalThreadStateException ignored) { 223 System.err.println("df command apparently hung"); 224 areFileSystemsAccessible = false; 225 } 226 } 227 return areFileSystemsAccessible; 228 } 229 }