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