/* * Copyright (c) 2019, 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. *
*/
/** * @test * @bug 8230870 * @summary Test ZIP Filesystem behavior with ~64k entries * @modules jdk.zipfs * @run testng LargeEntriesTest
*/ publicclass LargeEntriesTest {
privatestaticfinal Path HERE = Path.of(".");
/** * Number of ZIP entries which results in the use of ZIP64
*/ privatestaticfinalint ZIP64_ENTRIES = 65535;
/** * Classes and MANIFEST attribute used for invoking Main via java -jar
*/ privatestaticfinal String MANIFEST_MAIN_CLASS = "LargeEntriesTest$Main"; privatestaticfinal String MAIN_CLASS = "LargeEntriesTest$Main.class"; privatestaticfinal String THIS_CLASS = "LargeEntriesTest.class";
/** * Number of entries included in the JAR file including META-INF, * MANIFEST.MF, and the classes associated with this test
*/ privatestaticfinalint ADDITIONAL_JAR_ENTRIES = 4;
/** * Value used for creating the required entries in a ZIP or JAR file
*/ privatestaticfinal String ZIP_FILE_VALUE = "US Open 2019"; privatestaticfinalbyte[] ZIP_FILE_ENTRY =
ZIP_FILE_VALUE.getBytes(StandardCharsets.UTF_8);
/** * Location of the classes to be added to the JAR file
*/ staticfinal Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
privatestaticfinal SecureRandom random = new SecureRandom();
/** * Fields used for timing runs
*/ privatestaticint testNumberRunning; privatestaticlong runningTestTime; privatestaticlong startTestRunTime; privatestaticfinaldouble NANOS_IN_SECOND = 1_000_000_000.0;
@AfterMethod(enabled = false) publicvoid afterMethod() { long endRunningTestTime = System.nanoTime(); long duration = endRunningTestTime - runningTestTime;
System.out.printf("**** Completed test number: %s, Time: %.4f%n",
testNumberRunning, duration / NANOS_IN_SECOND);
testNumberRunning++;
}
/** * Validate that you can create a ZIP file with and without compression * and that the ZIP file is created using ZIP64 if there are 65535 or * more entries. * * @param env Properties used for creating the ZIP Filesystem * @param compression Indicates whether the files are DEFLATED(default) * or STORED * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file
*/
@Test(dataProvider = "zipfsMap", enabled = true) publicvoid testZip(Map<String, String> env, int compression) throws Exception {
/** * Validate that when the forceZIP64End property is set to true, * that ZIP64 is used. * * @param env Properties used for creating the ZIP Filesystem * @param compression Indicates whether the files are DEFLATED(default) * or STORED * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file
*/
@Test(dataProvider = "zip64Map", enabled = true) publicvoid testForceZIP64End(Map<String, String> env, int compression) throws Exception {
/** * Validate that you can create a JAR file with and without compression * and that the JAR file is created using ZIP64 if there are 65535 or * more entries. * * @param env Properties used for creating the ZIP Filesystem * @param compression Indicates whether the files are DEFLATED(default) * or STORED * @throws Exception If an error occurs during the creation, verification or * deletion of the JAR file
*/
@Test(dataProvider = "zipfsMap", enabled = true) publicvoid testJar(Map<String, String> env, int compression) throws Exception { for (int entries = ZIP64_ENTRIES - 1; entries < ZIP64_ENTRIES + 2; entries++) {
Path jar = generatePath(HERE, "test", ".jar");
// Now run the Main-Class specified the Manifest
runJar(jar.getFileName().toString()).assertSuccess()
.validate(r -> assertTrue(r.output.matches("\\AMain\\Z")));
/** * Create a ZIP File System using the specified properties and a ZIP file * with the specified number of entries * * @param zipFile Path to the ZIP File to create * @param env Properties used for creating the ZIP Filesystem * @param entries Number of entries to add to the ZIP File * @throws IOException If an error occurs while creating the ZIP file
*/ privatevoid createZipFile(Path zipFile, Map<String, String> env, int entries) throws IOException {
System.out.printf("Creating file = %s%n", zipFile); try (FileSystem zipfs =
FileSystems.newFileSystem(zipFile, env)) {
for (int i = 0; i < entries; i++) {
Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE);
}
}
}
/** * Create a ZIP File System using the specified properties and a JAR file * with the specified number of entries * * @param zipFile Path to the JAR File to create * @param env Properties used for creating the ZIP Filesystem * @param entries Number of entries to add to the JAR File * @throws IOException If an error occurs while creating the JAR file
*/ privatevoid createJarFile(Path zipFile, Map<String, String> env, int entries) throws IOException {
System.out.printf("Creating file = %s%n", zipFile);
String jdkVendor = System.getProperty("java.vendor");
String jdkVersion = System.getProperty("java.version");
String manifest = "Manifest-Version: 1.0"
+ System.lineSeparator()
+ "Main-Class: " + MANIFEST_MAIN_CLASS
+ System.lineSeparator()
+ "Created-By: " + jdkVersion + " (" + jdkVendor + ")";
try (FileSystem zipfs =
FileSystems.newFileSystem(zipFile, env);
InputStream in = new ByteArrayInputStream(manifest.getBytes())) {
// Get ZIP FS path to META-INF/MANIFEST.MF
Path metadir = zipfs.getPath("/", "META-INF");
Path manifestFile = metadir.resolve("MANIFEST.MF");
// Create META-INF directory if it does not already exist and // add the MANIFEST.MF file if (!Files.exists(metadir))
Files.createDirectory(zipfs.getPath("/", "META-INF"));
Files.copy(in, manifestFile);
// Add the needed test classes
Path target = zipfs.getPath("/");
Files.copy(TEST_CLASSES.resolve(MAIN_CLASS),
target.resolve(MAIN_CLASS));
Files.copy(TEST_CLASSES.resolve(THIS_CLASS),
target.resolve(THIS_CLASS));
// Add the remaining entries that are required for (int i = ADDITIONAL_JAR_ENTRIES; i < entries; i++) {
Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE);
}
}
}
/* * DataProvider used to validate that you can create a ZIP file with and * without compression.
*/
@DataProvider(name = "zipfsMap") private Object[][] zipfsMap() { returnnew Object[][]{
{Map.of("create", "true"), ZipEntry.DEFLATED},
{Map.of("create", "true", "noCompression", "true"),
ZipEntry.STORED},
{Map.of("create", "true", "noCompression", "false"),
ZipEntry.DEFLATED}
};
}
/* * DataProvider used to validate that you can create a ZIP file with/without * ZIP64 format extensions
*/
@DataProvider(name = "zip64Map") private Object[][] zip64Map() { returnnew Object[][]{
{Map.of("create", "true", "forceZIP64End", "true"),
ZipEntry.DEFLATED},
{Map.of("create", "true", "noCompression", "true", "forceZIP64End", "true"), ZipEntry.STORED},
{Map.of("create", "true", "noCompression", "false", "forceZIP64End", "false"), ZipEntry.DEFLATED},
{Map.of("create", "true", "noCompression", "true", "forceZIP64End", "false"), ZipEntry.STORED}
};
}
/** * Verify that the given path is a ZIP file containing the * expected entries. * * @param zipfile ZIP file to be validated * @param method Expected Compression method: STORED or DEFLATED * @param entries Number of expected entries * @param isZip64Forced true if ZIP64 use is being forced; false otherwise * @param start Starting number for verifying entries * @throws Exception If an error occurs while examining the ZIP file
*/ privatestaticvoid verify(Path zipfile, int method, int entries, boolean isZip64Forced, int start) throws Exception { // check entries with ZIP API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count
assertEquals(entries, zf.size());
// check compression method and content of each entry for (int i = start; i < entries; i++) {
ZipEntry ze = zf.getEntry("Entry-" + i);
assertNotNull(ze);
assertEquals(method, ze.getMethod()); try (InputStream is = zf.getInputStream(ze)) { byte[] bytes = is.readAllBytes();
assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY));
}
}
} // check entries with FileSystem API try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
/** * Determine if the specified property name=true/"true" * * @param env ZIP Filesystem Map * @param name property to validate * @return true if the property value is set to true/"true"; false otherwise
*/ privatestaticboolean isTrue(Map<String, ?> env, String name) { return"true".equals(env.get(name)) || TRUE.equals(env.get(name));
}
/** * Check to see if the ZIP64 End of Central Directory Locator has been found * * @param b byte array to check for the locator in * @param n starting offset for the search * @return true if the Zip64 End of Central Directory Locator is found; false * otherwise
*/ privatestaticboolean end64SigAt(byte[] b, int n) { return b[n] == 'P' & b[n + 1] == 'K' & b[n + 2] == 6 & b[n + 3] == 6;
}
/** * Utility method that checks the ZIP file for the use of the ZIP64 * End of Central Directory Locator * * @param zipFile ZIP file to check * @return true if the ZIP64 End of Central Directory Locator is found; false * otherwise * * @throws Exception If an error occurs while traversing the file
*/ privatestaticboolean usesZip64(File zipFile) throws Exception {
try (RandomAccessFile raf = new RandomAccessFile(zipFile, "r")) { byte[] buf = newbyte[4096]; long seeklen = raf.length() - buf.length;
if (seeklen < 0)
seeklen = 0;
raf.seek(seeklen);
raf.read(buf); for (int i = 0; i < buf.length - 4; i++) { // Is there a ZIP64 End of Central Directory Locator? if (end64SigAt(buf, i)) { returntrue;
}
}
} returnfalse;
}
/** * Generate a temporary file Path * * @param dir Directory used to create the path * @param prefix The prefix string used to create the path * @param suffix The suffix string used to create the path * @return Path that was generated
*/ privatestatic Path generatePath(Path dir, String prefix, String suffix) { long n = random.nextLong();
String s = prefix + Long.toUnsignedString(n) + suffix;
Path name = dir.getFileSystem().getPath(s); // the generated name should be a simple file name if (name.getParent() != null) thrownew IllegalArgumentException("Invalid prefix or suffix"); return dir.resolve(name);
}
/** * Utility method to return a formatted String of the key:value entries for * a Map * * @param env Map to format * @return Formatted string of the Map entries
*/ privatestatic String formatMap(Map<String, String> env) { return env.entrySet().stream()
.map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
.collect(joining(", "));
}
/** * Validates that a jar created using ZIP FS can be used by the java * tool to run a program specified in the Main-Class Manifest attribute * * @param jarFile Name of the JAR file to specify to the -jar option * @return A Result object representing the return code and output from the * program that was invoked
*/ privatestatic Result runJar(String jarFile) {
String javaHome = System.getProperty("java.home");
String java = Paths.get(javaHome, "bin", "java").toString();
String[] cmd = {java, "-jar", jarFile};
String output;
ProcessBuilder pb = new ProcessBuilder(cmd);
Process p; try {
p = pb.start();
output = toString(p.getInputStream(), p.getErrorStream());
p.waitFor();
} catch (IOException | InterruptedException e) { thrownew RuntimeException(
format("Error invoking: '%s', Exception= %s", pb.command(), e));
}
returnnew Result(p.exitValue(), output);
}
/** * Utility method to combine the output and error streams for the Process * started by ProcessBuilder * * @param is Process Outputstream * @param is2 Process ErrorStream * @return String representing the combination of the OutputStream & ErrorStream * @throws IOException If an error occurs while combining the streams
*/ privatestatic String toString(InputStream is, InputStream is2) throws IOException { try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
InputStream concatenated = new SequenceInputStream(is, is2)) {
concatenated.transferTo(dst); returnnew String(dst.toByteArray(), StandardCharsets.UTF_8);
}
}
/** * Wrapper class used to verify the results from a ProcessBuilder invocation
*/ privatestaticclass Result { finalint ec; // Return code for command that was executed final String output; // Output from the command that was executed
/** * Constructor * * @param ec Return code from the ProcessBuilder invocation * @param output ProcessBuilder output to be validated
*/ private Result(int ec, String output) { this.ec = ec; this.output = output;
}
/** * Validate that the command that was executed completed successfully * * @return This Result object
*/
Result assertSuccess() {
assertEquals(ec, 0, format("Expected ec 0, received: %s, output [%s]", ec, output)); returnthis;
}
/** * Validate that the expected result is received * * @param r The operation to perform * @return This Result object
*/
Result validate(Consumer<Result> r) {
r.accept(this); returnthis;
}
}
/** * Trivial class used to validate that a JAR created using ZIP FS * can be successfully executed
*/ publicstaticclass Main { publicstaticvoid main(String[] args) {
System.out.print("Main");
}
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet)
¤
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.