8022213: Intermittent test failures in java/net/URLClassLoader
Reviewed-by: dxu, alanb
--- a/jdk/test/java/net/URLClassLoader/closetest/CloseTest.java Wed Nov 13 07:49:42 2013 -0800
+++ b/jdk/test/java/net/URLClassLoader/closetest/CloseTest.java Wed Nov 13 16:44:12 2013 +0000
@@ -25,7 +25,8 @@
* @test
* @bug 4167874
* @library ../../../../com/sun/net/httpserver
- * @build FileServerHandler
+ * @library /lib/testlibrary
+ * @build FileServerHandler jdk.testlibrary.FileUtils
* @run shell build.sh
* @run main/othervm CloseTest
* @summary URL-downloaded jar files can consume all available file descriptors
--- a/jdk/test/java/net/URLClassLoader/closetest/Common.java Wed Nov 13 07:49:42 2013 -0800
+++ b/jdk/test/java/net/URLClassLoader/closetest/Common.java Wed Nov 13 16:44:12 2013 +0000
@@ -23,6 +23,9 @@
import java.io.*;
import java.net.*;
+import java.nio.file.Files;
+import jdk.testlibrary.FileUtils;
+import static java.nio.file.StandardCopyOption.*;
public class Common {
@@ -39,42 +42,16 @@
if (!src.isFile()) {
throw new RuntimeException ("File not found: " + src.toString());
}
- dst.delete();
- dst.createNewFile();
- FileInputStream i = new FileInputStream (src);
- FileOutputStream o = new FileOutputStream (dst);
- byte[] buf = new byte [1024];
- int count;
- while ((count=i.read(buf)) >= 0) {
- o.write (buf, 0, count);
- }
- i.close();
- o.close();
+ Files.copy(src.toPath(), dst.toPath(), REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException (e);
}
}
- static void rm_minus_rf (File path) {
- if (!path.exists()) {
+ static void rm_minus_rf (File path) throws IOException, InterruptedException {
+ if (!path.exists())
return;
- }
- if (path.isFile()) {
- if (!path.delete()) {
- throw new RuntimeException ("Could not delete " + path);
- }
- } else if (path.isDirectory ()) {
- String[] names = path.list();
- File[] files = path.listFiles();
- for (int i=0; i<files.length; i++) {
- rm_minus_rf (new File(path, names[i]));
- }
- if (!path.delete()) {
- throw new RuntimeException ("Could not delete " + path);
- }
- } else {
- throw new RuntimeException ("Trying to delete something that isn't a file or a directory");
- }
+ FileUtils.deleteFileTreeWithRetry(path.toPath());
}
static void copyDir (File src, File dst) {
--- a/jdk/test/java/net/URLClassLoader/closetest/GetResourceAsStream.java Wed Nov 13 07:49:42 2013 -0800
+++ b/jdk/test/java/net/URLClassLoader/closetest/GetResourceAsStream.java Wed Nov 13 16:44:12 2013 +0000
@@ -24,6 +24,8 @@
/**
* @test
* @bug 6899919
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.FileUtils
* @run shell build2.sh
* @run main/othervm GetResourceAsStream
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/FileUtils.java Wed Nov 13 16:44:12 2013 +0000
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+package jdk.testlibrary;
+
+import java.io.IOException;
+import java.nio.file.DirectoryNotEmptyException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Common library for various test file utility functions.
+ */
+public final class FileUtils {
+
+ private static final boolean isWindows =
+ System.getProperty("os.name").startsWith("Windows");
+ private static final int RETRY_DELETE_MILLIS = isWindows ? 500 : 0;
+ private static final int MAX_RETRY_DELETE_TIMES = isWindows ? 15 : 0;
+
+ /**
+ * 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
+ */
+ public static void deleteFileWithRetry(Path path)
+ throws IOException
+ {
+ try {
+ deleteFileWithRetry0(path);
+ } catch (InterruptedException x) {
+ throw new IOException("Interrupted while deleting.", x);
+ }
+ }
+
+ private static void deleteFileWithRetry0(Path path)
+ throws IOException, InterruptedException
+ {
+ int times = 0;
+ IOException ioe = null;
+ while (true) {
+ try {
+ Files.delete(path);
+ while (Files.exists(path)) {
+ times++;
+ if (times > MAX_RETRY_DELETE_TIMES)
+ throw new 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);
+
+ if (times > MAX_RETRY_DELETE_TIMES)
+ throw ioe;
+ Thread.sleep(RETRY_DELETE_MILLIS);
+ }
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public static void 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;
+ }
+
+ public static List<IOException> deleteFileTreeUnchecked(Path dir) {
+ final List<IOException> excs = new ArrayList<>();
+ try {
+ java.nio.file.Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ try {
+ deleteFileWithRetry0(file);
+ } catch (IOException x) {
+ excs.add(x);
+ } catch (InterruptedException x) {
+ excs.add(new IOException("Interrupted while deleting.", x));
+ return FileVisitResult.TERMINATE;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ try {
+ deleteFileWithRetry0(dir);
+ } catch (IOException x) {
+ excs.add(x);
+ } catch (InterruptedException x) {
+ excs.add(new IOException("Interrupted while deleting.", x));
+ return FileVisitResult.TERMINATE;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ excs.add(exc);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException x) {
+ excs.add(x);
+ }
+ return excs;
+ }
+}
+