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