1 /** 2 * Copyright (c) 2015, 2016, 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 * @summary Test the recording and checking of module hashes 27 * @author Andrei Eremeev 28 * @library /lib/testlibrary 29 * @modules java.base/jdk.internal.misc 30 * java.base/jdk.internal.module 31 * jdk.jlink 32 * jdk.compiler 33 * @build CompilerUtils 34 * @run testng HashesTest 35 */ 36 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.lang.module.ModuleDescriptor; 40 import java.lang.module.ModuleFinder; 41 import java.lang.module.ModuleReader; 42 import java.lang.module.ModuleReference; 43 import java.nio.file.FileVisitResult; 44 import java.nio.file.Files; 45 import java.nio.file.Path; 46 import java.nio.file.Paths; 47 import java.nio.file.SimpleFileVisitor; 48 import java.nio.file.attribute.BasicFileAttributes; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Collections; 52 import java.util.List; 53 import java.util.Optional; 54 import java.util.Set; 55 import java.util.spi.ToolProvider; 56 import java.util.stream.Collectors; 57 58 import jdk.internal.misc.SharedSecrets; 59 import jdk.internal.misc.JavaLangModuleAccess; 60 import jdk.internal.module.ModuleHashes; 61 62 import org.testng.annotations.BeforeTest; 63 import org.testng.annotations.Test; 64 65 import static org.testng.Assert.*; 66 67 public class HashesTest { 68 static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod") 69 .orElseThrow(() -> 70 new RuntimeException("jmod tool not found") 71 ); 72 73 private final Path testSrc = Paths.get(System.getProperty("test.src")); 74 private final Path modSrc = testSrc.resolve("src"); 75 private final Path mods = Paths.get("mods"); 76 private final Path jmods = Paths.get("jmods"); 77 private final String[] modules = new String[] { "m1", "m2", "m3"}; 78 79 @BeforeTest 80 private void setup() throws Exception { 81 if (Files.exists(jmods)) { 82 deleteDirectory(jmods); 83 } 84 Files.createDirectories(jmods); 85 86 // build m2, m3 required by m1 87 compileModule("m2", modSrc); 88 jmod("m2"); 89 90 compileModule("m3", modSrc); 91 jmod("m3"); 92 93 // build m1 94 compileModule("m1", modSrc); 95 // no hash is recorded since m1 has outgoing edges 96 jmod("m1", "--module-path", jmods.toString(), "--hash-modules", ".*"); 97 98 // compile org.bar and org.foo 99 compileModule("org.bar", modSrc); 100 compileModule("org.foo", modSrc); 101 } 102 103 @Test 104 public void test() throws Exception { 105 for (String mn : modules) { 106 assertFalse(hashes(mn).isPresent()); 107 } 108 109 // hash m1 in m2 110 jmod("m2", "--module-path", jmods.toString(), "--hash-modules", "m1"); 111 checkHashes(hashes("m2").get(), "m1"); 112 113 // hash m1 in m2 114 jmod("m2", "--module-path", jmods.toString(), "--hash-modules", ".*"); 115 checkHashes(hashes("m2").get(), "m1"); 116 117 // create m2.jmod with no hash 118 jmod("m2"); 119 // run jmod hash command to hash m1 in m2 and m3 120 runJmod(Arrays.asList("hash", "--module-path", jmods.toString(), 121 "--hash-modules", ".*")); 122 checkHashes(hashes("m2").get(), "m1"); 123 checkHashes(hashes("m3").get(), "m1"); 124 125 jmod("org.bar"); 126 jmod("org.foo"); 127 128 jmod("org.bar", "--module-path", jmods.toString(), "--hash-modules", "org.*"); 129 checkHashes(hashes("org.bar").get(), "org.foo"); 130 131 jmod("m3", "--module-path", jmods.toString(), "--hash-modules", ".*"); 132 checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1"); 133 } 134 135 private void checkHashes(ModuleHashes hashes, String... hashModules) { 136 assertTrue(hashes.names().equals(Set.of(hashModules))); 137 } 138 139 private Optional<ModuleHashes> hashes(String name) throws Exception { 140 ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess() 141 .newModulePath(Runtime.version(), true, jmods.resolve(name + ".jmod")); 142 ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new); 143 ModuleReader reader = mref.open(); 144 try (InputStream in = reader.open("module-info.class").get()) { 145 ModuleDescriptor md = ModuleDescriptor.read(in); 146 JavaLangModuleAccess jmla = SharedSecrets.getJavaLangModuleAccess(); 147 Optional<ModuleHashes> hashes = jmla.hashes(md); 148 System.out.format("hashes in module %s %s%n", name, 149 hashes.isPresent() ? "present" : "absent"); 150 if (hashes.isPresent()) { 151 hashes.get().names().stream() 152 .sorted() 153 .forEach(n -> System.out.format(" %s %s%n", n, hashes.get().hashFor(n))); 154 } 155 return hashes; 156 } finally { 157 reader.close(); 158 } 159 } 160 161 private void deleteDirectory(Path dir) throws IOException { 162 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 163 @Override 164 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 165 throws IOException 166 { 167 Files.delete(file); 168 return FileVisitResult.CONTINUE; 169 } 170 171 @Override 172 public FileVisitResult postVisitDirectory(Path dir, IOException exc) 173 throws IOException 174 { 175 Files.delete(dir); 176 return FileVisitResult.CONTINUE; 177 } 178 }); 179 } 180 181 private void compileModule(String moduleName, Path src) throws IOException { 182 Path msrc = src.resolve(moduleName); 183 assertTrue(CompilerUtils.compile(msrc, mods, "--module-source-path", src.toString())); 184 } 185 186 private void jmod(String moduleName, String... options) throws IOException { 187 Path mclasses = mods.resolve(moduleName); 188 Path outfile = jmods.resolve(moduleName + ".jmod"); 189 List<String> args = new ArrayList<>(); 190 args.add("create"); 191 Collections.addAll(args, options); 192 Collections.addAll(args, "--class-path", mclasses.toString(), 193 outfile.toString()); 194 195 if (Files.exists(outfile)) 196 Files.delete(outfile); 197 198 runJmod(args); 199 } 200 201 private void runJmod(List<String> args) { 202 int rc = JMOD_TOOL.run(System.out, System.out, args.toArray(new String[args.size()])); 203 System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" "))); 204 if (rc != 0) { 205 throw new AssertionError("Jmod failed: rc = " + rc); 206 } 207 } 208 }