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 * @bug 8132734 8144062 8159785 27 * @summary Test that URL connections to multi-release jars can be runtime versioned 28 * @library /lib/testlibrary/java/util/jar 29 * @modules java.compiler 30 * jdk.compiler 31 * jdk.httpserver 32 * jdk.jartool 33 * @build Compiler JarBuilder CreateMultiReleaseTestJars SimpleHttpServer 34 * @run testng MultiReleaseJarURLConnection 35 */ 36 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.lang.invoke.MethodHandle; 40 import java.lang.invoke.MethodHandles; 41 import java.lang.invoke.MethodType; 42 import java.net.JarURLConnection; 43 import java.net.URL; 44 import java.net.URLClassLoader; 45 import java.net.URLConnection; 46 import java.nio.file.Files; 47 import java.nio.file.Paths; 48 import java.util.Enumeration; 49 import java.util.jar.JarFile; 50 51 import org.testng.Assert; 52 import org.testng.annotations.AfterClass; 53 import org.testng.annotations.BeforeClass; 54 import org.testng.annotations.DataProvider; 55 import org.testng.annotations.Test; 56 57 public class MultiReleaseJarURLConnection { 58 String userdir = System.getProperty("user.dir","."); 59 String unversioned = userdir + "/unversioned.jar"; 60 String unsigned = userdir + "/multi-release.jar"; 61 String signed = userdir + "/signed-multi-release.jar"; 62 SimpleHttpServer server; 63 64 @BeforeClass 65 public void initialize() throws Exception { 66 CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); 67 creator.compileEntries(); 68 creator.buildUnversionedJar(); 69 creator.buildMultiReleaseJar(); 70 creator.buildSignedMultiReleaseJar(); 71 72 server = new SimpleHttpServer(); 73 server.start(); 74 75 } 76 77 @AfterClass 78 public void close() throws IOException { 79 // Windows requires server to stop before file is deleted 80 if (server != null) 81 server.stop(); 82 Files.delete(Paths.get(unversioned)); 83 Files.delete(Paths.get(unsigned)); 84 Files.delete(Paths.get(signed)); 85 } 86 87 @DataProvider(name = "data") 88 public Object[][] createData() { 89 return new Object[][]{ 90 {"unversioned", unversioned}, 91 {"unsigned", unsigned}, 92 {"signed", signed} 93 }; 94 } 95 96 @Test(dataProvider = "data") 97 public void testRuntimeVersioning(String style, String file) throws Exception { 98 String urlFile = "jar:file:" + file + "!/"; 99 String baseUrlEntry = urlFile + "version/Version.java"; 100 String rtreturn = "return " + Runtime.version().major(); 101 102 Assert.assertTrue(readAndCompare(new URL(baseUrlEntry), "return 8")); 103 // #runtime is "magic" for a multi-release jar, but not for unversioned jar 104 Assert.assertTrue(readAndCompare(new URL(baseUrlEntry + "#runtime"), 105 style.equals("unversioned") ? "return 8" : rtreturn)); 106 // #fragment or any other fragment is not magic 107 Assert.assertTrue(readAndCompare(new URL(baseUrlEntry + "#fragment"), "return 8")); 108 // cached entities not affected 109 Assert.assertTrue(readAndCompare(new URL(baseUrlEntry), "return 8")); 110 111 // the following tests will not work with unversioned jars 112 if (style.equals("unversioned")) return; 113 114 // direct access to versioned entry 115 String versUrlEntry = urlFile + "META-INF/versions/" + Runtime.version().major() 116 + "/version/Version.java"; 117 Assert.assertTrue(readAndCompare(new URL(versUrlEntry), rtreturn)); 118 // adding any fragment does not change things 119 Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#runtime"), rtreturn)); 120 Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#fragment"), rtreturn)); 121 122 // it really doesn't change things 123 versUrlEntry = urlFile + "META-INF/versions/10/version/Version.java"; 124 Assert.assertTrue(readAndCompare(new URL(versUrlEntry), "return 10")); 125 Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#runtime"), "return 10")); 126 Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#fragment"), "return 10")); 127 } 128 129 @Test(dataProvider = "data") 130 public void testCachedJars(String style, String file) throws Exception { 131 String urlFile = "jar:file:" + file + "!/"; 132 133 URL rootUrl = new URL(urlFile); 134 JarURLConnection juc = (JarURLConnection)rootUrl.openConnection(); 135 JarFile rootJar = juc.getJarFile(); 136 Runtime.Version root = rootJar.getVersion(); 137 138 URL runtimeUrl = new URL(urlFile + "#runtime"); 139 juc = (JarURLConnection)runtimeUrl.openConnection(); 140 JarFile runtimeJar = juc.getJarFile(); 141 Runtime.Version runtime = runtimeJar.getVersion(); 142 if (style.equals("unversioned")) { 143 Assert.assertEquals(root, runtime); 144 } else { 145 Assert.assertNotEquals(root, runtime); 146 } 147 148 juc = (JarURLConnection)rootUrl.openConnection(); 149 JarFile jar = juc.getJarFile(); 150 Assert.assertEquals(jar.getVersion(), root); 151 Assert.assertEquals(jar, rootJar); 152 153 juc = (JarURLConnection)runtimeUrl.openConnection(); 154 jar = juc.getJarFile(); 155 Assert.assertEquals(jar.getVersion(), runtime); 156 Assert.assertEquals(jar, runtimeJar); 157 158 rootJar.close(); 159 runtimeJar.close(); 160 jar.close(); // probably not needed 161 } 162 163 @DataProvider(name = "resourcedata") 164 public Object[][] createResourceData() throws Exception { 165 return new Object[][]{ 166 {"unversioned", Paths.get(unversioned).toUri().toURL()}, 167 {"unsigned", Paths.get(unsigned).toUri().toURL()}, 168 {"signed", Paths.get(signed).toUri().toURL()}, 169 {"unversioned", new URL("file:" + unversioned)}, 170 {"unsigned", new URL("file:" + unsigned)}, 171 {"signed", new URL("file:" + signed)}, 172 {"unversioned", new URL("jar:file:" + unversioned + "!/")}, 173 {"unsigned", new URL("jar:file:" + unsigned + "!/")}, 174 {"signed", new URL("jar:file:" + signed + "!/")}, 175 // external jar received via http protocol 176 {"http", new URL("jar:http://localhost:" + server.getPort() + "/multi-release.jar!/")}, 177 {"http", new URL("http://localhost:" + server.getPort() + "/multi-release.jar")}, 178 179 }; 180 } 181 182 @Test(dataProvider = "resourcedata") 183 public void testResources(String style, URL url) throws Throwable { 184 //System.out.println(" testing " + style + " url: " + url); 185 URL[] urls = {url}; 186 URLClassLoader cldr = new URLClassLoader(urls); 187 Class<?> vcls = cldr.loadClass("version.Version"); 188 189 // verify we are loading a runtime versioned class 190 MethodType mt = MethodType.methodType(int.class); 191 MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); 192 Assert.assertEquals((int)mh.invoke(vcls.newInstance()), 193 style.equals("unversioned") ? 8 : Runtime.version().major()); 194 195 // now get a resource and verify that we don't have a fragment attached 196 Enumeration<URL> vclsUrlEnum = cldr.getResources("version/Version.class"); 197 Assert.assertTrue(vclsUrlEnum.hasMoreElements()); 198 URL vclsUrls[] = new URL[] { 199 vcls.getResource("/version/Version.class"), 200 vcls.getResource("Version.class"), 201 cldr.getResource("version/Version.class"), 202 vclsUrlEnum.nextElement() 203 }; 204 Assert.assertFalse(vclsUrlEnum.hasMoreElements()); 205 for (URL vclsUrl : vclsUrls) { 206 String fragment = vclsUrl.getRef(); 207 Assert.assertNull(fragment); 208 209 // and verify that the the url is a reified pointer to the runtime entry 210 String rep = vclsUrl.toString(); 211 //System.out.println(" getResource(\"/version/Version.class\") returned: " + rep); 212 if (style.equals("http")) { 213 Assert.assertTrue(rep.startsWith("jar:http:")); 214 } else { 215 Assert.assertTrue(rep.startsWith("jar:file:")); 216 } 217 String suffix; 218 if (style.equals("unversioned")) { 219 suffix = ".jar!/version/Version.class"; 220 } else { 221 suffix = ".jar!/META-INF/versions/" + Runtime.version().major() 222 + "/version/Version.class"; 223 } 224 Assert.assertTrue(rep.endsWith(suffix)); 225 } 226 cldr.close(); 227 } 228 229 230 private boolean readAndCompare(URL url, String match) throws Exception { 231 boolean result; 232 // necessary to do it this way, instead of openStream(), so we can 233 // close underlying JarFile, otherwise windows can't delete the file 234 URLConnection conn = url.openConnection(); 235 try (InputStream is = conn.getInputStream()) { 236 byte[] bytes = is.readAllBytes(); 237 result = (new String(bytes)).contains(match); 238 } 239 if (conn instanceof JarURLConnection) { 240 ((JarURLConnection)conn).getJarFile().close(); 241 } 242 return result; 243 } 244 }