1 /* 2 * Copyright (c) 2015, 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 package tests; 24 25 import java.io.ByteArrayInputStream; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Properties; 35 36 import com.sun.tools.classfile.ClassFile; 37 import com.sun.tools.classfile.ConstantPoolException; 38 import jdk.internal.jimage.BasicImageReader; 39 import jdk.internal.jimage.ImageLocation; 40 41 /** 42 * 43 * JDK Modular image validator 44 */ 45 public class JImageValidator { 46 47 private static final String[] dirs = {"bin", "lib"}; 48 49 private final File rootDir; 50 private final List<String> expectedLocations; 51 private final String module; 52 private long moduleExecutionTime; 53 private long javaExecutionTime; 54 private final List<String> unexpectedPaths; 55 private final List<String> unexpectedFiles; 56 private final String[] expectedFiles; 57 JImageValidator(String module, List<String> expectedLocations, File rootDir, List<String> unexpectedPaths, List<String> unexpectedFiles)58 public JImageValidator(String module, List<String> expectedLocations, 59 File rootDir, 60 List<String> unexpectedPaths, 61 List<String> unexpectedFiles) throws Exception { 62 this(module, expectedLocations, rootDir, unexpectedPaths, unexpectedFiles, null); 63 } 64 JImageValidator(String module, List<String> expectedLocations, File rootDir, List<String> unexpectedPaths, List<String> unexpectedFiles, String[] expectedFiles)65 public JImageValidator(String module, List<String> expectedLocations, 66 File rootDir, 67 List<String> unexpectedPaths, 68 List<String> unexpectedFiles, 69 String[] expectedFiles) throws IOException { 70 if (!rootDir.exists()) { 71 throw new IOException("Image root dir not found " + 72 rootDir.getAbsolutePath()); 73 } 74 this.expectedLocations = expectedLocations; 75 this.rootDir = rootDir; 76 this.module = module; 77 this.unexpectedPaths = unexpectedPaths; 78 this.unexpectedFiles = unexpectedFiles; 79 this.expectedFiles = expectedFiles == null ? new String[0] : expectedFiles; 80 } 81 validate()82 public void validate() throws IOException { 83 for (String d : dirs) { 84 File dir = new File(rootDir, d); 85 if (!dir.isDirectory()) { 86 throw new IOException("Invalid directory " + d); 87 } 88 } 89 90 //check jimage file 91 Path path = rootDir.toPath().resolve("lib").resolve("modules"); 92 if (!Files.isRegularFile(path)) { 93 throw new IOException(path + " No jimage file generated"); 94 } 95 96 // Check binary file 97 File launcher = new File(rootDir, "bin" + File.separator + module); 98 if (launcher.exists()) { 99 ProcessBuilder builder = new ProcessBuilder("sh", launcher.getAbsolutePath()); 100 long t = System.currentTimeMillis(); 101 Process process = builder.inheritIO().start(); 102 int ret = waitFor(process); 103 moduleExecutionTime += System.currentTimeMillis() - t; 104 if (ret != 0) { 105 throw new IOException("Image " + module + " execution failed, check logs."); 106 } 107 } 108 109 for (String f : expectedFiles) { 110 File dd = new File(rootDir, f); 111 if (!dd.exists()) { 112 throw new IOException("Expected File " + f + " not found"); 113 } 114 } 115 116 //Walk and check that unexpected files are not there 117 try (java.util.stream.Stream<Path> stream = Files.walk(rootDir.toPath())) { 118 stream.forEach((p) -> { 119 for (String u : unexpectedFiles) { 120 if (p.toString().equals(u)) { 121 throw new RuntimeException("Seen unexpected path " + p); 122 } 123 } 124 }); 125 } 126 127 File javaLauncher = new File(rootDir, "bin" + File.separator + 128 (isWindows() ? "java.exe" : "java")); 129 if (javaLauncher.exists()) { 130 ProcessBuilder builder = new ProcessBuilder(javaLauncher.getAbsolutePath(), 131 "-version"); 132 long t = System.currentTimeMillis(); 133 Process process = builder.start(); 134 int ret = waitFor(process); 135 javaExecutionTime += System.currentTimeMillis() - t; 136 if (ret != 0) { 137 throw new RuntimeException("java launcher execution failed, check logs."); 138 } 139 } else { 140 throw new IOException("java launcher not found."); 141 } 142 143 // Check release file 144 File release = new File(rootDir, "release"); 145 if (!release.exists()) { 146 throw new IOException("Release file not generated"); 147 } else { 148 Properties props = new Properties(); 149 try (FileInputStream fs = new FileInputStream(release)) { 150 props.load(fs); 151 String s = props.getProperty("MODULES"); 152 if (s == null) { 153 throw new IOException("No MODULES property in release"); 154 } 155 if (!s.contains(module)) { 156 throw new IOException("Module not found in release file " + s); 157 } 158 } 159 } 160 161 } 162 waitFor(Process process)163 private int waitFor(Process process) { 164 try { 165 return process.waitFor(); 166 } catch (InterruptedException e) { 167 throw new RuntimeException(e); 168 } 169 } 170 isWindows()171 private static boolean isWindows() { 172 return System.getProperty("os.name").startsWith("Windows"); 173 } 174 validate(Path jimage, List<String> expectedLocations, List<String> unexpectedPaths)175 public static void validate(Path jimage, List<String> expectedLocations, 176 List<String> unexpectedPaths) throws IOException { 177 BasicImageReader reader = BasicImageReader.open(jimage); 178 // Validate expected locations 179 List<String> seenLocations = new ArrayList<>(); 180 for (String loc : expectedLocations) { 181 ImageLocation il = reader.findLocation(loc); 182 if (il == null) { 183 throw new IOException("Location " + loc + " not present in " + jimage); 184 } 185 } 186 seenLocations.addAll(expectedLocations); 187 188 for (String s : reader.getEntryNames()) { 189 if (s.endsWith(".class") && !s.endsWith("module-info.class")) { 190 ImageLocation il = reader.findLocation(s); 191 try { 192 byte[] r = reader.getResource(il); 193 if(r == null) { 194 System.out.println("IL, compressed " + 195 il.getCompressedSize() + " uncompressed " + 196 il.getUncompressedSize()); 197 throw new IOException("NULL RESOURCE " + s); 198 } 199 readClass(r); 200 } catch (IOException ex) { 201 System.err.println(s + " ERROR " + ex); 202 throw ex; 203 } 204 } 205 if (seenLocations.contains(s)) { 206 seenLocations.remove(s); 207 } 208 for(String p : unexpectedPaths) { 209 if (s.equals(p)) { 210 throw new IOException("Seen unexpected path " + s); 211 } 212 } 213 } 214 if (!seenLocations.isEmpty()) { 215 throw new IOException("ImageReader did not return " + seenLocations); 216 } 217 } 218 getJavaLauncherExecutionTime()219 public long getJavaLauncherExecutionTime() { 220 return javaExecutionTime; 221 } 222 getModuleLauncherExecutionTime()223 public long getModuleLauncherExecutionTime() { 224 return moduleExecutionTime; 225 } 226 readClass(byte[] clazz)227 public static void readClass(byte[] clazz) throws IOException { 228 try (InputStream stream = new ByteArrayInputStream(clazz)) { 229 ClassFile.read(stream); 230 } catch (ConstantPoolException e) { 231 throw new IOException(e); 232 } 233 } 234 } 235