1 /*
   2  * Copyright (c) 2012, 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 /*
  25  * @test
  26  * @bug 7194005
  27  * @summary launcher handling of zip64 archives (Scenario A and B)
  28  * @modules jdk.compiler
  29  *          jdk.zipfs
  30  * @compile  -XDignore.symbol.file BigJar.java
  31  * @run main/timeout=600 BigJar
  32  */
  33 /*
  34  * This test consists of two scenarios:
  35  *
  36  * Scenario A: create a jar with entries exceeding 64K, add a main class and
  37  * see if the launcher can handle it.
  38  *
  39  * Scenario A1: create a jar as in A, but add a zipfile comment as well.
  40  *
  41  * Scenario B: create a jar with a large enough file exceeding 4GB, and
  42  * similarly test the launcher. This test can be run optionally by using the
  43  * following jtreg option:
  44  *  "-javaoptions:-DBigJar_testScenarioB=true"
  45  * or set
  46  *  "BigJar_testScenarioB" environment variable.
  47  *
  48  * Note this test will only run iff all the disk requirements are met at runtime.
  49  */
  50 import java.io.BufferedInputStream;
  51 import java.io.BufferedOutputStream;
  52 import java.io.File;
  53 import java.io.FileInputStream;
  54 import java.io.FileOutputStream;
  55 import java.io.IOException;
  56 import java.io.OutputStream;
  57 import java.nio.file.Files;
  58 import java.nio.file.Path;
  59 import java.util.ArrayList;
  60 import java.util.List;
  61 import java.util.jar.Attributes;
  62 import java.util.jar.JarEntry;
  63 import java.util.jar.JarOutputStream;
  64 import java.util.jar.Manifest;
  65 import java.util.zip.CRC32;
  66 import java.util.zip.ZipEntry;
  67 import java.util.zip.ZipOutputStream;
  68 
  69 public class BigJar extends TestHelper {
  70 
  71     private static final long GIGA = 1024 * 1024 * 1024;
  72     private static final int BUFFER_LEN = Short.MAX_VALUE * 2;
  73 
  74     long getCount(long minlength) {
  75         return (minlength / BUFFER_LEN) + 1;
  76     }
  77 
  78     long computeCRC(long minlength) {
  79         CRC32 crc = new CRC32();
  80         byte[] buffer = new byte[BUFFER_LEN];
  81         long count = getCount(minlength);
  82         for (long i = 0; i < count; i++) {
  83             crc.update(buffer);
  84         }
  85         return crc.getValue();
  86     }
  87 
  88     long computeCRC(File inFile) throws IOException {
  89         byte[] buffer = new byte[8192];
  90         CRC32 crc = new CRC32();
  91         try (FileInputStream fis = new FileInputStream(inFile);
  92                 BufferedInputStream bis = new BufferedInputStream(fis)) {
  93             int n = bis.read(buffer);
  94             while (n > 0) {
  95                 crc.update(buffer, 0, n);
  96                 n = bis.read(buffer);
  97             }
  98         }
  99         return crc.getValue();
 100     }
 101 
 102     void createLargeFile(OutputStream os, long minlength) throws IOException {
 103         byte[] buffer = new byte[BUFFER_LEN];
 104         long count = getCount(minlength);
 105         for (long i = 0; i < count; i++) {
 106             os.write(buffer);
 107         }
 108         os.flush();
 109     }
 110 
 111     Manifest createMainClass(File javaFile) throws IOException {
 112         javaFile.delete();
 113         List<String> content = new ArrayList<>();
 114         content.add("public class " + baseName(javaFile) + "{");
 115         content.add("public static void main(String... args) {");
 116         content.add("System.out.println(\"Hello World\\n\");");
 117         content.add("System.exit(0);");
 118         content.add("}");
 119         content.add("}");
 120         createFile(javaFile, content);
 121         compile(javaFile.getName());
 122         Manifest manifest = new Manifest();
 123         manifest.clear();
 124         manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
 125         manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, baseName(javaFile));
 126         System.out.println(manifest.getMainAttributes().keySet());
 127         System.out.println(manifest.getMainAttributes().values());
 128         return manifest;
 129     }
 130 
 131     void createJarWithLargeFile(File jarFile, long minlength) throws IOException {
 132         File javaFile = new File("Foo.java");
 133         Manifest manifest = createMainClass(javaFile);
 134         File classFile = getClassFile(javaFile);
 135         try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
 136                 BufferedOutputStream bos = new BufferedOutputStream(jos);
 137                 FileInputStream fis = new FileInputStream(classFile);) {
 138             jos.setLevel(ZipOutputStream.STORED);
 139             jos.setMethod(0);
 140 
 141             JarEntry je = new JarEntry("large.data");
 142             je.setCompressedSize(getCount(minlength) * BUFFER_LEN);
 143             je.setSize(getCount(minlength) * BUFFER_LEN);
 144             je.setCrc(computeCRC(minlength));
 145             je.setMethod(ZipEntry.STORED);
 146             jos.putNextEntry(je);
 147             createLargeFile(bos, minlength);
 148 
 149             je = new JarEntry(classFile.getName());
 150             je.setCompressedSize(classFile.length());
 151             je.setSize(classFile.length());
 152             je.setCrc(computeCRC(classFile));
 153             je.setMethod(ZipEntry.STORED);
 154             jos.putNextEntry(je);
 155             copyStream(fis, bos);
 156             bos.flush();
 157             jos.closeEntry();
 158         }
 159     }
 160 
 161     void createLargeJar(File jarFile, String comment) throws IOException {
 162         final int MAX = Short.MAX_VALUE * 2 + 10;
 163         JarEntry je = null;
 164         File javaFile = new File("Foo.java");
 165         File classFile = getClassFile(javaFile);
 166         Manifest manifest = createMainClass(javaFile);
 167         try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
 168                 FileInputStream fis = new FileInputStream(classFile)) {
 169             jos.setLevel(JarOutputStream.STORED);
 170             jos.setMethod(JarOutputStream.STORED);
 171             for (int i = 0; i < MAX; i++) {
 172                 je = new JarEntry("X" + i + ".txt");
 173                 je.setSize(0);
 174                 je.setCompressedSize(0);
 175                 je.setCrc(0);
 176                 jos.putNextEntry(je);
 177             }
 178 
 179             // add a class file
 180             je = new JarEntry(classFile.getName());
 181             je.setCompressedSize(classFile.length());
 182             je.setSize(classFile.length());
 183             je.setCrc(computeCRC(classFile));
 184             jos.putNextEntry(je);
 185             copyStream(fis, jos);
 186             jos.closeEntry();
 187             if (comment != null) {
 188                 jos.setComment(comment);
 189             }
 190         }
 191     }
 192 
 193     void testTheJar(File theJar) throws Exception {
 194         try {
 195             TestResult tr = doExec(javaCmd, "-jar", theJar.getName());
 196             tr.checkPositive();
 197             if (!tr.testStatus) {
 198                 System.out.println(tr);
 199                 throw new Exception("Failed");
 200             }
 201         } finally {
 202             theJar.delete();
 203         }
 204     }
 205 
 206     // a jar with entries exceeding 64k + a class file for the existential test
 207     @Test
 208     void testScenarioA() throws Exception {
 209         File largeJar = new File("large.jar");
 210         createLargeJar(largeJar, null);
 211         testTheJar(largeJar);
 212     }
 213 
 214      // a jar with entries exceeding 64k and zip comment
 215     @Test
 216     void testScenarioA1() throws Exception {
 217         File largeJar = new File("largewithcomment.jar");
 218         createLargeJar(largeJar, "A really large jar with a comment");
 219         testTheJar(largeJar);
 220     }
 221 
 222     // a jar with an enormous file + a class file for the existential test
 223     @Test
 224     void testScenarioB() throws Exception {
 225         final String testString = "BigJar_testScenarioB";
 226         if (Boolean.getBoolean(testString) == false &&
 227                 System.getenv(testString) == null) {
 228             System.out.println("Warning: testScenarioB passes vacuously");
 229             return;
 230         }
 231         final File largeJar = new File("huge.jar");
 232 
 233         final Path path = largeJar.getAbsoluteFile().getParentFile().toPath();
 234         final long available = Files.getFileStore(path).getUsableSpace();
 235         final long MAX_VALUE = 0xFFFF_FFFFL;
 236 
 237         final long absolute = MAX_VALUE + 1L;
 238         final long required = (long) (absolute * 1.1); // pad for sundries
 239         System.out.println("\tavailable: " + available / GIGA + " GB");
 240         System.out.println("\trequired: " + required / GIGA + " GB");
 241 
 242         if (available > required) {
 243             createJarWithLargeFile(largeJar, absolute);
 244             testTheJar(largeJar);
 245         } else {
 246             System.out.println("Warning: testScenarioB passes vacuously,"
 247                     + " requirements exceeds available space");
 248         }
 249     }
 250 
 251     public static void main(String... args) throws Exception {
 252         BigJar bj = new BigJar();
 253         bj.run(args);
 254         if (testExitValue > 0) {
 255             System.out.println("Total of " + testExitValue + " failed");
 256             System.exit(1);
 257         } else {
 258             System.out.println("All tests pass");
 259         }
 260     }
 261 }