1 /* 2 * Copyright (c) 2016, 2017, 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 Multi-Release jar usage in runtime 27 * @library /test/lib 28 * @modules jdk.compiler 29 * @build jdk.test.lib.compiler.CompilerUtils 30 * jdk.test.lib.Utils 31 * jdk.test.lib.Asserts 32 * jdk.test.lib.JDKToolFinder 33 * jdk.test.lib.JDKToolLauncher 34 * jdk.test.lib.Platform 35 * jdk.test.lib.process.* 36 * @run testng RuntimeTest 37 */ 38 39 import static org.testng.Assert.*; 40 41 import java.io.BufferedReader; 42 import java.io.File; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.io.InputStreamReader; 46 import java.io.UncheckedIOException; 47 import java.lang.reflect.InvocationTargetException; 48 import java.lang.reflect.Method; 49 import java.net.URL; 50 import java.net.URLClassLoader; 51 import java.nio.file.Files; 52 import java.nio.file.Path; 53 import java.nio.file.Paths; 54 import java.nio.file.StandardCopyOption; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.HashMap; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.stream.Collectors; 61 import java.util.stream.Stream; 62 63 import org.testng.annotations.BeforeClass; 64 import org.testng.annotations.DataProvider; 65 import org.testng.annotations.Test; 66 67 import jdk.test.lib.JDKToolFinder; 68 import jdk.test.lib.JDKToolLauncher; 69 import jdk.test.lib.compiler.CompilerUtils; 70 import jdk.test.lib.process.OutputAnalyzer; 71 import jdk.test.lib.process.ProcessTools; 72 73 public class RuntimeTest { 74 public static final int SUCCESS = 0; 75 private static final String src = System.getProperty("test.src", "."); 76 private static final String usr = System.getProperty("user.dir", "."); 77 78 private static final Path srcFileRoot = Paths.get(src, "data", "runtimetest"); 79 private static final Path genFileRoot = Paths.get(usr, "data", "runtimetest"); 80 81 private static final int OLD_RELEASE = 8; 82 private static final int CURRENT_RELEASE = Runtime.version().major(); 83 private static final int FUTURE_RELEASE = CURRENT_RELEASE + 1; 84 private static final String MRJAR_BOTH_RELEASES = "MV_BOTH.jar"; 85 private static final String MRJAR_CURRENT_RELEASE = "MV_ONLY_" + CURRENT_RELEASE + ".jar"; 86 private static final String NON_MRJAR_OLD_RELEASE = "NON_MV.jar"; 87 88 private static final int[] versions = { OLD_RELEASE, CURRENT_RELEASE, FUTURE_RELEASE }; 89 90 @DataProvider(name = "jarFiles") 91 Object[][] jarFiles() { 92 return new Object[][]{ 93 { MRJAR_BOTH_RELEASES, CURRENT_RELEASE, CURRENT_RELEASE, CURRENT_RELEASE }, 94 { MRJAR_CURRENT_RELEASE, CURRENT_RELEASE, CURRENT_RELEASE, CURRENT_RELEASE }, 95 { NON_MRJAR_OLD_RELEASE, OLD_RELEASE, OLD_RELEASE, OLD_RELEASE } 96 }; 97 } 98 99 @BeforeClass 100 protected void setUpTest() throws Throwable { 101 createJarSourceFiles(); 102 compile(); 103 Path classes = Paths.get("classes"); 104 jar("cfm", MRJAR_BOTH_RELEASES, "manifest.txt", 105 "-C", classes.resolve("v" + OLD_RELEASE).toString(), ".", 106 "--release", "" + CURRENT_RELEASE, "-C", classes.resolve("v" + CURRENT_RELEASE).toString(), ".", 107 "--release", "" + FUTURE_RELEASE, "-C", classes.resolve("v" + FUTURE_RELEASE).toString(), ".") 108 .shouldHaveExitValue(0); 109 110 jar("cfm", MRJAR_CURRENT_RELEASE, "manifest.txt", 111 "-C", classes.resolve("v" + OLD_RELEASE).toString(), ".", 112 "--release", "" + CURRENT_RELEASE, "-C", classes.resolve("v" + CURRENT_RELEASE).toString(), ".") 113 .shouldHaveExitValue(0); 114 jar("cfm", NON_MRJAR_OLD_RELEASE, "manifest.txt", 115 "-C", classes.resolve("v" + OLD_RELEASE).toString(), ".") 116 .shouldHaveExitValue(0); 117 } 118 119 @Test(dataProvider = "jarFiles") 120 public void testClasspath(String jar, int mainVer, int helperVer, 121 int resVer) throws Throwable { 122 String[] command = { "-cp", jar, "testpackage.Main" }; 123 System.out.println("Command arguments:" + Arrays.asList(command)); 124 System.out.println(); 125 java(command).shouldHaveExitValue(SUCCESS) 126 .shouldContain("Main version: " + mainVer) 127 .shouldContain("Helpers version: " + helperVer) 128 .shouldContain("Resource version: " + resVer); 129 } 130 131 @Test(dataProvider = "jarFiles") 132 void testMVJarAsLib(String jar, int mainVer, int helperVer, int resVer) 133 throws Throwable { 134 String[] apps = { "UseByImport", "UseByReflection" }; 135 for (String app : apps) { 136 String[] command = {"-cp", 137 jar + File.pathSeparatorChar + "classes/test/", app }; 138 System.out.println("Command arguments:" + Arrays.asList(command)); 139 System.out.println(); 140 java(command).shouldHaveExitValue(SUCCESS) 141 .shouldContain("Main version: " + mainVer) 142 .shouldContain("Helpers version: " + helperVer) 143 .shouldContain("Resource version: " + resVer); 144 } 145 } 146 147 @Test(dataProvider = "jarFiles") 148 void testJavaJar(String jar, int mainVer, int helperVer, int resVer) 149 throws Throwable { 150 String[] command = { "-jar", jar }; 151 System.out.println("Command arguments:" + Arrays.asList(command)); 152 System.out.println(); 153 java(command).shouldHaveExitValue(SUCCESS) 154 .shouldContain("Main version: " + mainVer) 155 .shouldContain("Helpers version: " + helperVer) 156 .shouldContain("Resource version: " + resVer); 157 } 158 159 @Test(dataProvider = "jarFiles") 160 void testURLClassLoader(String jarName, int mainVer, int helperVer, 161 int resVer) throws ClassNotFoundException, NoSuchMethodException, 162 IllegalAccessException, IllegalArgumentException, 163 InvocationTargetException, IOException { 164 Path pathToJAR = Paths.get(jarName).toAbsolutePath(); 165 URL jarURL1 = new URL("jar:file:" + pathToJAR + "!/"); 166 URL jarURL2 = new URL("file:///" + pathToJAR); 167 testURLClassLoaderURL(jarURL1, mainVer, helperVer, resVer); 168 testURLClassLoaderURL(jarURL2, mainVer, helperVer, resVer); 169 } 170 171 private static void testURLClassLoaderURL(URL jarURL, 172 int mainVersionExpected, int helperVersionExpected, 173 int resourceVersionExpected) throws ClassNotFoundException, 174 NoSuchMethodException, IllegalAccessException, 175 IllegalArgumentException, InvocationTargetException, IOException { 176 System.out.println( 177 "Testing URLClassLoader MV JAR support for URL: " + jarURL); 178 URL[] urls = { jarURL }; 179 int mainVersionActual; 180 int helperVersionActual; 181 int resourceVersionActual; 182 try (URLClassLoader cl = URLClassLoader.newInstance(urls)) { 183 Class c = cl.loadClass("testpackage.Main"); 184 Method getMainVersion = c.getMethod("getMainVersion"); 185 mainVersionActual = (int) getMainVersion.invoke(null); 186 Method getHelperVersion = c.getMethod("getHelperVersion"); 187 helperVersionActual = (int) getHelperVersion.invoke(null); 188 try (InputStream ris = cl.getResourceAsStream("versionResource"); 189 BufferedReader br = new BufferedReader( 190 new InputStreamReader(ris))) { 191 resourceVersionActual = Integer.parseInt(br.readLine()); 192 } 193 } 194 195 assertEquals(mainVersionActual, mainVersionExpected, 196 "Test failed: Expected Main class version: " 197 + mainVersionExpected + " Actual version: " 198 + mainVersionActual); 199 assertEquals(helperVersionActual, helperVersionExpected, 200 "Test failed: Expected Helper class version: " 201 + helperVersionExpected + " Actual version: " 202 + helperVersionActual); 203 assertEquals(resourceVersionActual, resourceVersionExpected, 204 "Test failed: Expected resource version: " 205 + resourceVersionExpected + " Actual version: " 206 + resourceVersionActual); 207 } 208 209 @Test(dataProvider = "jarFiles") 210 void testJjs(String jar, int mainVer, int helperVer, int resVer) 211 throws Throwable { 212 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jjs"); 213 launcher.addToolArg("-cp").addToolArg(jar) 214 .addToolArg(src + "/data/runtimetest/MVJarJJSTestScript.js"); 215 ProcessTools.executeCommand(launcher.getCommand()) 216 .shouldHaveExitValue(SUCCESS) 217 .shouldContain("Main version: " + mainVer) 218 .shouldContain("Helpers version: " + helperVer) 219 .shouldContain("Resource version: " + resVer); 220 } 221 222 private static OutputAnalyzer jar(String... args) throws Throwable { 223 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jar"); 224 Stream.of(args).forEach(launcher::addToolArg); 225 return ProcessTools.executeCommand(launcher.getCommand()); 226 } 227 228 private static String platformPath(String p) { 229 return p.replace("/", File.separator); 230 } 231 232 private static void createJarSourceFiles() throws IOException { 233 for (int ver : versions) { 234 Files.find(srcFileRoot, 3, (file, attrs) -> (file.toString().endsWith(".template"))) 235 .map(srcFileRoot::relativize) 236 .map(Path::toString) 237 .map(p -> p.replace(".template", "")) 238 .forEach(f -> { 239 try { 240 Path template = srcFileRoot.resolve(f + ".template"); 241 Path out = genFileRoot.resolve(platformPath("v" + ver + "/" + f)); 242 Files.createDirectories(out.getParent()); 243 List<String> lines = Files.lines(template) 244 .map(s -> s.replaceAll("\\$version", String.valueOf(ver))) 245 .collect(Collectors.toList()); 246 Files.write(out, lines); 247 } catch (IOException x) { 248 throw new UncheckedIOException(x); 249 } 250 }); 251 } 252 } 253 254 private void compile() throws Throwable { 255 for (int ver : versions) { 256 Path classes = Paths.get(usr, "classes", "v" + ver); 257 Files.createDirectories(classes); 258 Path source = genFileRoot.resolve("v" + ver); 259 assertTrue(CompilerUtils.compile(source, classes)); 260 Files.copy(source.resolve("versionResource"), 261 classes.resolve("versionResource"), 262 StandardCopyOption.REPLACE_EXISTING); 263 } 264 265 Path classes = Paths.get(usr, "classes", "test"); 266 Files.createDirectory(classes); 267 Path source = srcFileRoot.resolve("test"); 268 assertTrue( 269 CompilerUtils.compile(source, classes, "-cp", "classes/v" + OLD_RELEASE)); 270 Files.copy(srcFileRoot.resolve("manifest.txt"), 271 Paths.get(usr, "manifest.txt"), 272 StandardCopyOption.REPLACE_EXISTING); 273 } 274 275 OutputAnalyzer java(String... args) throws Throwable { 276 String java = JDKToolFinder.getJDKTool("java"); 277 278 List<String> commands = new ArrayList<>(); 279 commands.add(java); 280 Stream.of(args).forEach(x -> commands.add(x)); 281 return ProcessTools.executeCommand(new ProcessBuilder(commands)); 282 } 283 }