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 8149757 8144062 27 * @summary Test that StandardJavaFileManager uses the correct version of a 28 * class from a multi-release jar on classpath 29 * @library /tools/lib 30 * @modules jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.main 32 * @build toolbox.ToolBox 33 * @run testng MultiReleaseJarAwareSJFM 34 */ 35 36import org.testng.Assert; 37import org.testng.annotations.AfterClass; 38import org.testng.annotations.BeforeClass; 39import org.testng.annotations.DataProvider; 40import org.testng.annotations.Test; 41 42import javax.tools.FileObject; 43import javax.tools.JavaFileManager; 44import javax.tools.StandardJavaFileManager; 45import javax.tools.ToolProvider; 46import java.io.File; 47import java.io.IOException; 48import java.io.InputStream; 49import java.lang.invoke.MethodHandle; 50import java.lang.invoke.MethodHandles; 51import java.lang.invoke.MethodType; 52import java.util.List; 53 54import toolbox.JarTask; 55import toolbox.JavacTask; 56import toolbox.ToolBox; 57 58public class MultiReleaseJarAwareSJFM { 59 60 private final String version8 = 61 "package version;\n" + 62 "\n" + 63 "public class Version {\n" + 64 " public int getVersion() {\n" + 65 " return 8;\n" + 66 " }\n" + 67 "}\n"; 68 69 private final String version9 = 70 "package version;\n" + 71 "\n" + 72 "public class Version {\n" + 73 " public int getVersion() {\n" + 74 " int version = (new PackagePrivate()).getVersion();\n" + 75 " if (version == 9) return 9;\n" + 76 " return version;\n" + 77 " }\n" + 78 "}\n"; 79 80 private final String packagePrivate = 81 "package version;\n" + 82 "\n" + 83 "class PackagePrivate {\n" + 84 " int getVersion() {\n" + 85 " return 9;\n" + 86 " }\n" + 87 "}\n"; 88 89 private final String version10 = 90 "package version;\n" + 91 "\n" + 92 "public class Version {\n" + 93 " public int getVersion() {\n" + 94 " return 10;\n" + 95 " }\n" + 96 "}\n"; 97 98 private final String manifest = 99 "Manifest-Version: 1.0\n" + 100 "Multi-Release: true\n"; 101 102 private final ToolBox tb = new ToolBox(); 103 104 private final JavaFileManager.Location jloc = new JavaFileManager.Location() { 105 @Override 106 public String getName() { 107 return "Multi-Release Jar"; 108 } 109 @Override 110 public boolean isOutputLocation() { 111 return false; 112 } 113 }; 114 115 @BeforeClass 116 public void setup() throws Exception { 117 tb.createDirectories("classes", 118 "classes/META-INF/versions/9", 119 "classes/META-INF/versions/10"); 120 new JavacTask(tb) 121 .outdir("classes") 122 .sources(version8) 123 .run(); 124 new JavacTask(tb) 125 .outdir("classes/META-INF/versions/9") 126 .sources(version9, packagePrivate) 127 .run(); 128 new JavacTask(tb) 129 .outdir("classes/META-INF/versions/10") 130 .sources(version10) 131 .run(); 132 new JarTask(tb, "multi-release.jar") 133 .manifest(manifest) 134 .baseDir("classes") 135 .files("version/Version.class", 136 "META-INF/versions/9/version/Version.class", 137 "META-INF/versions/9/version/PackagePrivate.class", 138 "META-INF/versions/10/version/Version.class") 139 .run(); 140 } 141 142 @AfterClass 143 public void teardown() throws Exception { 144 tb.deleteFiles( 145 "classes/META-INF/versions/10/version/Version.class", 146 "classes/META-INF/versions/10/version", 147 "classes/META-INF/versions/10/", 148 "classes/META-INF/versions/9/version/Version.class", 149 "classes/META-INF/versions/9/version/PackagePrivate.class", 150 "classes/META-INF/versions/9/version", 151 "classes/META-INF/versions/9", 152 "classes/META-INF/versions", 153 "classes/META-INF", 154 "classes/version/Version.class", 155 "classes/version", 156 "classes", 157 "multi-release.jar" 158 ); 159 } 160 161 @DataProvider(name = "versions") 162 public Object[][] data() { 163 return new Object[][] { 164 {"", 8}, 165 {"8", 8}, 166 {"9", 9}, 167 {"runtime", Runtime.version().major()} 168 }; 169 } 170 171 @Test(dataProvider = "versions") 172 public void test(String version, int expected) throws Throwable { 173 StandardJavaFileManager jfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null); 174 jfm.setLocation(jloc, List.of(new File("multi-release.jar"))); 175 176 if (version.length() > 0) { 177 jfm.handleOption("--multi-release", List.of(version).iterator()); 178 } 179 180 CustomClassLoader cldr = new CustomClassLoader(jfm); 181 Class<?> versionClass = cldr.loadClass("version.Version"); 182 MethodType mt = MethodType.methodType(int.class); 183 MethodHandle mh = MethodHandles.lookup().findVirtual(versionClass, "getVersion", mt); 184 int v = (int)mh.invoke(versionClass.newInstance()); 185 Assert.assertEquals(v, expected); 186 187 jfm.close(); 188 } 189 190 private class CustomClassLoader extends ClassLoader { 191 private final JavaFileManager jfm; 192 193 public CustomClassLoader(JavaFileManager jfm) { 194 super(null); 195 this.jfm = jfm; 196 } 197 198 @Override 199 protected Class<?> findClass(String name) throws ClassNotFoundException { 200 int n = name.lastIndexOf('.'); 201 String pkg = n == -1 ? "" : name.substring(0, n); 202 String cls = name.substring(n + 1) + ".class"; 203 byte[] b; 204 try { 205 FileObject obj = jfm.getFileForInput(jloc, pkg, cls); 206 try (InputStream is = obj.openInputStream()) { 207 b = is.readAllBytes(); 208 } 209 } catch (IOException x) { 210 throw new ClassNotFoundException(x.getMessage(), x); 211 } 212 return defineClass(name, b, 0, b.length); 213 } 214 } 215} 216 217