1 /*
   2  * Copyright (c) 2007, 2013, 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 import java.nio.file.Path;
  25 import java.io.BufferedReader;
  26 import java.io.ByteArrayOutputStream;
  27 import java.io.Closeable;
  28 import java.io.File;
  29 import java.io.FileFilter;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.PrintStream;
  35 import java.nio.charset.Charset;
  36 import java.nio.file.Files;
  37 import java.util.ArrayList;
  38 import java.util.Arrays;
  39 import java.util.Collections;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.jar.JarFile;
  43 import java.util.jar.JarOutputStream;
  44 import java.util.jar.Pack200;
  45 import java.util.zip.ZipEntry;
  46 import java.util.zip.ZipFile;
  47 
  48 import static java.nio.file.StandardCopyOption.*;
  49 import static java.nio.file.StandardOpenOption.*;
  50 
  51 /**
  52  *
  53  * @author ksrini
  54  */
  55 
  56 /*
  57  * This class contains all the commonly used utilities used by various tests
  58  * in this directory.
  59  */
  60 class Utils {
  61     static final String JavaHome = System.getProperty("test.java",
  62             System.getProperty("java.home"));
  63     static final boolean IsWindows =
  64             System.getProperty("os.name").startsWith("Windows");
  65     static final boolean Is64Bit =
  66             System.getProperty("sun.arch.data.model", "32").equals("64");
  67     static final File   JavaSDK =  new File(JavaHome).getParentFile();
  68 
  69     static final String PACK_FILE_EXT   = ".pack";
  70     static final String JAVA_FILE_EXT   = ".java";
  71     static final String CLASS_FILE_EXT  = ".class";
  72     static final String JAR_FILE_EXT    = ".jar";
  73 
  74     static final File   TEST_SRC_DIR = new File(System.getProperty("test.src"));
  75     static final File   TEST_CLS_DIR = new File(System.getProperty("test.classes"));
  76     static final String VERIFIER_DIR_NAME = "pack200-verifier";
  77     static final File   VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT);
  78     static final File   XCLASSES = new File("xclasses");
  79 
  80     private Utils() {} // all static
  81 
  82     static {
  83         if (!JavaHome.endsWith("jre")) {
  84             throw new RuntimeException("Error: requires an SDK to run");
  85         }
  86     }
  87 
  88     private static void init() throws IOException {
  89         if (VerifierJar.exists()) {
  90             return;
  91         }
  92         File srcDir = new File(TEST_SRC_DIR, VERIFIER_DIR_NAME);
  93         if (!srcDir.exists()) {
  94             // if not available try one level above
  95             srcDir = new File(TEST_SRC_DIR.getParentFile(), VERIFIER_DIR_NAME);
  96         }
  97         List<File> javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT));
  98         File tmpFile = File.createTempFile("javac", ".tmp");
  99         XCLASSES.mkdirs();
 100         FileOutputStream fos = null;
 101         PrintStream ps = null;
 102         try {
 103             fos = new FileOutputStream(tmpFile);
 104             ps = new PrintStream(fos);
 105             for (File f : javaFileList) {
 106                 ps.println(f.getAbsolutePath());
 107             }
 108         } finally {
 109             close(ps);
 110             close(fos);
 111         }
 112 
 113         compiler("-d",
 114                 XCLASSES.getName(),
 115                 "@" + tmpFile.getAbsolutePath());
 116 
 117         jar("cvfe",
 118             VerifierJar.getName(),
 119             "sun.tools.pack.verify.Main",
 120             "-C",
 121             XCLASSES.getName(),
 122             ".");
 123     }
 124 
 125     static void dirlist(File dir) {
 126         File[] files = dir.listFiles();
 127         System.out.println("--listing " + dir.getAbsolutePath() + "---");
 128         for (File f : files) {
 129             StringBuffer sb = new StringBuffer();
 130             sb.append(f.isDirectory() ? "d " : "- ");
 131             sb.append(f.getName());
 132             System.out.println(sb);
 133         }
 134     }
 135     static void doCompareVerify(File reference, File specimen) throws IOException {
 136         init();
 137         List<String> cmds = new ArrayList<String>();
 138         cmds.add(getJavaCmd());
 139         cmds.add("-cp");
 140         cmds.add(Utils.locateJar("tools.jar") +
 141                 System.getProperty("path.separator") + VerifierJar.getName());
 142         cmds.add("sun.tools.pack.verify.Main");
 143         cmds.add(reference.getAbsolutePath());
 144         cmds.add(specimen.getAbsolutePath());
 145         cmds.add("-O");
 146         runExec(cmds);
 147     }
 148 
 149     static void doCompareBitWise(File reference, File specimen)
 150             throws IOException {
 151         init();
 152         List<String> cmds = new ArrayList<String>();
 153         cmds.add(getJavaCmd());
 154         cmds.add("-cp");
 155         cmds.add(Utils.locateJar("tools.jar")
 156                 + System.getProperty("path.separator") + VerifierJar.getName());
 157         cmds.add("sun.tools.pack.verify.Main");
 158         cmds.add(reference.getName());
 159         cmds.add(specimen.getName());
 160         cmds.add("-O");
 161         cmds.add("-b");
 162         runExec(cmds);
 163     }
 164 
 165     static FileFilter createFilter(final String extension) {
 166         return new FileFilter() {
 167             @Override
 168             public boolean accept(File pathname) {
 169                 String name = pathname.getName();
 170                 if (name.endsWith(extension)) {
 171                     return true;
 172                 }
 173                 return false;
 174             }
 175         };
 176     }
 177 
 178     /*
 179      * clean up all the usual suspects
 180      */
 181     static void cleanup() throws IOException {
 182         recursiveDelete(XCLASSES);
 183         List<File> toDelete = new ArrayList<>();
 184         toDelete.addAll(Utils.findFiles(new File("."),
 185                 Utils.createFilter(".out")));
 186         toDelete.addAll(Utils.findFiles(new File("."),
 187                 Utils.createFilter(".bak")));
 188         toDelete.addAll(Utils.findFiles(new File("."),
 189                 Utils.createFilter(".jar")));
 190         toDelete.addAll(Utils.findFiles(new File("."),
 191                 Utils.createFilter(".pack")));
 192         toDelete.addAll(Utils.findFiles(new File("."),
 193                 Utils.createFilter(".bnd")));
 194         toDelete.addAll(Utils.findFiles(new File("."),
 195                 Utils.createFilter(".txt")));
 196         toDelete.addAll(Utils.findFiles(new File("."),
 197                 Utils.createFilter(".idx")));
 198         toDelete.addAll(Utils.findFiles(new File("."),
 199                 Utils.createFilter(".gidx")));
 200         for (File f : toDelete) {
 201             f.delete();
 202         }
 203     }
 204 
 205     static final FileFilter DIR_FILTER = new FileFilter() {
 206         public boolean accept(File pathname) {
 207             if (pathname.isDirectory()) {
 208                 return true;
 209             }
 210             return false;
 211         }
 212     };
 213 
 214     static final FileFilter FILE_FILTER = new FileFilter() {
 215         public boolean accept(File pathname) {
 216             if (pathname.isFile()) {
 217                 return true;
 218             }
 219             return false;
 220         }
 221     };
 222 
 223     static void copyFile(File src, File dst) throws IOException {
 224         Path parent = dst.toPath().getParent();
 225         if (parent != null) {
 226             Files.createDirectories(parent);
 227         }
 228         Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
 229         if (dst.isDirectory() && !dst.canWrite()) {
 230             dst.setWritable(true);
 231         }
 232     }
 233 
 234     static String baseName(File file, String extension) {
 235         return baseName(file.getAbsolutePath(), extension);
 236     }
 237 
 238     static String baseName(String name, String extension) {
 239         int cut = name.length() - extension.length();
 240         return name.lastIndexOf(extension) == cut
 241                 ? name.substring(0, cut)
 242                 : name;
 243 
 244     }
 245    static void createFile(File outFile, List<String> content) throws IOException {
 246         Files.write(outFile.getAbsoluteFile().toPath(), content,
 247                 Charset.defaultCharset(), CREATE_NEW, TRUNCATE_EXISTING);
 248     }
 249 
 250     /*
 251      * Suppose a path is provided which consists of a full path
 252      * this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z
 253      * and the base path is /foo/bar it will will return baz/foobar.z.
 254      */
 255     private static String getEntryPath(String basePath, String fullPath) {
 256         if (!fullPath.startsWith(basePath)) {
 257             return null;
 258         }
 259         return fullPath.substring(basePath.length());
 260     }
 261 
 262     static String getEntryPath(File basePathFile, File fullPathFile) {
 263         return getEntryPath(basePathFile.toString(), fullPathFile.toString());
 264     }
 265 
 266     public static void recursiveCopy(File src, File dest) throws IOException {
 267         if (!src.exists() || !src.canRead()) {
 268             throw new IOException("file not found or readable: " + src);
 269         }
 270         if (dest.exists() && !dest.isDirectory() && !dest.canWrite()) {
 271             throw new IOException("file not found or writeable: " + dest);
 272         }
 273         if (!dest.exists()) {
 274             dest.mkdirs();
 275         }
 276         List<File> a = directoryList(src);
 277         for (File f : a) {
 278             copyFile(f, new File(dest, getEntryPath(src, f)));
 279         }
 280     }
 281 
 282     static List<File> directoryList(File dirname) {
 283         List<File>  dirList = new ArrayList<File>();
 284         return directoryList(dirname, dirList, null);
 285     }
 286 
 287     private static List<File> directoryList(File dirname, List<File> dirList,
 288             File[] dirs) {
 289         dirList.addAll(Arrays.asList(dirname.listFiles(FILE_FILTER)));
 290         dirs = dirname.listFiles(DIR_FILTER);
 291         for (File f : dirs) {
 292             if (f.isDirectory() && !f.equals(dirname)) {
 293                 dirList.add(f);
 294                 directoryList(f, dirList, dirs);
 295             }
 296         }
 297         return dirList;
 298     }
 299 
 300     static void recursiveDelete(File dir) throws IOException {
 301         if (dir.isFile()) {
 302             dir.delete();
 303         } else if (dir.isDirectory()) {
 304             File[] entries = dir.listFiles();
 305             for (int i = 0; i < entries.length; i++) {
 306                 if (entries[i].isDirectory()) {
 307                     recursiveDelete(entries[i]);
 308                 }
 309                 entries[i].delete();
 310             }
 311             dir.delete();
 312         }
 313     }
 314 
 315     static List<File> findFiles(File startDir, FileFilter filter)
 316             throws IOException {
 317         List<File> list = new ArrayList<File>();
 318         findFiles0(startDir, list, filter);
 319         return list;
 320     }
 321     /*
 322      * finds files in the start directory using the the filter, appends
 323      * the files to the dirList.
 324      */
 325     private static void findFiles0(File startDir, List<File> list,
 326                                     FileFilter filter) throws IOException {
 327         File[] foundFiles = startDir.listFiles(filter);
 328         list.addAll(Arrays.asList(foundFiles));
 329         File[] dirs = startDir.listFiles(DIR_FILTER);
 330         for (File dir : dirs) {
 331             findFiles0(dir, list, filter);
 332         }
 333     }
 334 
 335     static void close(Closeable c) {
 336         if (c == null) {
 337             return;
 338         }
 339         try {
 340             c.close();
 341         } catch (IOException ignore) {
 342         }
 343     }
 344 
 345     static void compiler(String... javacCmds) {
 346         if (com.sun.tools.javac.Main.compile(javacCmds) != 0) {
 347             throw new RuntimeException("compilation failed");
 348         }
 349     }
 350 
 351     static void jar(String... jargs) {
 352         sun.tools.jar.Main jarTool =
 353                 new sun.tools.jar.Main(System.out, System.err, "jartool");
 354         if (!jarTool.run(jargs)) {
 355             throw new RuntimeException("jar command failed");
 356         }
 357     }
 358 
 359     static void testWithRepack(File inFile, String... repackOpts) throws IOException {
 360         File cwd = new File(".");
 361         // pack using --repack in native mode
 362         File nativejarFile = new File(cwd, "out-n" + Utils.JAR_FILE_EXT);
 363         repack(inFile, nativejarFile, false, repackOpts);
 364         doCompareVerify(inFile, nativejarFile);
 365 
 366         // ensure bit compatibility between the unpacker variants
 367         File javajarFile = new File(cwd, "out-j" + Utils.JAR_FILE_EXT);
 368         repack(inFile, javajarFile, true, repackOpts);
 369         doCompareBitWise(javajarFile, nativejarFile);
 370     }
 371 
 372     static List<String> repack(File inFile, File outFile,
 373             boolean disableNative, String... extraOpts) {
 374         List<String> cmdList = new ArrayList<>();
 375         cmdList.clear();
 376         cmdList.add(Utils.getJavaCmd());
 377         cmdList.add("-ea");
 378         cmdList.add("-esa");
 379         if (disableNative) {
 380             cmdList.add("-Dcom.sun.java.util.jar.pack.disable.native=true");
 381         }
 382         cmdList.add("com.sun.java.util.jar.pack.Driver");
 383         cmdList.add("--repack");
 384         if (extraOpts != null) {
 385            for (String opt: extraOpts) {
 386                cmdList.add(opt);
 387            }
 388         }
 389         cmdList.add(outFile.getName());
 390         cmdList.add(inFile.getName());
 391         return Utils.runExec(cmdList);
 392     }
 393 
 394     // given a jar file foo.jar will write to foo.pack
 395     static void pack(JarFile jarFile, File packFile) throws IOException {
 396         Pack200.Packer packer = Pack200.newPacker();
 397         Map<String, String> p = packer.properties();
 398         // Take the time optimization vs. space
 399         p.put(packer.EFFORT, "1");  // CAUTION: do not use 0.
 400         // Make the memory consumption as effective as possible
 401         p.put(packer.SEGMENT_LIMIT, "10000");
 402         // ignore all JAR deflation requests to save time
 403         p.put(packer.DEFLATE_HINT, packer.FALSE);
 404         // save the file ordering of the original JAR
 405         p.put(packer.KEEP_FILE_ORDER, packer.TRUE);
 406         FileOutputStream fos = null;
 407         try {
 408             // Write out to a jtreg scratch area
 409             fos = new FileOutputStream(packFile);
 410             // Call the packer
 411             packer.pack(jarFile, fos);
 412         } finally {
 413             close(fos);
 414         }
 415     }
 416 
 417     // uses java unpacker, slow but useful to discover issues with the packer
 418     static void unpackj(File inFile, JarOutputStream jarStream)
 419             throws IOException {
 420         unpack0(inFile, jarStream, true);
 421 
 422     }
 423 
 424     // uses native unpacker using the java APIs
 425     static void unpackn(File inFile, JarOutputStream jarStream)
 426             throws IOException {
 427         unpack0(inFile, jarStream, false);
 428     }
 429 
 430     // given a packed file, create the jar file in the current directory.
 431     private static void unpack0(File inFile, JarOutputStream jarStream,
 432             boolean useJavaUnpack) throws IOException {
 433         // Unpack the files
 434         Pack200.Unpacker unpacker = Pack200.newUnpacker();
 435         Map<String, String> props = unpacker.properties();
 436         if (useJavaUnpack) {
 437             props.put("com.sun.java.util.jar.pack.disable.native", "true");
 438         }
 439         // Call the unpacker
 440         unpacker.unpack(inFile, jarStream);
 441     }
 442 
 443     static byte[] getBuffer(ZipFile zf, ZipEntry ze) throws IOException {
 444         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 445         byte buf[] = new byte[8192];
 446         InputStream is = null;
 447         try {
 448             is = zf.getInputStream(ze);
 449             int n = is.read(buf);
 450             while (n > 0) {
 451                 baos.write(buf, 0, n);
 452                 n = is.read(buf);
 453             }
 454             return baos.toByteArray();
 455         } finally {
 456             close(is);
 457         }
 458     }
 459 
 460     static ArrayList<String> getZipFileEntryNames(ZipFile z) {
 461         ArrayList<String> out = new ArrayList<String>();
 462         for (ZipEntry ze : Collections.list(z.entries())) {
 463             out.add(ze.getName());
 464         }
 465         return out;
 466     }
 467     static List<String> runExec(List<String> cmdsList) {
 468         return runExec(cmdsList, null);
 469     }
 470     static List<String> runExec(List<String> cmdsList, Map<String, String> penv) {
 471         ArrayList<String> alist = new ArrayList<String>();
 472         ProcessBuilder pb =
 473                 new ProcessBuilder(cmdsList);
 474         Map<String, String> env = pb.environment();
 475         if (penv != null && !penv.isEmpty()) {
 476             env.putAll(penv);
 477         }
 478         pb.directory(new File("."));
 479         dirlist(new File("."));
 480         for (String x : cmdsList) {
 481             System.out.print(x + " ");
 482         }
 483         System.out.println("");
 484         int retval = 0;
 485         Process p = null;
 486         InputStreamReader ir = null;
 487         BufferedReader rd = null;
 488         InputStream is = null;
 489         try {
 490             pb.redirectErrorStream(true);
 491             p = pb.start();
 492             is = p.getInputStream();
 493             ir = new InputStreamReader(is);
 494             rd = new BufferedReader(ir, 8192);
 495 
 496             String in = rd.readLine();
 497             while (in != null) {
 498                 alist.add(in);
 499                 System.out.println(in);
 500                 in = rd.readLine();
 501             }
 502             retval = p.waitFor();
 503             if (retval != 0) {
 504                 throw new RuntimeException("process failed with non-zero exit");
 505             }
 506         } catch (Exception ex) {
 507             throw new RuntimeException(ex.getMessage());
 508         } finally {
 509             close(rd);
 510             close(ir);
 511             close(is);
 512             if (p != null) {
 513                 p.destroy();
 514             }
 515         }
 516         return alist;
 517     }
 518 
 519     static String getUnpack200Cmd() {
 520         return getAjavaCmd("unpack200");
 521     }
 522 
 523     static String getPack200Cmd() {
 524         return getAjavaCmd("pack200");
 525     }
 526 
 527     static String getJavaCmd() {
 528         return getAjavaCmd("java");
 529     }
 530 
 531     static String getAjavaCmd(String cmdStr) {
 532         File binDir = new File(JavaHome, "bin");
 533         File unpack200File = IsWindows
 534                 ? new File(binDir, cmdStr + ".exe")
 535                 : new File(binDir, cmdStr);
 536 
 537         String cmd = unpack200File.getAbsolutePath();
 538         if (!unpack200File.canExecute()) {
 539             throw new RuntimeException("please check" +
 540                     cmd + " exists and is executable");
 541         }
 542         return cmd;
 543     }
 544 
 545     private static List<File> locaterCache = null;
 546     // search the source dir and jdk dir for requested file and returns
 547     // the first location it finds.
 548     static File locateJar(String name) {
 549         try {
 550             if (locaterCache == null) {
 551                 locaterCache = new ArrayList<File>();
 552                 locaterCache.addAll(findFiles(TEST_SRC_DIR, createFilter(JAR_FILE_EXT)));
 553                 locaterCache.addAll(findFiles(JavaSDK, createFilter(JAR_FILE_EXT)));
 554             }
 555             for (File f : locaterCache) {
 556                 if (f.getName().equals(name)) {
 557                     return f;
 558                 }
 559             }
 560             throw new IOException("file not found: " + name);
 561         } catch (IOException e) {
 562             throw new RuntimeException(e);
 563         }
 564     }
 565 }