/* * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/** * Common library for various test file utility functions.
*/ publicfinalclass FileUtils { privatestaticfinalboolean IS_WINDOWS = Platform.isWindows(); privatestaticfinalint RETRY_DELETE_MILLIS = IS_WINDOWS ? 500 : 0; privatestaticfinalint MAX_RETRY_DELETE_TIMES = IS_WINDOWS ? 15 : 0; privatestaticvolatileboolean nativeLibLoaded;
/** * Deletes a file, retrying if necessary. * * @param path the file to delete * * @throws NoSuchFileException * if the file does not exist (optional specific exception) * @throws DirectoryNotEmptyException * if the file is a directory and could not otherwise be deleted * because the directory is not empty (optional specific exception) * @throws IOException * if an I/O error occurs
*/ publicstaticvoid deleteFileWithRetry(Path path) throws IOException { try {
deleteFileWithRetry0(path);
} catch (InterruptedException x) { thrownew IOException("Interrupted while deleting.", x);
}
}
/** * Deletes a file, retrying if necessary. * No exception thrown if file doesn't exist. * * @param path the file to delete * * @throws NoSuchFileException * if the file does not exist (optional specific exception) * @throws DirectoryNotEmptyException * if the file is a directory and could not otherwise be deleted * because the directory is not empty (optional specific exception) * @throws IOException * if an I/O error occurs
*/ publicstaticvoid deleteFileIfExistsWithRetry(Path path) throws IOException { try { if (!Files.notExists(path)) {
deleteFileWithRetry0(path);
}
} catch (InterruptedException x) { thrownew IOException("Interrupted while deleting.", x);
}
}
privatestaticvoid deleteFileWithRetry0(Path path) throws IOException, InterruptedException { int times = 0;
IOException ioe = null; while (true) { try {
Files.delete(path); // Checks for absence of the file. Semantics of Files.exists() is not the same. while (!Files.notExists(path)) {
times++; if (times > MAX_RETRY_DELETE_TIMES) { thrownew IOException("File still exists after " + times + " waits.");
} Thread.sleep(RETRY_DELETE_MILLIS);
} break;
} catch (NoSuchFileException | DirectoryNotEmptyException x) { throw x;
} catch (IOException x) { // Backoff/retry in case another process is accessing the file
times++; if (ioe == null) {
ioe = x;
} else {
ioe.addSuppressed(x);
}
/** * Deletes a directory and its subdirectories, retrying if necessary. * * @param dir the directory to delete * * @throws IOException * If an I/O error occurs. Any such exceptions are caught * internally. If only one is caught, then it is re-thrown. * If more than one exception is caught, then the second and * following exceptions are added as suppressed exceptions of the * first one caught, which is then re-thrown.
*/ publicstaticvoid deleteFileTreeWithRetry(Path dir) throws IOException {
IOException ioe = null; final List<IOException> excs = deleteFileTreeUnchecked(dir); if (!excs.isEmpty()) {
ioe = excs.remove(0); for (IOException x : excs) {
ioe.addSuppressed(x);
}
} if (ioe != null) { throw ioe;
}
}
/** * Checks whether all file systems are accessible. This is performed * by checking free disk space on all mounted file systems via a * separate, spawned process. File systems are considered to be * accessible if this process completes successfully before a given * fixed duration has elapsed. * * @implNote On Unix this executes the {@code df} command in a separate * process and on Windows always returns {@code true}.
*/ publicstaticboolean areFileSystemsAccessible() throws IOException { boolean areFileSystemsAccessible = true; if (!IS_WINDOWS) { // try to check whether 'df' hangs
System.out.println("\n--- df output ---");
System.out.flush();
Process proc = new ProcessBuilder("df").inheritIO().start(); try {
proc.waitFor(90, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
} try { int exitValue = proc.exitValue(); if (exitValue != 0) {
System.err.printf("df process exited with %d != 0%n",
exitValue);
areFileSystemsAccessible = false;
}
} catch (IllegalThreadStateException ignored) {
System.err.println("df command apparently hung");
areFileSystemsAccessible = false;
}
} return areFileSystemsAccessible;
}
/** * Checks whether all file systems are accessible and there are no * duplicate mount points. This is performed by checking free disk * space on all mounted file systems via a separate, spawned process. * File systems are considered to be accessible if this process completes * successfully before a given fixed duration has elapsed. * * @implNote On Unix this executes the {@code df} command in a separate * process and on Windows always returns {@code true}. * * @return whether file systems appear to be accessible and duplicate-free
*/ publicstaticboolean areMountPointsAccessibleAndUnique() { if (IS_WINDOWS) returntrue;
final AtomicBoolean areMountPointsOK = new AtomicBoolean(true); Thread thr = newThread(() -> { try {
Process proc = new ProcessBuilder("df").start();
BufferedReader reader = new BufferedReader
(new InputStreamReader(proc.getInputStream())); // Skip the first line as it is the "df" output header. if (reader.readLine() != null ) {
Set mountPoints = new HashSet();
String mountPoint = null; while ((mountPoint = reader.readLine()) != null) { if (!mountPoints.add(mountPoint)) {
System.err.printf
("Config error: duplicate mount point %s%n",
mountPoint);
areMountPointsOK.set(false); break;
}
}
}
if (thr.isAlive()) { thrownew RuntimeException("df thread did not join in time");
}
return areMountPointsOK.get();
}
/** * List the open file descriptors (if supported by the 'lsof' command). * @param ps a printStream to send the output to * @throws UncheckedIOException if an error occurs
*/ publicstaticvoid listFileDescriptors(PrintStream ps) {
// Return the current process handle count publicstaticlong getProcessHandleCount() { if (IS_WINDOWS) { if (!nativeLibLoaded) {
System.loadLibrary("FileUtils");
nativeLibLoaded = true;
} return getWinProcessHandleCount();
} else { return ((UnixOperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean()).getOpenFileDescriptorCount();
}
}
/** * Patches a part of a file. * * @param path the file * @param fromLine the first line to patch. This is the number you see in an editor, 1-based, inclusive. * @param toLine the last line to patch. This is the number you see in an editor, inclusive. * Set {@code toLine} to {@code fromLine - 1} if you only want to insert lines. * @param from lines to remove, used to ensure the correct lines are removed. Can be multiple lines or empty. * It's compared to existing lines with all lines trimmed and no new lines at both ends. Ignored if null. * @param to the newly added lines, can be multiple lines or empty. New line at end is optional. Cannot be null. * @throws IOException if there's an I/O error or {@code from} does not match the existing lines * @throws IndexOutOfBoundsException if {@code fromLine} or {@code toLine} is invalid
*/ publicstaticvoid patch(Path path, int fromLine, int toLine, String from, String to) throws IOException { var lines = Files.readAllLines(path); // The next line does a from/to as well var subList = lines.subList(fromLine - 1, toLine); if (from != null) { // Each line is trimmed so caller needs not care about indentation. // Caller also needs not care about new lines on both ends. // New lines inside are preserved.
String actuallyRemoved = subList.stream()
.map(String::trim)
.collect(Collectors.joining("\n")).trim();
String wantToRemove = from.lines()
.map(String::trim)
.collect(Collectors.joining("\n")).trim(); if (!actuallyRemoved.equals(wantToRemove)) { thrownew IOException("Removed not the same: ["
+ String.join("\\n", subList) + "] and ["
+ from.replaceAll("\\n", "\\\\n") + "]");
}
}
subList.clear();
lines.addAll(fromLine - 1, to.lines().toList());
Files.write(path, lines);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.