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 8003255
  27  * @compile -XDignore.symbol.file Basic.java Main.java Logging.java
  28  * @run main Basic
  29  * @summary Test the launcher checks the Profile attribute of executable JAR
  30  *     files. Also checks that libraries that specify the Profile attribute
  31  *     are not loaded if the runtime does not support the required profile.
  32  */
  33 
  34 import java.io.*;
  35 import java.util.jar.*;
  36 import static java.util.jar.JarFile.MANIFEST_NAME;
  37 import java.util.zip.*;
  38 import java.nio.file.*;
  39 import java.nio.file.attribute.BasicFileAttributes;
  40 
  41 public class Basic {
  42 
  43     static final String MANIFEST_DIR = "META-INF/";
  44 
  45     static final String JAVA_HOME = System.getProperty("java.home");
  46     static final String OS_NAME = System.getProperty("os.name");
  47     static final String OS_ARCH = System.getProperty("os.arch");
  48 
  49     static final String JAVA_CMD =
  50             OS_NAME.startsWith("Windows") ? "java.exe" : "java";
  51 
  52     static final boolean NEED_D64 =
  53             OS_NAME.equals("SunOS") &&
  54             (OS_ARCH.equals("sparcv9") || OS_ARCH.equals("amd64"));
  55 
  56     /**
  57      * Creates a JAR file with the given attributes and the given entries.
  58      * Class files are assumed to be in ${test.classes}. Note that this this
  59      * method cannot use the "jar" tool as it may not be present in the image.
  60      */
  61     static void createJarFile(String jarfile,
  62                               String mainAttributes,
  63                               String... entries)
  64         throws IOException
  65     {
  66         // create Manifest
  67         Manifest manifest = new Manifest();
  68         Attributes jarAttrs = manifest.getMainAttributes();
  69         jarAttrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
  70         if (mainAttributes.length() > 0) {
  71             for (String attr: mainAttributes.split(",")) {
  72                 String[] s = attr.split("=");
  73                 jarAttrs.put(new Attributes.Name(s[0]), s[1]);
  74             }
  75         }
  76 
  77         try (OutputStream out = Files.newOutputStream(Paths.get(jarfile));
  78              ZipOutputStream zos = new JarOutputStream(out))
  79         {
  80             // add manifest directory and manifest file
  81             ZipEntry e = new JarEntry(MANIFEST_DIR);
  82             e.setTime(System.currentTimeMillis());
  83             e.setSize(0);
  84             e.setCrc(0);
  85             zos.putNextEntry(e);
  86             e = new ZipEntry(MANIFEST_NAME);
  87             e.setTime(System.currentTimeMillis());
  88             zos.putNextEntry(e);
  89             manifest.write(zos);
  90             zos.closeEntry();
  91 
  92             // entries in JAR file
  93             for (String entry: entries) {
  94                 e = new JarEntry(entry);
  95                 Path path;
  96                 if (entry.endsWith(".class")) {
  97                     path = Paths.get(System.getProperty("test.classes"), entry);
  98                 } else {
  99                     path = Paths.get(entry);
 100                 }
 101                 BasicFileAttributes attrs =
 102                     Files.readAttributes(path, BasicFileAttributes.class);
 103                 e.setTime(attrs.lastModifiedTime().toMillis());
 104                 if (attrs.size() == 0) {
 105                     e.setMethod(ZipEntry.STORED);
 106                     e.setSize(0);
 107                     e.setCrc(0);
 108                 }
 109                 zos.putNextEntry(e);
 110                 if (attrs.isRegularFile())
 111                     Files.copy(path, zos);
 112                 zos.closeEntry();
 113             }
 114         }
 115     }
 116 
 117     /**
 118      * Execute the given executable JAR file with the given arguments. This
 119      * method blocks until the launched VM terminates. Any output or error
 120      * message from the launched VM are printed to System.out. Returns the
 121      * exit value.
 122      */
 123     static int exec(String jf, String... args) throws IOException {
 124         StringBuilder sb = new StringBuilder();
 125         sb.append(Paths.get(JAVA_HOME, "bin", JAVA_CMD).toString());
 126         if (NEED_D64)
 127             sb.append(" -d64");
 128         sb.append(" -jar ");
 129         sb.append(Paths.get(jf).toAbsolutePath());
 130         for (String arg: args) {
 131             sb.append(' ');
 132             sb.append(arg);
 133         }
 134         String[] cmd = sb.toString().split(" ");
 135         ProcessBuilder pb = new ProcessBuilder(cmd);
 136         pb.redirectErrorStream(true);
 137         Process p = pb.start();
 138         BufferedReader reader =
 139             new BufferedReader(new InputStreamReader(p.getInputStream()));
 140         String line;
 141         while ((line = reader.readLine()) != null) {
 142             System.out.println(line);
 143         }
 144         try {
 145             return p.waitFor();
 146         } catch (InterruptedException e) {
 147             throw new RuntimeException("Should not happen");
 148         }
 149     }
 150 
 151     static void checkRun(String jf, String... args) throws IOException {
 152         if (exec(jf) != 0)
 153             throw new RuntimeException(jf + " failed!!!");
 154     }
 155 
 156     static void checkRunFail(String jf, String... args) throws IOException {
 157         if (exec(jf) == 0)
 158             throw new RuntimeException(jf + " did not fail!!!");
 159         System.out.println("Failed as expected");
 160     }
 161 
 162     public static void main(String[] args) throws IOException {
 163         // ## replace this if there is a standard way to determine the profile
 164         String profile = sun.misc.Version.profileName();
 165 
 166         int thisProfile = 4;
 167         if ("compact1".equals(profile)) thisProfile = 1;
 168         if ("compact2".equals(profile)) thisProfile = 2;
 169         if ("compact3".equals(profile)) thisProfile = 3;
 170 
 171         // "library" JAR file used by the test
 172         createJarFile("Logging.jar", "", "Logging.class");
 173 
 174         // Executable JAR file without the Profile attribute
 175         if (thisProfile <= 3) {
 176             createJarFile("Main.jar",
 177                           "Main-Class=Main,Class-Path=Logging.jar",
 178                           "Main.class");
 179             checkRunFail("Main.jar");
 180         }
 181 
 182         // Executable JAR file with Profile attribute, Library JAR file without
 183         for (int p=1; p<=3; p++) {
 184             String attrs = "Main-Class=Main,Class-Path=Logging.jar" +
 185                  ",Profile=compact" + p;
 186             createJarFile("Main.jar", attrs,  "Main.class");
 187             if (p <= thisProfile) {
 188                 checkRun("Main.jar");
 189             } else {
 190                 checkRunFail("Main.jar");
 191             }
 192         }
 193 
 194         // Executable JAR file with Profile attribute and unrecognized profile
 195         createJarFile("Main.jar",
 196                       "Main-Class=Main,Class-Path=Logging.jar,Profile=BadName",
 197                       "Main.class");
 198         checkRunFail("Main.jar");
 199 
 200         // Executable JAR file and Librrary JAR file with Profile attribute
 201         createJarFile("Main.jar",
 202                       "Main-Class=Main,Class-Path=Logging.jar,Profile=compact1",
 203                       "Main.class");
 204         for (int p=1; p<=3; p++) {
 205             String attrs = "Profile=compact" + p;
 206             createJarFile("Logging.jar", attrs, "Logging.class");
 207             if (p <= thisProfile) {
 208                 checkRun("Main.jar");
 209             } else {
 210                 checkRunFail("Main.jar");
 211             }
 212         }
 213 
 214         // Executable JAR file and Library JAR with Profile attribute, value
 215         // of Profile not recognized
 216         createJarFile("Logging.jar", "Profile=BadName", "Logging.class");
 217         createJarFile("Main.jar",
 218                       "Main-Class=Main,Class-Path=Logging.jar,Profile=compact1",
 219                       "Main.class");
 220         checkRunFail("Main.jar");
 221 
 222         System.out.println("TEST PASSED.");
 223     }
 224 
 225 }