1 /* 2 * Copyright (c) 2015, 2018, 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 8144355 8144062 8176709 8194070 8193802 27 * @summary Test aliasing additions to ZipFileSystem for multi-release jar files 28 * @library /lib /test/lib 29 * @modules jdk.compiler 30 * jdk.jartool 31 * jdk.zipfs 32 * @build jdk.test.lib.compiler.Compiler 33 * jdk.test.lib.util.JarBuilder 34 * mrjar.CreateMultiReleaseTestJars 35 * @run testng MultiReleaseJarTest 36 */ 37 38 import java.io.IOException; 39 import java.lang.invoke.MethodHandle; 40 import java.lang.invoke.MethodHandles; 41 import java.lang.invoke.MethodType; 42 import java.lang.Runtime.Version; 43 import java.net.URI; 44 import java.nio.file.*; 45 import java.util.HashMap; 46 import java.util.Map; 47 import java.util.concurrent.atomic.AtomicInteger; 48 49 import jdk.test.lib.compiler.Compiler; 50 import jdk.test.lib.util.JarBuilder; 51 import mrjar.CreateMultiReleaseTestJars; 52 53 import org.testng.Assert; 54 import org.testng.annotations.*; 55 56 public class MultiReleaseJarTest { 57 final private int MAJOR_VERSION = Runtime.version().major(); 58 59 final private String userdir = System.getProperty("user.dir","."); 60 final private CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); 61 final private Map<String,String> stringEnv = new HashMap<>(); 62 final private Map<String,Integer> integerEnv = new HashMap<>(); 63 final private Map<String,Version> versionEnv = new HashMap<>(); 64 final private String className = "version.Version"; 65 final private MethodType mt = MethodType.methodType(int.class); 66 67 private String entryName; 68 private URI uvuri; 69 private URI mruri; 70 private URI smruri; 71 72 @BeforeClass 73 public void initialize() throws Exception { 74 creator.compileEntries(); 75 creator.buildUnversionedJar(); 76 creator.buildMultiReleaseJar(); 77 creator.buildShortMultiReleaseJar(); 78 String ssp = Paths.get(userdir, "unversioned.jar").toUri().toString(); 79 uvuri = new URI("jar", ssp , null); 80 ssp = Paths.get(userdir, "multi-release.jar").toUri().toString(); 81 mruri = new URI("jar", ssp, null); 82 ssp = Paths.get(userdir, "short-multi-release.jar").toUri().toString(); 83 smruri = new URI("jar", ssp, null); 84 entryName = className.replace('.', '/') + ".class"; 85 } 86 87 public void close() throws IOException { 88 Files.delete(Paths.get(userdir, "unversioned.jar")); 89 Files.delete(Paths.get(userdir, "multi-release.jar")); 90 Files.delete(Paths.get(userdir, "short-multi-release.jar")); 91 } 92 93 @DataProvider(name="strings") 94 public Object[][] createStrings() { 95 return new Object[][]{ 96 {"runtime", MAJOR_VERSION}, 97 {"-20", 8}, 98 {"0", 8}, 99 {"8", 8}, 100 {"9", 9}, 101 {Integer.toString(MAJOR_VERSION), MAJOR_VERSION}, 102 {Integer.toString(MAJOR_VERSION+1), MAJOR_VERSION}, 103 {"50", MAJOR_VERSION} 104 }; 105 } 106 107 @DataProvider(name="integers") 108 public Object[][] createIntegers() { 109 return new Object[][] { 110 {new Integer(-5), 8}, 111 {new Integer(0), 8}, 112 {new Integer(8), 8}, 113 {new Integer(9), 9}, 114 {new Integer(MAJOR_VERSION), MAJOR_VERSION}, 115 {new Integer(MAJOR_VERSION + 1), MAJOR_VERSION}, 116 {new Integer(100), MAJOR_VERSION} 117 }; 118 } 119 120 @DataProvider(name="versions") 121 public Object[][] createVersions() { 122 return new Object[][] { 123 {Version.parse("8"), 8}, 124 {Version.parse("9"), 9}, 125 {Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION}, 126 {Version.parse(Integer.toString(MAJOR_VERSION) + 1), MAJOR_VERSION}, 127 {Version.parse("100"), MAJOR_VERSION} 128 }; 129 } 130 131 // Not the best test but all I can do since ZipFileSystem and JarFileSystem 132 // are not public, so I can't use (fs instanceof ...) 133 @Test 134 public void testNewFileSystem() throws Exception { 135 Map<String,String> env = new HashMap<>(); 136 // no configuration, treat multi-release jar as unversioned 137 try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { 138 Assert.assertTrue(readAndCompare(fs, 8)); 139 } 140 env.put("multi-release", "runtime"); 141 // a configuration and jar file is multi-release 142 try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { 143 Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); 144 } 145 // a configuration but jar file is unversioned 146 try (FileSystem fs = FileSystems.newFileSystem(uvuri, env)) { 147 Assert.assertTrue(readAndCompare(fs, 8)); 148 } 149 } 150 151 private boolean readAndCompare(FileSystem fs, int expected) throws IOException { 152 Path path = fs.getPath("version/Version.java"); 153 String src = new String(Files.readAllBytes(path)); 154 return src.contains("return " + expected); 155 } 156 157 @Test(dataProvider="strings") 158 public void testStrings(String value, int expected) throws Throwable { 159 stringEnv.put("multi-release", value); 160 runTest(stringEnv, expected); 161 } 162 163 @Test(dataProvider="integers") 164 public void testIntegers(Integer value, int expected) throws Throwable { 165 integerEnv.put("multi-release", value); 166 runTest(integerEnv, expected); 167 } 168 169 @Test(dataProvider="versions") 170 public void testVersions(Version value, int expected) throws Throwable { 171 versionEnv.put("multi-release", value); 172 runTest(versionEnv, expected); 173 } 174 175 @Test 176 public void testShortJar() throws Throwable { 177 integerEnv.put("multi-release", Integer.valueOf(MAJOR_VERSION)); 178 runTest(smruri, integerEnv, MAJOR_VERSION); 179 integerEnv.put("multi-release", Integer.valueOf(9)); 180 runTest(smruri, integerEnv, 8); 181 } 182 183 private void runTest(Map<String,?> env, int expected) throws Throwable { 184 runTest(mruri, env, expected); 185 } 186 187 private void runTest(URI uri, Map<String,?> env, int expected) throws Throwable { 188 try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { 189 Path version = fs.getPath(entryName); 190 byte [] bytes = Files.readAllBytes(version); 191 Class<?> vcls = (new ByteArrayClassLoader(fs)).defineClass(className, bytes); 192 MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); 193 Assert.assertEquals((int)mh.invoke(vcls.newInstance()), expected); 194 } 195 } 196 197 @Test 198 public void testIsMultiReleaseJar() throws Exception { 199 // Re-examine commented out tests as part of JDK-8176843 200 testCustomMultiReleaseValue("true", true); 201 testCustomMultiReleaseValue("true\r\nOther: value", true); 202 testCustomMultiReleaseValue("true\nOther: value", true); 203 //testCustomMultiReleaseValue("true\rOther: value", true); 204 205 testCustomMultiReleaseValue("false", false); 206 testCustomMultiReleaseValue(" true", false); 207 testCustomMultiReleaseValue("true ", false); 208 //testCustomMultiReleaseValue("true\n ", false); 209 //testCustomMultiReleaseValue("true\r ", false); 210 //testCustomMultiReleaseValue("true\n true", false); 211 //testCustomMultiReleaseValue("true\r\n true", false); 212 } 213 214 @Test 215 public void testMultiReleaseJarWithNonVersionDir() throws Exception { 216 String jfname = "multi-release-non-ver.jar"; 217 Path jfpath = Paths.get(jfname); 218 URI uri = new URI("jar", jfpath.toUri().toString() , null); 219 JarBuilder jb = new JarBuilder(jfname); 220 jb.addAttribute("Multi-Release", "true"); 221 jb.build(); 222 Map<String,String> env = Map.of("multi-release", "runtime"); 223 try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { 224 Assert.assertTrue(true); 225 } 226 Files.delete(jfpath); 227 } 228 229 private static final AtomicInteger JAR_COUNT = new AtomicInteger(0); 230 231 private void testCustomMultiReleaseValue(String value, boolean expected) 232 throws Exception { 233 String fileName = "custom-mr" + JAR_COUNT.incrementAndGet() + ".jar"; 234 creator.buildCustomMultiReleaseJar(fileName, value, Map.of(), 235 /*addEntries*/true); 236 237 Map<String,String> env = Map.of("multi-release", "runtime"); 238 Path filePath = Paths.get(userdir, fileName); 239 String ssp = filePath.toUri().toString(); 240 URI customJar = new URI("jar", ssp , null); 241 try (FileSystem fs = FileSystems.newFileSystem(customJar, env)) { 242 if (expected) { 243 Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); 244 } else { 245 Assert.assertTrue(readAndCompare(fs, 8)); 246 } 247 } 248 Files.delete(filePath); 249 } 250 251 private static class ByteArrayClassLoader extends ClassLoader { 252 final private FileSystem fs; 253 254 ByteArrayClassLoader(FileSystem fs) { 255 super(null); 256 this.fs = fs; 257 } 258 259 @Override 260 public Class<?> loadClass(String name) throws ClassNotFoundException { 261 try { 262 return super.loadClass(name); 263 } catch (ClassNotFoundException x) {} 264 Path cls = fs.getPath(name.replace('.', '/') + ".class"); 265 try { 266 byte[] bytes = Files.readAllBytes(cls); 267 return defineClass(name, bytes); 268 } catch (IOException x) { 269 throw new ClassNotFoundException(x.getMessage()); 270 } 271 } 272 273 public Class<?> defineClass(String name, byte[] bytes) throws ClassNotFoundException { 274 if (bytes == null) throw new ClassNotFoundException("No bytes for " + name); 275 return defineClass(name, bytes, 0, bytes.length); 276 } 277 } 278 }