1 /* 2 * Copyright (c) 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 * @bug 8156499 27 * @summary Test image creation from Multi-Release JAR 28 * @author Steve Drach 29 * @library /lib/testlibrary 30 * @modules java.base/jdk.internal.jimage 31 * jdk.jartool/sun.tools.jar 32 * jdk.jlink/jdk.tools.jlink.internal 33 * @build CompilerUtils 34 * @run testng JLinkMultiReleaseJarTest 35 */ 36 37 import java.io.IOException; 38 import java.io.PrintWriter; 39 import java.lang.invoke.MethodHandle; 40 import java.lang.invoke.MethodHandles; 41 import java.lang.invoke.MethodType; 42 import java.nio.file.FileVisitResult; 43 import java.nio.file.Files; 44 import java.nio.file.Path; 45 import java.nio.file.Paths; 46 import java.nio.file.SimpleFileVisitor; 47 import java.nio.file.attribute.BasicFileAttributes; 48 import java.util.Arrays; 49 import java.util.Set; 50 import java.util.jar.JarFile; 51 import java.util.stream.Collectors; 52 import java.util.stream.Stream; 53 54 import jdk.internal.jimage.BasicImageReader; 55 56 import org.testng.Assert; 57 import org.testng.annotations.AfterClass; 58 import org.testng.annotations.BeforeClass; 59 import org.testng.annotations.Test; 60 61 public class JLinkMultiReleaseJarTest { 62 private Path userdir; 63 private Path javahome; 64 65 @BeforeClass 66 public void initialize() throws IOException { 67 Path srcdir = Paths.get(System.getProperty("test.src")); 68 userdir = Paths.get(System.getProperty("user.dir", ".")); 69 javahome = Paths.get(System.getProperty("java.home")); 70 71 // create class files from source 72 Path base = srcdir.resolve("base"); 73 Path basemods = userdir.resolve("basemods"); 74 CompilerUtils.compile(base, basemods, "-modulesourcepath", base.toString()); 75 76 Path rt = srcdir.resolve("rt"); 77 Path rtmods = userdir.resolve("rtmods"); 78 CompilerUtils.compile(rt, rtmods, "-modulesourcepath", rt.toString()); 79 80 // copy resource into basemods 81 Path resource = base.resolve("m1").resolve("resources.txt"); 82 Path dest = basemods.resolve("m1").resolve("resources.txt"); 83 Files.copy(resource, dest); 84 85 // build multi-release jar file 86 sun.tools.jar.Main jartool = new sun.tools.jar.Main(System.out, System.err, "jar"); 87 String args = "-cf m1.jar -C basemods/m1 . --release " 88 + JarFile.runtimeVersion().major() + " -C rtmods/m1 ."; 89 jartool.run(args.split(" +")); 90 } 91 92 //@AfterClass 93 public void close() throws IOException { 94 Files.walkFileTree(userdir, new SimpleFileVisitor<>() { 95 @Override 96 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 97 throws IOException 98 { 99 Files.delete(file); 100 return FileVisitResult.CONTINUE; 101 } 102 @Override 103 public FileVisitResult postVisitDirectory(Path dir, IOException e) 104 throws IOException 105 { 106 if (dir.equals(userdir)) { 107 return FileVisitResult.CONTINUE; 108 } 109 if (e == null) { 110 Files.delete(dir); 111 return FileVisitResult.CONTINUE; 112 } else { 113 // directory iteration failed 114 throw e; 115 } 116 } 117 }); 118 } 119 120 @Test 121 public void test() throws Throwable { 122 // check that there are only packaged modules (.jmod files) for modulepath 123 Path jmodsdir = javahome.resolve("jmods"); 124 try (Stream<Path> jmods = Files.walk(jmodsdir, 1)) { 125 if (!jmods 126 .filter(path -> !path.equals(jmodsdir)) 127 .filter(path -> !path.toString().endsWith(".jmod")) 128 .collect(Collectors.toSet()) 129 .isEmpty() 130 ) { 131 throw new Exception("Exploded modules found"); 132 } 133 } 134 135 // use jlink to build image from multi-release jar 136 String args = "--output myimage --add-modules m1 --module-path m1.jar:" 137 + jmodsdir.toString(); 138 int exitCode = jdk.tools.jlink.internal.Main.run(args.split(" +"), 139 new PrintWriter(System.out)); 140 Assert.assertEquals(exitCode, 0); 141 142 // validate image 143 Path jimage = userdir.resolve("myimage").resolve("lib").resolve("modules"); 144 BasicImageReader reader = BasicImageReader.open(jimage); 145 146 // do we have the right entry names? 147 Set<String> names = Arrays.stream(reader.getEntryNames()) 148 .filter(n -> n.startsWith("/m1")) 149 .collect(Collectors.toSet()); 150 Assert.assertEquals(names, Set.of( 151 "/m1/module-info.class", 152 "/m1/p/Main.class", 153 "/m1/p/Type.class", 154 "/m1/resources.txt")); 155 156 // do we have the right code? 157 byte[] b = reader.getResource("/m1/p/Main.class"); 158 Class<?> clazz = (new ByteArrayClassLoader()).loadClass("p.Main", b); 159 MethodHandle getVersion = MethodHandles.lookup() 160 .findVirtual(clazz, "getVersion", MethodType.methodType(int.class)); 161 int version = (int)getVersion.invoke(clazz.getConstructor().newInstance()); 162 Assert.assertEquals(version, JarFile.runtimeVersion().major()); 163 } 164 165 private static class ByteArrayClassLoader extends ClassLoader { 166 public Class<?> loadClass(String name, byte[] bytes) { 167 return defineClass(name, bytes, 0, bytes.length); 168 } 169 } 170 }