--- old/src/java.logging/share/classes/java/util/logging/FileHandler.java 2017-11-09 19:11:39.000000000 +0000 +++ new/src/java.logging/share/classes/java/util/logging/FileHandler.java 2017-11-09 19:11:38.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -616,79 +616,97 @@ * @throws IOException */ private File generate(String pattern, int generation, int unique) - throws IOException { - File file = null; - String word = ""; - int ix = 0; + throws IOException + { + return generate(pattern, count, generation, unique); + } + + // The static method here is provided for whitebox testing of the algorithm. + static File generate(String pat, int count, int generation, int unique) + throws IOException + { + Path path = Paths.get(pat); + Path result = null; boolean sawg = false; boolean sawu = false; - while (ix < pattern.length()) { - char ch = pattern.charAt(ix); - ix++; - char ch2 = 0; - if (ix < pattern.length()) { - ch2 = Character.toLowerCase(pattern.charAt(ix)); + StringBuilder word = new StringBuilder(); + Path prev = null; + for (Path elem : path) { + if (prev != null) { + prev = prev.resolveSibling(word.toString()); + if (result == null) result = prev; + else result = result.resolve(prev); } - if (ch == '/') { - if (file == null) { - file = new File(word); - } else { - file = new File(file, word); + String pattern = elem.toString(); + int ix = 0; + word.setLength(0); + while (ix < pattern.length()) { + char ch = pattern.charAt(ix); + ix++; + char ch2 = 0; + if (ix < pattern.length()) { + ch2 = Character.toLowerCase(pattern.charAt(ix)); } - word = ""; - continue; - } else if (ch == '%') { - if (ch2 == 't') { - String tmpDir = System.getProperty("java.io.tmpdir"); - if (tmpDir == null) { - tmpDir = System.getProperty("user.home"); - } - file = new File(tmpDir); - ix++; - word = ""; - continue; - } else if (ch2 == 'h') { - file = new File(System.getProperty("user.home")); - if (jdk.internal.misc.VM.isSetUID()) { - // Ok, we are in a set UID program. For safety's sake - // we disallow attempts to open files relative to %h. - throw new IOException("can't use %h in set UID program"); + if (ch == '%') { + if (ch2 == 't') { + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir == null) { + tmpDir = System.getProperty("user.home"); + } + result = Paths.get(tmpDir); + ix++; + word.setLength(0); + continue; + } else if (ch2 == 'h') { + result = Paths.get(System.getProperty("user.home")); + if (jdk.internal.misc.VM.isSetUID()) { + // Ok, we are in a set UID program. For safety's sake + // we disallow attempts to open files relative to %h. + throw new IOException("can't use %h in set UID program"); + } + ix++; + word.setLength(0); + continue; + } else if (ch2 == 'g') { + word = word.append(generation); + sawg = true; + ix++; + continue; + } else if (ch2 == 'u') { + word = word.append(unique); + sawu = true; + ix++; + continue; + } else if (ch2 == '%') { + word = word.append('%'); + ix++; + continue; } - ix++; - word = ""; - continue; - } else if (ch2 == 'g') { - word = word + generation; - sawg = true; - ix++; - continue; - } else if (ch2 == 'u') { - word = word + unique; - sawu = true; - ix++; - continue; - } else if (ch2 == '%') { - word = word + "%"; - ix++; - continue; } + word = word.append(ch); } - word = word + ch; + prev = elem; } + if (count > 1 && !sawg) { - word = word + "." + generation; + word = word.append('.').append(generation); } if (unique > 0 && !sawu) { - word = word + "." + unique; + word = word.append('.').append(unique); } if (word.length() > 0) { - if (file == null) { - file = new File(word); - } else { - file = new File(file, word); - } + String n = word.toString(); + Path p = prev == null ? Paths.get(n) : prev.resolveSibling(n); + result = result == null ? p : result.resolve(p); + } else if (result == null) { + result = Paths.get(""); + } + + if (path.getRoot() == null) { + return result.toFile(); + } else { + return path.getRoot().resolve(result).toFile(); } - return file; } /** --- /dev/null 2017-11-09 19:11:40.000000000 +0000 +++ new/test/jdk/java/util/logging/FileHandlerPatternGeneration.java 2017-11-09 19:11:40.000000000 +0000 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; + +/** + * @test + * @bug 8189953 + * @summary tests the pattern generation algorithm + * @modules java.logging/java.util.logging:open + * @run main/othervm FileHandlerPatternGeneration + * @author danielfuchs + */ +public class FileHandlerPatternGeneration { + + /** + * An array of strings where the elements at even indices are the input + * to give to FileHandler::generate(pattern, count, generation, unique), + * and the elements at the next odd index are a partially computed expected + * output, where %t, %h, %u, %g and file separator still need to be replaced. + * The final expected output is obtained by passing the partially computed + * output to FileHandlerPatternGeneration::generateExpected + *
+ * The test verifies that {@code
+ * FileHandler.generate(PATTERN[i], c, g, u).toString()
+ * }
+ * is equal to {@code
+ * FileHandlerPatternGeneration.generateExpected(PATTERN[i],
+ * PATTERN[i+1],
+ * c, g, u)
+ * }
+ */
+ static final String[] PATTERNS = {
+ "C:/Workspace/hoge.log", "C:/Workspace/hoge.log",
+ "C:/Workspace%g/hoge.log", "C:/Workspace%g/hoge.log",
+ "C:/%uWorkspace/hoge.log", "C:/%uWorkspace/hoge.log",
+ "C:/%uWorkspace%g/hoge.log", "C:/%uWorkspace%g/hoge.log",
+ "C:/Workspace/%ghoge.log", "C:/Workspace/%ghoge.log",
+ "C:/Workspace/%ghoge%u.log", "C:/Workspace/%ghoge%u.log",
+ "C:/Workspace-%g/hoge.log", "C:/Workspace-%g/hoge.log",
+ "C:/Work%hspace/hoge.log", "%h/space/hoge.log",
+ "C:/Works%tpace%g/hoge.log", "%t/pace%g/hoge.log",
+ "C:/%uWork%hspace/hoge.log", "%h/space/hoge.log",
+ "C:/%uWorkspace%g/%thoge.log", "%t/hoge.log",
+ "C:/Workspace/%g%h%%hoge.log", "%h/%%hoge.log",
+ "C:/Work%h%%hspace/hoge.log", "%h/%%hspace/hoge.log",
+ "C:/Works%t%%hpace%g/hoge.log", "%t/%%hpace%g/hoge.log",
+ "C:/%uWork%h%%tspace/hoge.log", "%h/%%tspace/hoge.log",
+ "C:/%uWorkspace%g/%t%%hoge.log", "%t/%%hoge.log",
+ "C:/Workspace/%g%h%%hoge.log", "%h/%%hoge.log",
+ "ahaha", "ahaha",
+ "ahaha/ahabe", "ahaha/ahabe",
+ "../ahaha/ahabe", "../ahaha/ahabe",
+ "/x%ty/w/hoge.log", "%t/y/w/hoge.log",
+ "/x/%ty/w/hoge.log", "%t/y/w/hoge.log",
+ "/x%t/y/w/hoge.log", "%t/y/w/hoge.log",
+ "/x/%t/y/w/hoge.log", "%t/y/w/hoge.log",
+ "%ty/w/hoge.log", "%t/y/w/hoge.log",
+ "%t/y/w/hoge.log", "%t/y/w/hoge.log",
+ "/x%hy/w/hoge.log", "%h/y/w/hoge.log",
+ "/x/%hy/w/hoge.log", "%h/y/w/hoge.log",
+ "/x%h/y/w/hoge.log", "%h/y/w/hoge.log",
+ "/x/%h/y/w/hoge.log", "%h/y/w/hoge.log",
+ "%hy/w/hoge.log", "%h/y/w/hoge.log",
+ "%h/y/w/hoge.log", "%h/y/w/hoge.log",
+ "ahaha-%u-%g", "ahaha-%u-%g",
+ "ahaha-%g/ahabe-%u", "ahaha-%g/ahabe-%u",
+ "../ahaha-%u/ahabe", "../ahaha-%u/ahabe",
+ "/x%ty/w/hoge-%g.log", "%t/y/w/hoge-%g.log",
+ "/x/%ty/w/hoge-%u.log", "%t/y/w/hoge-%u.log",
+ "%u-%g/x%t/y/w/hoge.log", "%t/y/w/hoge.log",
+ "/x/%g%t%u/y/w/hoge.log", "%t/%u/y/w/hoge.log",
+ "%ty/w-%g/hoge.log", "%t/y/w-%g/hoge.log",
+ "%t/y/w-%u/hoge.log", "%t/y/w-%u/hoge.log",
+ "/x%hy/%u-%g-w/hoge.log", "%h/y/%u-%g-w/hoge.log",
+ "/x/%hy/w-%u-%g/hoge.log", "%h/y/w-%u-%g/hoge.log",
+ "/x%h/y/w/%u-%ghoge.log", "%h/y/w/%u-%ghoge.log",
+ "/x/%h/y/w/hoge-%u-%g.log", "%h/y/w/hoge-%u-%g.log",
+ "%hy/w/%u-%g-hoge.log", "%h/y/w/%u-%g-hoge.log",
+ "%h/y/w/hoge-%u-%g.log", "%h/y/w/hoge-%u-%g.log",
+ "/x/y/z/hoge-%u.log", "/x/y/z/hoge-%u.log",
+ };
+
+ // the (count, generation, unique) parameters to pass to
+ // FileHandler.generate(pattern, count, generation, unique)
+ static final int[][] GENERATIONS = {
+ {0, 0, 0},
+ {0, 1, 0},
+ {0, 1, 1},
+ {1, 1, 0},
+ {1, 1, 1},
+ {1, 1, 2},
+ {1, 2, 3},
+ {3, 4, 0},
+ {3, 4, 1},
+ {3, 4, 2},
+ {3, 0, 5},
+ {3, 1, 5},
+ {3, 2, 5},
+ };
+
+ static final Class