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