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